Changeset 8b51009 in mainline for uspace/srv/bd/hr/util.c


Ignore:
Timestamp:
2025-03-28T23:37:16Z (3 months ago)
Author:
Miroslav Cimerman <mc@…>
Children:
0437dd5
Parents:
7bfe468
git-author:
Miroslav Cimerman <mc@…> (2025-03-28 23:25:57)
git-committer:
Miroslav Cimerman <mc@…> (2025-03-28 23:37:16)
Message:

hr: auto assembly, refactor

Added automatic assembly (with hrctl -A). All disks or their partitions
are scanned for HelenRAID metadata and assembly is attempted.

Main volume list is now locked with RW lock. The volume list
manipulation functions are moved into util.c.

hr_{create,destroy}_vol_struct() are implemented for better reusability
and modularity.

Volume destroy/stop (hrctl -D) now returns EBUSY if someone has still
the volume open()-ed.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/srv/bd/hr/util.c

    r7bfe468 r8b51009  
    4141#include <io/log.h>
    4242#include <loc.h>
     43#include <mem.h>
    4344#include <stdatomic.h>
    4445#include <stdlib.h>
    4546#include <stdio.h>
    4647#include <str_error.h>
    47 
     48#include <vbd.h>
     49
     50#include "io.h"
     51#include "superblock.h"
    4852#include "util.h"
    4953#include "var.h"
     
    5660
    5761extern loc_srv_t *hr_srv;
     62extern list_t hr_volumes;
     63extern fibril_rwlock_t hr_volumes_lock;
     64
     65errno_t hr_create_vol_struct(hr_volume_t **rvol, hr_level_t level)
     66{
     67        errno_t rc;
     68
     69        hr_volume_t *vol = calloc(1, sizeof(hr_volume_t));
     70        if (vol == NULL)
     71                return ENOMEM;
     72
     73        vol->level = level;
     74
     75        switch (level) {
     76        case HR_LVL_1:
     77                vol->hr_ops.create = hr_raid1_create;
     78                vol->hr_ops.init = hr_raid1_init;
     79                vol->hr_ops.status_event = hr_raid1_status_event;
     80                vol->hr_ops.add_hotspare = hr_raid1_add_hotspare;
     81                break;
     82        case HR_LVL_0:
     83                vol->hr_ops.create = hr_raid0_create;
     84                vol->hr_ops.init = hr_raid0_init;
     85                vol->hr_ops.status_event = hr_raid0_status_event;
     86                break;
     87        case HR_LVL_4:
     88                vol->hr_ops.create = hr_raid5_create;
     89                vol->hr_ops.init = hr_raid5_init;
     90                vol->hr_ops.status_event = hr_raid5_status_event;
     91                vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
     92                break;
     93        case HR_LVL_5:
     94                vol->hr_ops.create = hr_raid5_create;
     95                vol->hr_ops.init = hr_raid5_init;
     96                vol->hr_ops.status_event = hr_raid5_status_event;
     97                vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
     98                break;
     99        default:
     100                HR_DEBUG("unkown level: %d, aborting\n", vol->level);
     101                rc = EINVAL;
     102                goto error;
     103        }
     104
     105        vol->fge = hr_fpool_create(16, 32, sizeof(hr_io_t));
     106        if (vol->fge == NULL) {
     107                rc = ENOMEM;
     108                goto error;
     109        }
     110
     111        vol->status = HR_VOL_NONE;
     112
     113        for (size_t i = 0; i < HR_MAX_EXTENTS; ++i)
     114                vol->extents[i].status = HR_EXT_MISSING;
     115
     116        for (size_t i = 0; i < HR_MAX_HOTSPARES; ++i)
     117                vol->extents[i].status = HR_EXT_MISSING;
     118
     119        fibril_mutex_initialize(&vol->lock); /* XXX: will remove this */
     120
     121        fibril_rwlock_initialize(&vol->extents_lock);
     122        fibril_rwlock_initialize(&vol->states_lock);
     123
     124        fibril_mutex_initialize(&vol->hotspare_lock);
     125
     126        list_initialize(&vol->range_lock_list);
     127        fibril_mutex_initialize(&vol->range_lock_list_lock);
     128
     129        atomic_init(&vol->rebuild_blk, 0);
     130        atomic_init(&vol->state_dirty, false);
     131        atomic_init(&vol->open_cnt, 0);
     132
     133        *rvol = vol;
     134
     135        return EOK;
     136error:
     137        free(vol);
     138        return rc;
     139}
     140
     141void hr_destroy_vol_struct(hr_volume_t *vol)
     142{
     143        if (vol == NULL)
     144                return;
     145
     146        hr_fpool_destroy(vol->fge);
     147        hr_fini_devs(vol);
     148        free(vol->in_mem_md);
     149        free(vol);
     150}
     151
     152hr_volume_t *hr_get_volume(service_id_t svc_id)
     153{
     154        HR_DEBUG("hr_get_volume(): (%" PRIun ")\n", svc_id);
     155
     156        hr_volume_t *rvol = NULL;
     157
     158        fibril_rwlock_read_lock(&hr_volumes_lock);
     159        list_foreach(hr_volumes, lvolumes, hr_volume_t, iter) {
     160                if (iter->svc_id == svc_id) {
     161                        rvol = iter;
     162                        break;
     163                }
     164        }
     165
     166        fibril_rwlock_read_unlock(&hr_volumes_lock);
     167        return rvol;
     168}
     169
     170errno_t hr_remove_volume(service_id_t svc_id)
     171{
     172        HR_DEBUG("hr_remove_volume(): (%" PRIun ")\n", svc_id);
     173
     174        errno_t rc;
     175
     176        fibril_rwlock_write_lock(&hr_volumes_lock);
     177        list_foreach(hr_volumes, lvolumes, hr_volume_t, vol) {
     178                if (vol->svc_id == svc_id) {
     179                        int open_cnt = atomic_load_explicit(&vol->open_cnt,
     180                            memory_order_relaxed);
     181                        /*
     182                         * The "atomicity" of this if condition is provided
     183                         * by the write lock - no new bd connection can
     184                         * come, because we need to get the bd_srvs_t from
     185                         * volume, which we get from the list.
     186                         * (see hr_client_conn() in hr.c)
     187                         */
     188                        if (open_cnt > 0) {
     189                                fibril_rwlock_write_unlock(&hr_volumes_lock);
     190                                return EBUSY;
     191                        }
     192                        list_remove(&vol->lvolumes);
     193                        fibril_rwlock_write_unlock(&hr_volumes_lock);
     194
     195                        hr_destroy_vol_struct(vol);
     196
     197                        rc = loc_service_unregister(hr_srv, svc_id);
     198                        return rc;
     199                }
     200        }
     201
     202        fibril_rwlock_write_unlock(&hr_volumes_lock);
     203        return ENOENT;
     204}
    58205
    59206errno_t hr_init_devs(hr_volume_t *vol)
     
    419566}
    420567
     568struct svc_id_linked {
     569        link_t link;
     570        service_id_t svc_id;
     571        hr_metadata_t *md;
     572        bool inited;
     573        bool md_present;
     574};
     575
     576static errno_t hr_add_svc_linked_to_list(list_t *list, service_id_t svc_id,
     577    bool inited, hr_metadata_t *md)
     578{
     579        errno_t rc = EOK;
     580        struct svc_id_linked *to_add;
     581
     582        to_add = malloc(sizeof(struct svc_id_linked));
     583        if (to_add == NULL) {
     584                rc = ENOMEM;
     585                goto error;
     586        }
     587        to_add->svc_id = svc_id;
     588        to_add->inited = inited;
     589
     590        if (md != NULL) {
     591                to_add->md = malloc(sizeof(hr_metadata_t));
     592                if (to_add->md == NULL) {
     593                        rc = ENOMEM;
     594                        goto error;
     595                }
     596                to_add->md_present = true;
     597                memcpy(to_add->md, md, sizeof(*md));
     598        } else {
     599                to_add->md_present = false;
     600        }
     601
     602        list_append(&to_add->link, list);
     603
     604error:
     605        return rc;
     606}
     607
     608static void free_svc_id_linked(struct svc_id_linked *p)
     609{
     610        if (p->md_present)
     611                free(p->md);
     612        free(p);
     613}
     614
     615static void free_svc_id_list(list_t *list)
     616{
     617        struct svc_id_linked *dev_id;
     618        while (!list_empty(list)) {
     619                dev_id = list_pop(list, struct svc_id_linked, link);
     620                free_svc_id_linked(dev_id);
     621        }
     622}
     623
     624static errno_t hr_fill_disk_part_svcs_list(list_t *list)
     625{
     626        errno_t rc;
     627        size_t disk_count;
     628        service_id_t *disk_svcs = NULL;
     629        vbd_t *vbd = NULL;
     630
     631        rc = vbd_create(&vbd);
     632        if (rc != EOK)
     633                goto error;
     634
     635        rc = vbd_get_disks(vbd, &disk_svcs, &disk_count);
     636        if (rc != EOK)
     637                goto error;
     638
     639        for (size_t i = 0; i < disk_count; i++) {
     640                vbd_disk_info_t disk_info;
     641                rc = vbd_disk_info(vbd, disk_svcs[i], &disk_info);
     642                if (rc != EOK)
     643                        goto error;
     644
     645                if (disk_info.ltype == lt_none) {
     646                        rc = hr_add_svc_linked_to_list(list, disk_svcs[i], false, NULL);
     647                        if (rc != EOK)
     648                                goto error;
     649                } else {
     650                        size_t part_count;
     651                        service_id_t *part_ids = NULL;
     652                        rc = vbd_label_get_parts(vbd, disk_svcs[i], &part_ids, &part_count);
     653                        if (rc != EOK)
     654                                goto error;
     655
     656                        for (size_t j = 0; j < part_count; j++) {
     657                                vbd_part_info_t part_info;
     658                                rc = vbd_part_get_info(vbd, part_ids[j], &part_info);
     659                                if (rc != EOK) {
     660                                        free(part_ids);
     661                                        goto error;
     662                                }
     663
     664                                rc = hr_add_svc_linked_to_list(list,
     665                                    part_info.svc_id, false, NULL);
     666                                if (rc != EOK) {
     667                                        free(part_ids);
     668                                        goto error;
     669                                }
     670                        }
     671
     672                        free(part_ids);
     673                }
     674        }
     675
     676        free(disk_svcs);
     677        vbd_destroy(vbd);
     678        return EOK;
     679error:
     680        free_svc_id_list(list);
     681        if (disk_svcs != NULL)
     682                free(disk_svcs);
     683        vbd_destroy(vbd);
     684
     685        return rc;
     686}
     687
     688static errno_t block_init_dev_list(list_t *list)
     689{
     690        list_foreach_safe(*list, cur_link, next_link) {
     691                struct svc_id_linked *iter;
     692                iter = list_get_instance(cur_link, struct svc_id_linked, link);
     693
     694                if (iter->inited)
     695                        continue;
     696
     697                errno_t rc = block_init(iter->svc_id);
     698
     699                /* already used as an extent of active volume */
     700                /* XXX: figure out how it is with hotspares too */
     701                if (rc == EEXIST) {
     702                        list_remove(cur_link);
     703                        free_svc_id_linked(iter);
     704                        continue;
     705                }
     706
     707                if (rc != EOK)
     708                        return rc;
     709
     710                iter->inited = true;
     711        }
     712
     713        return EOK;
     714}
     715
     716static void block_fini_dev_list(list_t *list)
     717{
     718        list_foreach(*list, link, struct svc_id_linked, iter) {
     719                if (iter->inited) {
     720                        block_fini(iter->svc_id);
     721                        iter->inited = false;
     722                }
     723        }
     724}
     725
     726static errno_t hr_util_get_matching_md_svcs_list(list_t *rlist, list_t *devlist,
     727    service_id_t svc_id, hr_metadata_t *md_main)
     728{
     729        errno_t rc = EOK;
     730
     731        list_foreach(*devlist, link, struct svc_id_linked, iter) {
     732                if (iter->svc_id == svc_id)
     733                        continue;
     734                void *md_block;
     735                hr_metadata_t md;
     736                rc = hr_get_metadata_block(iter->svc_id, &md_block);
     737                if (rc != EOK)
     738                        goto error;
     739                hr_decode_metadata_from_block(md_block, &md);
     740
     741                free(md_block);
     742
     743                if (!hr_valid_md_magic(&md))
     744                        continue;
     745
     746                if (memcmp(md_main->uuid, md.uuid, HR_UUID_LEN) != 0)
     747                        continue;
     748
     749                /*
     750                 * XXX: can I assume bsize and everything is fine when
     751                 * UUID matches?
     752                 */
     753
     754                rc = hr_add_svc_linked_to_list(rlist, iter->svc_id, true, &md);
     755                if (rc != EOK)
     756                        goto error;
     757        }
     758
     759        return  EOK;
     760error:
     761        free_svc_id_list(rlist);
     762        return rc;
     763}
     764
     765static errno_t hr_util_assemble_from_matching_list(list_t *list)
     766{
     767        HR_DEBUG("%s()", __func__);
     768
     769        errno_t rc = EOK;
     770
     771        hr_metadata_t *main_md = NULL;
     772        size_t max_counter_val = 0;
     773
     774        list_foreach(*list, link, struct svc_id_linked, iter) {
     775                hr_metadata_dump(iter->md);
     776                if (iter->md->counter >= max_counter_val) {
     777                        max_counter_val = iter->md->counter;
     778                        main_md = iter->md;
     779                }
     780        }
     781
     782        assert(main_md != NULL);
     783
     784        hr_volume_t *vol;
     785        rc = hr_create_vol_struct(&vol, (hr_level_t)main_md->level);
     786        if (rc != EOK)
     787                goto error;
     788
     789        vol->nblocks = main_md->nblocks;
     790        vol->data_blkno = main_md->data_blkno;
     791        vol->truncated_blkno = main_md->truncated_blkno;
     792        vol->data_offset = main_md->data_offset;
     793        vol->counter = main_md->counter;
     794        vol->metadata_version = main_md->version;
     795        vol->extent_no = main_md->extent_no;
     796        vol->level = main_md->level;
     797        vol->layout = main_md->layout;
     798        vol->strip_size = main_md->strip_size;
     799        vol->bsize = main_md->bsize;
     800        memcpy(vol->devname, main_md->devname, HR_DEVNAME_LEN);
     801
     802        list_foreach(*list, link, struct svc_id_linked, iter) {
     803                vol->extents[iter->md->index].svc_id = iter->svc_id;
     804                if (iter->md->counter == max_counter_val)
     805                        vol->extents[iter->md->index].status = HR_EXT_ONLINE;
     806                else
     807                        vol->extents[iter->md->index].status = HR_EXT_INVALID;
     808        }
     809
     810        rc = vol->hr_ops.create(vol);
     811        if (rc != EOK)
     812                goto error;
     813
     814        fibril_rwlock_write_lock(&hr_volumes_lock);
     815
     816        list_foreach(hr_volumes, lvolumes, hr_volume_t, other) {
     817                uint8_t *our_uuid = vol->in_mem_md->uuid;
     818                uint8_t *other_uuid = other->in_mem_md->uuid;
     819                if (memcmp(our_uuid, other_uuid, HR_UUID_LEN) == 0) {
     820                        rc = EEXIST;
     821                        fibril_rwlock_write_unlock(&hr_volumes_lock);
     822                        goto error;
     823                }
     824        }
     825
     826        /*
     827         * XXX: register it here
     828         * ... if it fails on EEXIST try different name... like + 1 on the end
     829         */
     830
     831        list_append(&vol->lvolumes, &hr_volumes);
     832
     833        fibril_rwlock_write_unlock(&hr_volumes_lock);
     834
     835        return EOK;
     836error:
     837        hr_destroy_vol_struct(vol);
     838        return rc;
     839}
     840
     841errno_t hr_util_try_auto_assemble(size_t *rassembled_cnt)
     842{
     843        HR_DEBUG("%s()", __func__);
     844
     845        /*
     846         * scan partitions or disks:
     847         *
     848         * When we find a metadata block with valid
     849         * magic, take UUID and try to find other matching
     850         * UUIDs.
     851         *
     852         * We ignore extents that are a part of already
     853         * active volumes. (even when the counter is lower
     854         * on active volumes... XXX: use timestamp as initial counter value
     855         * when assembling, or writing dirty metadata?)
     856         */
     857
     858        size_t asm_cnt = 0;
     859        errno_t rc;
     860        list_t dev_id_list;
     861
     862        list_initialize(&dev_id_list);
     863        rc = hr_fill_disk_part_svcs_list(&dev_id_list);
     864        if (rc != EOK)
     865                goto error;
     866
     867        rc = block_init_dev_list(&dev_id_list);
     868        if (rc != EOK)
     869                goto error;
     870
     871        struct svc_id_linked *iter;
     872        while (!list_empty(&dev_id_list)) {
     873                iter = list_pop(&dev_id_list, struct svc_id_linked, link);
     874
     875                printf("svc_id: %lu\n", iter->svc_id);
     876
     877                void *metadata_block;
     878                hr_metadata_t metadata;
     879
     880                rc = hr_get_metadata_block(iter->svc_id, &metadata_block);
     881                if (rc != EOK)
     882                        goto error;
     883
     884                hr_decode_metadata_from_block(metadata_block, &metadata);
     885
     886                free(metadata_block);
     887
     888                if (!hr_valid_md_magic(&metadata)) {
     889                        printf("BAD magic\n");
     890                        block_fini(iter->svc_id);
     891                        free_svc_id_linked(iter);
     892                        continue;
     893                }
     894
     895                hr_metadata_dump(&metadata);
     896
     897                char *svc_name = NULL;
     898                rc = loc_service_get_name(iter->svc_id, &svc_name);
     899                if (rc != EOK)
     900                        goto error;
     901
     902                HR_DEBUG("found valid metadata on %s, "
     903                    "will try to match other extents\n", svc_name);
     904
     905                free(svc_name);
     906
     907                list_t matching_svcs_list;
     908                list_initialize(&matching_svcs_list);
     909
     910                rc = hr_util_get_matching_md_svcs_list(&matching_svcs_list,
     911                    &dev_id_list, iter->svc_id, &metadata);
     912                if (rc != EOK)
     913                        goto error;
     914
     915                /* add current iter to list as well */
     916                rc = hr_add_svc_linked_to_list(&matching_svcs_list,
     917                    iter->svc_id, true, &metadata);
     918                if (rc != EOK) {
     919                        free_svc_id_list(&matching_svcs_list);
     920                        goto error;
     921                }
     922
     923                /* remove matching list members from dev_id_list */
     924                list_foreach(matching_svcs_list, link, struct svc_id_linked,
     925                    iter2) {
     926                        printf("matching svc_id: %lu\n", iter2->svc_id);
     927                        struct svc_id_linked *to_remove;
     928                        list_foreach_safe(dev_id_list, cur_link, next_link) {
     929                                to_remove = list_get_instance(cur_link,
     930                                    struct svc_id_linked, link);
     931                                if (to_remove->svc_id == iter2->svc_id) {
     932                                        list_remove(cur_link);
     933                                        free_svc_id_linked(to_remove);
     934                                }
     935                        }
     936                }
     937
     938                rc = hr_util_assemble_from_matching_list(&matching_svcs_list);
     939                switch (rc) {
     940                case EOK:
     941                        asm_cnt++;
     942                        break;
     943                case EEXIST:
     944                        /*
     945                         * A race is detected this way, because we don't want
     946                         * to hold the hr_volumes list lock for a long time,
     947                         * for all assembly attempts. XXX: discuss...
     948                         */
     949                        rc = EOK;
     950                        break;
     951                default:
     952                        block_fini_dev_list(&matching_svcs_list);
     953                        free_svc_id_list(&matching_svcs_list);
     954                        goto error;
     955                }
     956
     957                free_svc_id_list(&matching_svcs_list);
     958        }
     959
     960error:
     961        if (rassembled_cnt != NULL)
     962                *rassembled_cnt = asm_cnt;
     963
     964        block_fini_dev_list(&dev_id_list);
     965        free_svc_id_list(&dev_id_list);
     966
     967        return rc;
     968}
     969
    421970/** @}
    422971 */
Note: See TracChangeset for help on using the changeset viewer.