source: mainline/uspace/srv/bd/hr/util.c@ 8b51009

Last change on this file since 8b51009 was 8b51009, checked in by Miroslav Cimerman <mc@…>, 4 months ago

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.

  • Property mode set to 100644
File size: 22.0 KB
Line 
1/*
2 * Copyright (c) 2025 Miroslav Cimerman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup hr
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <adt/list.h>
37#include <block.h>
38#include <errno.h>
39#include <fibril_synch.h>
40#include <hr.h>
41#include <io/log.h>
42#include <loc.h>
43#include <mem.h>
44#include <stdatomic.h>
45#include <stdlib.h>
46#include <stdio.h>
47#include <str_error.h>
48#include <vbd.h>
49
50#include "io.h"
51#include "superblock.h"
52#include "util.h"
53#include "var.h"
54
55#define HR_RL_LIST_LOCK(vol) (fibril_mutex_lock(&vol->range_lock_list_lock))
56#define HR_RL_LIST_UNLOCK(vol) \
57 (fibril_mutex_unlock(&vol->range_lock_list_lock))
58
59static bool hr_range_lock_overlap(hr_range_lock_t *, hr_range_lock_t *);
60
61extern 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}
205
206errno_t hr_init_devs(hr_volume_t *vol)
207{
208 HR_DEBUG("%s()", __func__);
209
210 errno_t rc;
211 size_t i;
212 hr_extent_t *extent;
213
214 for (i = 0; i < vol->extent_no; i++) {
215 extent = &vol->extents[i];
216 if (extent->svc_id == 0) {
217 extent->status = HR_EXT_MISSING;
218 continue;
219 }
220
221 HR_DEBUG("hr_init_devs(): block_init() on (%lu)\n",
222 extent->svc_id);
223 rc = block_init(extent->svc_id);
224 extent->status = HR_EXT_ONLINE;
225
226 if (rc != EOK) {
227 HR_ERROR("hr_init_devs(): initing (%lu) failed, "
228 "aborting\n", extent->svc_id);
229 break;
230 }
231 }
232
233 for (i = 0; i < HR_MAX_HOTSPARES; i++)
234 vol->hotspares[i].status = HR_EXT_MISSING;
235
236 return rc;
237}
238
239void hr_fini_devs(hr_volume_t *vol)
240{
241 HR_DEBUG("%s()", __func__);
242
243 size_t i;
244
245 for (i = 0; i < vol->extent_no; i++) {
246 if (vol->extents[i].svc_id != 0) {
247 HR_DEBUG("hr_fini_devs(): block_fini() on (%lu)\n",
248 vol->extents[i].svc_id);
249 block_fini(vol->extents[i].svc_id);
250 }
251 }
252}
253
254errno_t hr_register_volume(hr_volume_t *vol)
255{
256 HR_DEBUG("%s()", __func__);
257
258 errno_t rc;
259 service_id_t new_id;
260 category_id_t cat_id;
261 char *fullname = NULL;
262 char *devname = vol->devname;
263
264 if (asprintf(&fullname, "devices/%s", devname) < 0)
265 return ENOMEM;
266
267 rc = loc_service_register(hr_srv, fullname, &new_id);
268 if (rc != EOK) {
269 HR_ERROR("unable to register device \"%s\": %s\n",
270 fullname, str_error(rc));
271 goto error;
272 }
273
274 rc = loc_category_get_id("raid", &cat_id, IPC_FLAG_BLOCKING);
275 if (rc != EOK) {
276 HR_ERROR("failed resolving category \"raid\": %s\n",
277 str_error(rc));
278 goto error;
279 }
280
281 rc = loc_service_add_to_cat(hr_srv, new_id, cat_id);
282 if (rc != EOK) {
283 HR_ERROR("failed adding \"%s\" to category \"raid\": %s\n",
284 fullname, str_error(rc));
285 goto error;
286 }
287
288 vol->svc_id = new_id;
289error:
290 free(fullname);
291 return rc;
292}
293
294errno_t hr_check_devs(hr_volume_t *vol, uint64_t *rblkno, size_t *rbsize)
295{
296 HR_DEBUG("%s()", __func__);
297
298 errno_t rc;
299 size_t i, bsize;
300 uint64_t nblocks;
301 size_t last_bsize = 0;
302 uint64_t last_nblocks = 0;
303 uint64_t total_blocks = 0;
304 hr_extent_t *extent;
305
306 for (i = 0; i < vol->extent_no; i++) {
307 extent = &vol->extents[i];
308 if (extent->status == HR_EXT_MISSING)
309 continue;
310 rc = block_get_nblocks(extent->svc_id, &nblocks);
311 if (rc != EOK)
312 goto error;
313 if (last_nblocks != 0 && nblocks != last_nblocks) {
314 HR_ERROR("number of blocks differs\n");
315 rc = EINVAL;
316 goto error;
317 }
318
319 total_blocks += nblocks;
320 last_nblocks = nblocks;
321 }
322
323 for (i = 0; i < vol->extent_no; i++) {
324 extent = &vol->extents[i];
325 if (extent->status == HR_EXT_MISSING)
326 continue;
327 rc = block_get_bsize(extent->svc_id, &bsize);
328 if (rc != EOK)
329 goto error;
330 if (last_bsize != 0 && bsize != last_bsize) {
331 HR_ERROR("block sizes differ\n");
332 rc = EINVAL;
333 goto error;
334 }
335
336 last_bsize = bsize;
337 }
338
339 if ((bsize % 512) != 0) {
340 HR_ERROR("block size not multiple of 512\n");
341 return EINVAL;
342 }
343
344 if (rblkno != NULL)
345 *rblkno = total_blocks;
346 if (rbsize != NULL)
347 *rbsize = bsize;
348error:
349 return rc;
350}
351
352errno_t hr_check_ba_range(hr_volume_t *vol, size_t cnt, uint64_t ba)
353{
354 if (ba + cnt > vol->data_blkno)
355 return ERANGE;
356 return EOK;
357}
358
359void hr_add_ba_offset(hr_volume_t *vol, uint64_t *ba)
360{
361 *ba = *ba + vol->data_offset;
362}
363
364void hr_update_ext_status(hr_volume_t *vol, size_t extent, hr_ext_status_t s)
365{
366 if (vol->level != HR_LVL_0)
367 assert(fibril_rwlock_is_locked(&vol->extents_lock));
368
369 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
370
371 assert(extent < vol->extent_no);
372
373 hr_ext_status_t old = vol->extents[extent].status;
374 HR_WARN("\"%s\": changing extent %lu state: %s -> %s\n",
375 vol->devname, extent, hr_get_ext_status_msg(old),
376 hr_get_ext_status_msg(s));
377 vol->extents[extent].status = s;
378}
379
380void hr_update_hotspare_status(hr_volume_t *vol, size_t hs, hr_ext_status_t s)
381{
382 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
383
384 assert(hs < vol->hotspare_no);
385
386 hr_ext_status_t old = vol->hotspares[hs].status;
387 HR_WARN("\"%s\": changing hotspare %lu state: %s -> %s\n",
388 vol->devname, hs, hr_get_ext_status_msg(old),
389 hr_get_ext_status_msg(s));
390 vol->hotspares[hs].status = s;
391}
392
393void hr_update_vol_status(hr_volume_t *vol, hr_vol_status_t new)
394{
395 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
396
397 HR_WARN("\"%s\": changing volume state: %s -> %s\n", vol->devname,
398 hr_get_vol_status_msg(vol->status), hr_get_vol_status_msg(new));
399 vol->status = new;
400}
401
402void hr_update_ext_svc_id(hr_volume_t *vol, size_t extent, service_id_t new)
403{
404 if (vol->level != HR_LVL_0)
405 assert(fibril_rwlock_is_write_locked(&vol->extents_lock));
406
407 assert(extent < vol->extent_no);
408
409 service_id_t old = vol->extents[extent].svc_id;
410 HR_WARN("\"%s\": changing extent no. %lu svc_id: (%lu) -> (%lu)\n",
411 vol->devname, extent, old, new);
412 vol->extents[extent].svc_id = new;
413}
414
415void hr_update_hotspare_svc_id(hr_volume_t *vol, size_t hs, service_id_t new)
416{
417 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
418
419 assert(hs < vol->hotspare_no);
420
421 service_id_t old = vol->hotspares[hs].svc_id;
422 HR_WARN("\"%s\": changing hotspare no. %lu svc_id: (%lu) -> (%lu)\n",
423 vol->devname, hs, old, new);
424 vol->hotspares[hs].svc_id = new;
425}
426
427/*
428 * Do a whole sync (ba = 0, cnt = 0) across all extents,
429 * and update extent status. *For now*, the caller has to
430 * update volume status after the syncs.
431 *
432 * TODO: add update_vol_status fcn ptr for each raid
433 */
434void hr_sync_all_extents(hr_volume_t *vol)
435{
436 errno_t rc;
437
438 fibril_mutex_lock(&vol->lock);
439 for (size_t i = 0; i < vol->extent_no; i++) {
440 if (vol->extents[i].status != HR_EXT_ONLINE)
441 continue;
442 rc = block_sync_cache(vol->extents[i].svc_id, 0, 0);
443 if (rc == ENOMEM || rc == ENOTSUP)
444 continue;
445 if (rc != EOK) {
446 if (rc == ENOENT)
447 hr_update_ext_status(vol, i, HR_EXT_MISSING);
448 else if (rc != EOK)
449 hr_update_ext_status(vol, i, HR_EXT_FAILED);
450 }
451 }
452 fibril_mutex_unlock(&vol->lock);
453}
454
455size_t hr_count_extents(hr_volume_t *vol, hr_ext_status_t status)
456{
457 if (vol->level != HR_LVL_0)
458 assert(fibril_rwlock_is_locked(&vol->extents_lock));
459 assert(fibril_rwlock_is_locked(&vol->states_lock));
460
461 size_t count = 0;
462 for (size_t i = 0; i < vol->extent_no; i++)
463 if (vol->extents[i].status == status)
464 count++;
465
466 return count;
467}
468
469hr_range_lock_t *hr_range_lock_acquire(hr_volume_t *vol, uint64_t ba,
470 uint64_t cnt)
471{
472 hr_range_lock_t *rl = malloc(sizeof(hr_range_lock_t));
473 if (rl == NULL)
474 return NULL;
475
476 rl->vol = vol;
477 rl->off = ba;
478 rl->len = cnt;
479
480 rl->pending = 1;
481 rl->ignore = false;
482
483 link_initialize(&rl->link);
484 fibril_mutex_initialize(&rl->lock);
485
486 fibril_mutex_lock(&rl->lock);
487
488again:
489 HR_RL_LIST_LOCK(vol);
490 list_foreach(vol->range_lock_list, link, hr_range_lock_t, rlp) {
491 if (rlp->ignore)
492 continue;
493 if (hr_range_lock_overlap(rlp, rl)) {
494 rlp->pending++;
495
496 HR_RL_LIST_UNLOCK(vol);
497
498 fibril_mutex_lock(&rlp->lock);
499
500 HR_RL_LIST_LOCK(vol);
501
502 rlp->pending--;
503
504 /*
505 * when ignore is set, after HR_RL_LIST_UNLOCK(),
506 * noone new is going to be able to start sleeping
507 * on the ignored range lock, only already waiting
508 * IOs will come through here
509 */
510 rlp->ignore = true;
511
512 fibril_mutex_unlock(&rlp->lock);
513
514 if (rlp->pending == 0) {
515 list_remove(&rlp->link);
516 free(rlp);
517 }
518
519 HR_RL_LIST_UNLOCK(vol);
520 goto again;
521 }
522 }
523
524 list_append(&rl->link, &vol->range_lock_list);
525
526 HR_RL_LIST_UNLOCK(vol);
527 return rl;
528}
529
530void hr_range_lock_release(hr_range_lock_t *rl)
531{
532 if (rl == NULL)
533 return;
534
535 HR_RL_LIST_LOCK(rl->vol);
536
537 rl->pending--;
538
539 fibril_mutex_unlock(&rl->lock);
540
541 if (rl->pending == 0) {
542 list_remove(&rl->link);
543 free(rl);
544 }
545
546 HR_RL_LIST_UNLOCK(rl->vol);
547}
548
549static bool hr_range_lock_overlap(hr_range_lock_t *rl1, hr_range_lock_t *rl2)
550{
551 uint64_t rl1_start = rl1->off;
552 uint64_t rl1_end = rl1->off + rl1->len - 1;
553 uint64_t rl2_start = rl2->off;
554 uint64_t rl2_end = rl2->off + rl2->len - 1;
555
556 /* one ends before the other starts */
557 if (rl1_end < rl2_start || rl2_end < rl1_start)
558 return false;
559
560 return true;
561}
562
563void hr_mark_vol_state_dirty(hr_volume_t *vol)
564{
565 atomic_store(&vol->state_dirty, true);
566}
567
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
970/** @}
971 */
Note: See TracBrowser for help on using the repository browser.