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

Last change on this file since c9ce6d22 was c9ce6d22, checked in by Miroslav Cimerman <mc@…>, 6 weeks ago

hr: refactor volume removal

  • Property mode set to 100644
File size: 25.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 <inttypes.h>
42#include <io/log.h>
43#include <loc.h>
44#include <mem.h>
45#include <stdatomic.h>
46#include <stdlib.h>
47#include <stdio.h>
48#include <str.h>
49#include <str_error.h>
50#include <vbd.h>
51
52#include "io.h"
53#include "superblock.h"
54#include "util.h"
55#include "var.h"
56
57static bool hr_range_lock_overlap(hr_range_lock_t *, hr_range_lock_t *);
58static errno_t hr_add_svc_linked_to_list(list_t *, service_id_t, bool,
59 void *);
60static void free_dev_list_member(struct dev_list_member *);
61static void free_svc_id_list(list_t *);
62static errno_t hr_fill_disk_part_svcs_list(list_t *);
63static errno_t block_init_dev_list(list_t *);
64static void block_fini_dev_list(list_t *);
65static errno_t hr_util_get_matching_md_svcs_list(list_t *, list_t *,
66 service_id_t, hr_metadata_type_t, void *);
67static errno_t hr_util_assemble_from_matching_list(list_t *, hr_metadata_type_t);
68static errno_t hr_fill_svcs_list_from_cfg(hr_config_t *, list_t *);
69
70#define HR_RL_LIST_LOCK(vol) (fibril_mutex_lock(&(vol)->range_lock_list_lock))
71#define HR_RL_LIST_UNLOCK(vol) \
72 (fibril_mutex_unlock(&(vol)->range_lock_list_lock))
73
74extern loc_srv_t *hr_srv;
75extern list_t hr_volumes;
76extern fibril_rwlock_t hr_volumes_lock;
77
78errno_t hr_create_vol_struct(hr_volume_t **rvol, hr_level_t level,
79 const char *devname, hr_metadata_type_t metadata_type)
80{
81 HR_DEBUG("%s()", __func__);
82
83 errno_t rc;
84
85 hr_volume_t *vol = calloc(1, sizeof(hr_volume_t));
86 if (vol == NULL)
87 return ENOMEM;
88
89 str_cpy(vol->devname, HR_DEVNAME_LEN, devname);
90 vol->level = level;
91
92 vol->meta_ops = get_type_ops(metadata_type);
93
94 uint8_t meta_flags = vol->meta_ops->get_flags();
95
96 switch (level) {
97 case HR_LVL_0:
98 vol->hr_ops.create = hr_raid0_create;
99 vol->hr_ops.init = hr_raid0_init;
100 vol->hr_ops.state_event = hr_raid0_state_event;
101 break;
102 case HR_LVL_1:
103 vol->hr_ops.create = hr_raid1_create;
104 vol->hr_ops.init = hr_raid1_init;
105 vol->hr_ops.state_event = hr_raid1_state_event;
106 if (meta_flags & HR_METADATA_HOTSPARE_SUPPORT)
107 vol->hr_ops.add_hotspare = hr_raid1_add_hotspare;
108 break;
109 case HR_LVL_4:
110 case HR_LVL_5:
111 vol->hr_ops.create = hr_raid5_create;
112 vol->hr_ops.init = hr_raid5_init;
113 vol->hr_ops.state_event = hr_raid5_state_event;
114 if (meta_flags & HR_METADATA_HOTSPARE_SUPPORT)
115 vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
116 break;
117 default:
118 HR_DEBUG("unkown level: %d, aborting\n", vol->level);
119 rc = EINVAL;
120 goto error;
121 }
122
123 vol->fge = hr_fpool_create(16, 32, sizeof(hr_io_t));
124 if (vol->fge == NULL) {
125 rc = ENOMEM;
126 goto error;
127 }
128
129 vol->in_mem_md = vol->meta_ops->alloc_struct();
130 if (vol->in_mem_md == NULL) {
131 free(vol->fge);
132 rc = ENOMEM;
133 goto error;
134 }
135
136 vol->state = HR_VOL_NONE;
137
138 fibril_mutex_initialize(&vol->lock); /* XXX: will remove this */
139
140 fibril_mutex_initialize(&vol->md_lock);
141
142 fibril_rwlock_initialize(&vol->extents_lock);
143 fibril_rwlock_initialize(&vol->states_lock);
144
145 fibril_mutex_initialize(&vol->hotspare_lock);
146
147 list_initialize(&vol->range_lock_list);
148 fibril_mutex_initialize(&vol->range_lock_list_lock);
149
150 atomic_init(&vol->rebuild_blk, 0);
151 atomic_init(&vol->state_dirty, false);
152 atomic_init(&vol->open_cnt, 0);
153
154 *rvol = vol;
155
156 return EOK;
157error:
158 free(vol);
159 return rc;
160}
161
162void hr_destroy_vol_struct(hr_volume_t *vol)
163{
164 HR_DEBUG("%s()", __func__);
165
166 if (vol == NULL)
167 return;
168
169 hr_fpool_destroy(vol->fge);
170 hr_fini_devs(vol);
171 free(vol->in_mem_md);
172 free(vol);
173}
174
175errno_t hr_get_volume_svcs(size_t *rcnt, service_id_t **rsvcs)
176{
177 size_t i;
178 service_id_t *vol_svcs;
179
180 if (rcnt == NULL || rsvcs == NULL)
181 return EINVAL;
182
183 fibril_rwlock_read_lock(&hr_volumes_lock);
184
185 size_t vol_cnt = list_count(&hr_volumes);
186 vol_svcs = malloc(vol_cnt * sizeof(service_id_t));
187 if (vol_svcs == NULL) {
188 fibril_rwlock_read_unlock(&hr_volumes_lock);
189 return ENOMEM;
190 }
191
192 i = 0;
193 list_foreach(hr_volumes, lvolumes, hr_volume_t, iter)
194 vol_svcs[i++] = iter->svc_id;
195
196 fibril_rwlock_read_unlock(&hr_volumes_lock);
197
198 *rcnt = vol_cnt;
199 *rsvcs = vol_svcs;
200
201 return EOK;
202}
203
204hr_volume_t *hr_get_volume(service_id_t svc_id)
205{
206 HR_DEBUG("%s()", __func__);
207
208 hr_volume_t *rvol = NULL;
209
210 fibril_rwlock_read_lock(&hr_volumes_lock);
211 list_foreach(hr_volumes, lvolumes, hr_volume_t, iter) {
212 if (iter->svc_id == svc_id) {
213 rvol = iter;
214 break;
215 }
216 }
217 fibril_rwlock_read_unlock(&hr_volumes_lock);
218
219 return rvol;
220}
221
222errno_t hr_remove_volume(service_id_t svc_id)
223{
224 HR_DEBUG("%s()", __func__);
225
226 hr_volume_t *vol = hr_get_volume(svc_id);
227 if (vol == NULL)
228 return ENOENT;
229
230 fibril_rwlock_write_lock(&hr_volumes_lock);
231
232 int open_cnt = atomic_load_explicit(&vol->open_cnt,
233 memory_order_relaxed);
234
235 /*
236 * The atomicity of this if condition (and this whole
237 * operation) is provided by the write lock - no new
238 * bd connection can come, because we need to get the
239 * bd_srvs_t from the volume, which we get from the list.
240 * (see hr_client_conn() in hr.c)
241 */
242 if (open_cnt > 0) {
243 fibril_rwlock_write_unlock(&hr_volumes_lock);
244 return EBUSY;
245 }
246
247 list_remove(&vol->lvolumes);
248
249 fibril_rwlock_write_unlock(&hr_volumes_lock);
250
251 /* save metadata, but we don't care about states anymore */
252 (void)vol->meta_ops->save(vol, NO_STATE_CALLBACK);
253
254 HR_NOTE("deactivating volume \"%s\"\n", vol->devname);
255
256 hr_destroy_vol_struct(vol);
257
258 errno_t rc = loc_service_unregister(hr_srv, svc_id);
259 return rc;
260}
261
262errno_t hr_init_extents_from_cfg(hr_volume_t *vol, hr_config_t *cfg)
263{
264 HR_DEBUG("%s()", __func__);
265
266 errno_t rc;
267 uint64_t blkno, smallest_blkno = ~0ULL;
268 size_t i, bsize;
269 size_t last_bsize = 0;
270
271 for (i = 0; i < cfg->dev_no; i++) {
272 service_id_t svc_id = cfg->devs[i];
273 if (svc_id == 0) {
274 rc = EINVAL;
275 goto error;
276 }
277
278 HR_DEBUG("%s(): block_init() on (%" PRIun ")\n", __func__,
279 svc_id);
280 rc = block_init(svc_id);
281 if (rc != EOK) {
282 HR_DEBUG("%s(): initing (%" PRIun ") failed, "
283 "aborting\n", __func__, svc_id);
284 goto error;
285 }
286
287 rc = block_get_nblocks(svc_id, &blkno);
288 if (rc != EOK)
289 goto error;
290
291 rc = block_get_bsize(svc_id, &bsize);
292 if (rc != EOK)
293 goto error;
294
295 if (last_bsize != 0 && bsize != last_bsize) {
296 HR_DEBUG("block sizes differ\n");
297 rc = EINVAL;
298 goto error;
299 }
300
301 vol->extents[i].svc_id = svc_id;
302 vol->extents[i].state = HR_EXT_ONLINE;
303
304 if (blkno < smallest_blkno)
305 smallest_blkno = blkno;
306 last_bsize = bsize;
307 }
308
309 vol->bsize = last_bsize;
310 vol->extent_no = cfg->dev_no;
311 vol->truncated_blkno = smallest_blkno;
312
313 for (i = 0; i < HR_MAX_HOTSPARES; i++)
314 vol->hotspares[i].state = HR_EXT_MISSING;
315
316 return EOK;
317
318error:
319 for (i = 0; i < HR_MAX_EXTENTS; i++) {
320 if (vol->extents[i].svc_id != 0)
321 block_fini(vol->extents[i].svc_id);
322 }
323
324 return rc;
325}
326
327void hr_fini_devs(hr_volume_t *vol)
328{
329 HR_DEBUG("%s()", __func__);
330
331 size_t i;
332
333 for (i = 0; i < vol->extent_no; i++) {
334 if (vol->extents[i].svc_id != 0) {
335 HR_DEBUG("hr_fini_devs(): block_fini() on "
336 "(%" PRIun ")\n", vol->extents[i].svc_id);
337 block_fini(vol->extents[i].svc_id);
338 }
339 }
340
341 for (i = 0; i < vol->hotspare_no; i++) {
342 if (vol->hotspares[i].svc_id != 0) {
343 HR_DEBUG("hr_fini_devs(): block_fini() on "
344 "(%" PRIun ")\n",
345 vol->hotspares[i].svc_id);
346 block_fini(vol->hotspares[i].svc_id);
347 }
348 }
349}
350
351errno_t hr_register_volume(hr_volume_t *vol)
352{
353 HR_DEBUG("%s()", __func__);
354
355 errno_t rc;
356 service_id_t new_id;
357 category_id_t cat_id;
358 const char *devname = vol->devname;
359
360 rc = loc_service_register(hr_srv, devname, &new_id);
361 if (rc != EOK) {
362 HR_ERROR("unable to register device \"%s\": %s\n",
363 devname, str_error(rc));
364 return rc;
365 }
366
367 rc = loc_category_get_id("raid", &cat_id, IPC_FLAG_BLOCKING);
368 if (rc != EOK) {
369 HR_ERROR("failed resolving category \"raid\": %s\n",
370 str_error(rc));
371 goto error;
372 }
373
374 rc = loc_service_add_to_cat(hr_srv, new_id, cat_id);
375 if (rc != EOK) {
376 HR_ERROR("failed adding \"%s\" to category \"raid\": %s\n",
377 devname, str_error(rc));
378 goto error;
379 }
380
381 vol->svc_id = new_id;
382 return EOK;
383error:
384 rc = loc_service_unregister(hr_srv, new_id);
385 return rc;
386}
387
388errno_t hr_check_ba_range(hr_volume_t *vol, size_t cnt, uint64_t ba)
389{
390 if (ba + cnt > vol->data_blkno)
391 return ERANGE;
392 return EOK;
393}
394
395void hr_add_ba_offset(hr_volume_t *vol, uint64_t *ba)
396{
397 *ba = *ba + vol->data_offset;
398}
399
400void hr_update_ext_state(hr_volume_t *vol, size_t ext_idx, hr_ext_state_t s)
401{
402 if (vol->level != HR_LVL_0)
403 assert(fibril_rwlock_is_locked(&vol->extents_lock));
404
405 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
406
407 assert(ext_idx < vol->extent_no);
408
409 hr_ext_state_t old = vol->extents[ext_idx].state;
410 HR_NOTE("\"%s\": changing extent %zu state: %s -> %s\n",
411 vol->devname, ext_idx, hr_get_ext_state_str(old),
412 hr_get_ext_state_str(s));
413 vol->extents[ext_idx].state = s;
414}
415
416void hr_update_hotspare_state(hr_volume_t *vol, size_t hs_idx,
417 hr_ext_state_t s)
418{
419 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
420
421 assert(hs_idx < vol->hotspare_no);
422
423 hr_ext_state_t old = vol->hotspares[hs_idx].state;
424 HR_NOTE("\"%s\": changing hotspare %zu state: %s -> %s\n",
425 vol->devname, hs_idx, hr_get_ext_state_str(old),
426 hr_get_ext_state_str(s));
427 vol->hotspares[hs_idx].state = s;
428}
429
430void hr_update_vol_state(hr_volume_t *vol, hr_vol_state_t new)
431{
432 assert(fibril_rwlock_is_write_locked(&vol->states_lock));
433
434 HR_NOTE("\"%s\": changing volume state: %s -> %s\n", vol->devname,
435 hr_get_vol_state_str(vol->state), hr_get_vol_state_str(new));
436 vol->state = new;
437}
438
439void hr_update_ext_svc_id(hr_volume_t *vol, size_t ext_idx, service_id_t new)
440{
441 if (vol->level != HR_LVL_0)
442 assert(fibril_rwlock_is_write_locked(&vol->extents_lock));
443
444 assert(ext_idx < vol->extent_no);
445
446 service_id_t old = vol->extents[ext_idx].svc_id;
447 HR_NOTE("\"%s\": changing extent no. %zu svc_id: (%" PRIun ") -> "
448 "(%" PRIun ")\n", vol->devname, ext_idx, old, new);
449 vol->extents[ext_idx].svc_id = new;
450}
451
452void hr_update_hotspare_svc_id(hr_volume_t *vol, size_t hs_idx,
453 service_id_t new)
454{
455 assert(fibril_mutex_is_locked(&vol->hotspare_lock));
456
457 assert(hs_idx < vol->hotspare_no);
458
459 service_id_t old = vol->hotspares[hs_idx].svc_id;
460 HR_NOTE("\"%s\": changing hotspare no. %zu svc_id: (%" PRIun ") -> "
461 "(%" PRIun ")\n", vol->devname, hs_idx, old, new);
462 vol->hotspares[hs_idx].svc_id = new;
463}
464
465/*
466 * Do a whole sync (ba = 0, cnt = 0) across all extents,
467 * and update extent state. *For now*, the caller has to
468 * update volume state after the syncs.
469 *
470 * TODO: add update_vol_state fcn ptr for each raid
471 */
472void hr_sync_all_extents(hr_volume_t *vol)
473{
474 errno_t rc;
475
476 fibril_mutex_lock(&vol->lock);
477 for (size_t i = 0; i < vol->extent_no; i++) {
478 if (vol->extents[i].state != HR_EXT_ONLINE)
479 continue;
480 rc = block_sync_cache(vol->extents[i].svc_id, 0, 0);
481 if (rc == ENOMEM || rc == ENOTSUP)
482 continue;
483 if (rc != EOK) {
484 if (rc == ENOENT)
485 hr_update_ext_state(vol, i, HR_EXT_MISSING);
486 else if (rc != EOK)
487 hr_update_ext_state(vol, i, HR_EXT_FAILED);
488 }
489 }
490 fibril_mutex_unlock(&vol->lock);
491}
492
493size_t hr_count_extents(hr_volume_t *vol, hr_ext_state_t state)
494{
495 if (vol->level != HR_LVL_0)
496 assert(fibril_rwlock_is_locked(&vol->extents_lock));
497 assert(fibril_rwlock_is_locked(&vol->states_lock));
498
499 size_t count = 0;
500 for (size_t i = 0; i < vol->extent_no; i++)
501 if (vol->extents[i].state == state)
502 count++;
503
504 return count;
505}
506
507hr_range_lock_t *hr_range_lock_acquire(hr_volume_t *vol, uint64_t ba,
508 uint64_t cnt)
509{
510 hr_range_lock_t *rl = malloc(sizeof(hr_range_lock_t));
511 if (rl == NULL)
512 return NULL;
513
514 rl->vol = vol;
515 rl->off = ba;
516 rl->len = cnt;
517
518 rl->pending = 1;
519 rl->ignore = false;
520
521 link_initialize(&rl->link);
522 fibril_mutex_initialize(&rl->lock);
523
524 fibril_mutex_lock(&rl->lock);
525
526again:
527 HR_RL_LIST_LOCK(vol);
528 list_foreach(vol->range_lock_list, link, hr_range_lock_t, rlp) {
529 if (rlp->ignore)
530 continue;
531 if (hr_range_lock_overlap(rlp, rl)) {
532 rlp->pending++;
533
534 HR_RL_LIST_UNLOCK(vol);
535
536 fibril_mutex_lock(&rlp->lock);
537
538 HR_RL_LIST_LOCK(vol);
539
540 rlp->pending--;
541
542 /*
543 * when ignore is set, after HR_RL_LIST_UNLOCK(),
544 * noone new is going to be able to start sleeping
545 * on the ignored range lock, only already waiting
546 * IOs will come through here
547 */
548 rlp->ignore = true;
549
550 fibril_mutex_unlock(&rlp->lock);
551
552 if (rlp->pending == 0) {
553 list_remove(&rlp->link);
554 free(rlp);
555 }
556
557 HR_RL_LIST_UNLOCK(vol);
558 goto again;
559 }
560 }
561
562 list_append(&rl->link, &vol->range_lock_list);
563
564 HR_RL_LIST_UNLOCK(vol);
565 return rl;
566}
567
568void hr_range_lock_release(hr_range_lock_t *rl)
569{
570 if (rl == NULL)
571 return;
572
573 HR_RL_LIST_LOCK(rl->vol);
574
575 rl->pending--;
576
577 fibril_mutex_unlock(&rl->lock);
578
579 if (rl->pending == 0) {
580 list_remove(&rl->link);
581 free(rl);
582 }
583
584 HR_RL_LIST_UNLOCK(rl->vol);
585}
586
587static bool hr_range_lock_overlap(hr_range_lock_t *rl1, hr_range_lock_t *rl2)
588{
589 uint64_t rl1_start = rl1->off;
590 uint64_t rl1_end = rl1->off + rl1->len - 1;
591 uint64_t rl2_start = rl2->off;
592 uint64_t rl2_end = rl2->off + rl2->len - 1;
593
594 /* one ends before the other starts */
595 if (rl1_end < rl2_start || rl2_end < rl1_start)
596 return false;
597
598 return true;
599}
600
601void hr_mark_vol_state_dirty(hr_volume_t *vol)
602{
603 atomic_store(&vol->state_dirty, true);
604}
605
606static errno_t hr_add_svc_linked_to_list(list_t *list, service_id_t svc_id,
607 bool inited, void *md)
608{
609 HR_DEBUG("%s()", __func__);
610
611 errno_t rc = EOK;
612 struct dev_list_member *to_add;
613
614 if (list == NULL)
615 return EINVAL;
616
617 to_add = malloc(sizeof(struct dev_list_member));
618 if (to_add == NULL) {
619 rc = ENOMEM;
620 goto error;
621 }
622
623 to_add->svc_id = svc_id;
624 to_add->inited = inited;
625
626 if (md != NULL) {
627 to_add->md = md;
628 to_add->md_present = true;
629 } else {
630 to_add->md_present = false;
631 }
632
633 list_append(&to_add->link, list);
634
635error:
636 return rc;
637}
638
639static void free_dev_list_member(struct dev_list_member *p)
640{
641 HR_DEBUG("%s()", __func__);
642
643 if (p->md_present)
644 free(p->md);
645 free(p);
646}
647
648static void free_svc_id_list(list_t *list)
649{
650 HR_DEBUG("%s()", __func__);
651
652 struct dev_list_member *dev_id;
653 while (!list_empty(list)) {
654 dev_id = list_pop(list, struct dev_list_member, link);
655
656 free_dev_list_member(dev_id);
657 }
658}
659
660static errno_t hr_fill_disk_part_svcs_list(list_t *list)
661{
662 HR_DEBUG("%s()", __func__);
663
664 errno_t rc;
665 size_t disk_count;
666 service_id_t *disk_svcs = NULL;
667 vbd_t *vbd = NULL;
668
669 rc = vbd_create(&vbd);
670 if (rc != EOK)
671 goto error;
672
673 rc = vbd_get_disks(vbd, &disk_svcs, &disk_count);
674 if (rc != EOK)
675 goto error;
676
677 for (size_t i = 0; i < disk_count; i++) {
678 vbd_disk_info_t disk_info;
679 rc = vbd_disk_info(vbd, disk_svcs[i], &disk_info);
680 if (rc != EOK)
681 goto error;
682
683 if (disk_info.ltype != lt_none) {
684 size_t part_count;
685 service_id_t *part_ids = NULL;
686 rc = vbd_label_get_parts(vbd, disk_svcs[i], &part_ids,
687 &part_count);
688 if (rc != EOK)
689 goto error;
690
691 for (size_t j = 0; j < part_count; j++) {
692 vbd_part_info_t part_info;
693 rc = vbd_part_get_info(vbd, part_ids[j],
694 &part_info);
695 if (rc != EOK) {
696 free(part_ids);
697 goto error;
698 }
699
700 rc = hr_add_svc_linked_to_list(list,
701 part_info.svc_id, false, NULL);
702 if (rc != EOK) {
703 free(part_ids);
704 goto error;
705 }
706 }
707
708 free(part_ids);
709
710 /*
711 * vbd can detect some bogus label type, but
712 * no partitions. In that case we handle the
713 * svc_id as a label-less disk.
714 *
715 * This can happen when creating an exfat fs
716 * in FreeBSD for example.
717 */
718 if (part_count == 0)
719 disk_info.ltype = lt_none;
720 }
721
722 if (disk_info.ltype == lt_none) {
723 rc = hr_add_svc_linked_to_list(list, disk_svcs[i],
724 false, NULL);
725 if (rc != EOK)
726 goto error;
727 }
728 }
729
730 free(disk_svcs);
731 vbd_destroy(vbd);
732 return EOK;
733error:
734 free_svc_id_list(list);
735 if (disk_svcs != NULL)
736 free(disk_svcs);
737 vbd_destroy(vbd);
738
739 return rc;
740}
741
742static errno_t block_init_dev_list(list_t *list)
743{
744 HR_DEBUG("%s()", __func__);
745
746 list_foreach_safe(*list, cur_link, next_link) {
747 struct dev_list_member *iter;
748 iter = list_get_instance(cur_link, struct dev_list_member,
749 link);
750
751 if (iter->inited)
752 continue;
753
754 errno_t rc = block_init(iter->svc_id);
755
756 /* already used as an extent of active volume */
757 /* XXX: figure out how it is with hotspares too */
758 if (rc == EEXIST) {
759 list_remove(cur_link);
760 free_dev_list_member(iter);
761 continue;
762 }
763
764 if (rc != EOK)
765 return rc;
766
767 iter->inited = true;
768 iter->fini = true;
769 }
770
771 return EOK;
772}
773
774static void block_fini_dev_list(list_t *list)
775{
776 HR_DEBUG("%s()", __func__);
777
778 list_foreach(*list, link, struct dev_list_member, iter) {
779 if (iter->inited && iter->fini) {
780 block_fini(iter->svc_id);
781 iter->inited = false;
782 iter->fini = false;
783 }
784 }
785}
786
787static errno_t hr_util_get_matching_md_svcs_list(list_t *rlist, list_t *list,
788 service_id_t svc_id, hr_metadata_type_t type_main,
789 void *metadata_struct_main)
790{
791 HR_DEBUG("%s()", __func__);
792
793 errno_t rc = EOK;
794
795 hr_superblock_ops_t *meta_ops = get_type_ops(type_main);
796
797 list_foreach(*list, link, struct dev_list_member, iter) {
798 if (iter->svc_id == svc_id)
799 continue;
800
801 void *metadata_struct;
802 hr_metadata_type_t type;
803
804 rc = find_metadata(iter->svc_id, &metadata_struct, &type);
805 if (rc == ENOFS)
806 continue;
807 if (rc != EOK)
808 goto error;
809
810 if (type != type_main) {
811 free(metadata_struct);
812 continue;
813 }
814
815 if (!meta_ops->compare_uuids(metadata_struct_main,
816 metadata_struct)) {
817 free(metadata_struct);
818 continue;
819 }
820
821 rc = hr_add_svc_linked_to_list(rlist, iter->svc_id, true,
822 metadata_struct);
823 if (rc != EOK)
824 goto error;
825 }
826
827 return EOK;
828error:
829 free_svc_id_list(rlist);
830 return rc;
831}
832
833static errno_t hr_util_assemble_from_matching_list(list_t *list,
834 hr_metadata_type_t type)
835{
836 HR_DEBUG("%s()", __func__);
837
838 errno_t rc = EOK;
839
840 hr_superblock_ops_t *meta_ops = get_type_ops(type);
841
842 link_t *memb_l = list_first(list);
843 struct dev_list_member *memb = list_get_instance(memb_l,
844 struct dev_list_member, link);
845
846 hr_level_t level = meta_ops->get_level(memb->md);
847 const char *devname = meta_ops->get_devname(memb->md);
848
849 hr_volume_t *vol;
850 rc = hr_create_vol_struct(&vol, level, devname, type);
851 if (rc != EOK)
852 return rc;
853
854 meta_ops->init_meta2vol(list, vol);
855
856 /*
857 * TODO: something like mark md dirty or whatever
858 * - probably will be handled by each md type differently,
859 * by specific function pointers
860 * - deal with this when foreign md will be handled
861 *
862 * XXX: if thats the only thing that can change in metadata
863 * during volume runtime, then whatever, but if more
864 * things will need to be synced, think of something more clever
865 *
866 * TODO: remove from here and increment it the "first" time (if nothing
867 * happens - no state changes, no rebuild, etc) - only after the first
868 * write... but for now leave it here
869 */
870 (void)vol->meta_ops->inc_counter(vol->in_mem_md);
871
872 rc = vol->hr_ops.create(vol);
873 if (rc != EOK)
874 goto error;
875
876 /*
877 * XXX: register it here
878 * ... if it fails on EEXIST try different name... like + 1 on the end
879 *
880 * or have metadata edit utility as a part of hrctl..., or create
881 * the original name + 4 random characters, tell the user that the device
882 * was created with this new name, and add a option to hrctl to rename
883 * an active array, and then write the new dirty metadata...
884 *
885 * or just refuse to assemble a name that is already used...
886 *
887 * TODO: discuss
888 */
889 rc = hr_register_volume(vol);
890 if (rc != EOK)
891 goto error;
892
893 (void)vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
894
895 fibril_rwlock_write_lock(&hr_volumes_lock);
896 list_append(&vol->lvolumes, &hr_volumes);
897 fibril_rwlock_write_unlock(&hr_volumes_lock);
898
899 HR_NOTE("assembled volume \"%s\"\n", vol->devname);
900
901 return EOK;
902error:
903 hr_destroy_vol_struct(vol);
904 return rc;
905}
906
907static errno_t hr_fill_svcs_list_from_cfg(hr_config_t *cfg, list_t *list)
908{
909 HR_DEBUG("%s()", __func__);
910
911 errno_t rc = EOK;
912 for (size_t i = 0; i < cfg->dev_no; ++i) {
913 rc = hr_add_svc_linked_to_list(list, cfg->devs[i], false,
914 NULL);
915 if (rc != EOK)
916 goto error;
917 }
918
919 return EOK;
920error:
921 free_svc_id_list(list);
922 return rc;
923}
924
925errno_t hr_util_try_assemble(hr_config_t *cfg, size_t *rassembled_cnt)
926{
927 HR_DEBUG("%s()", __func__);
928
929 /*
930 * scan partitions or disks:
931 *
932 * When we find a metadata block with valid
933 * magic, take UUID and try to find other matching
934 * UUIDs.
935 *
936 * We ignore extents that are a part of already
937 * active volumes. (even when the counter is lower
938 * on active volumes... XXX: use timestamp as initial counter value
939 * when assembling, or writing dirty metadata?)
940 */
941
942 size_t asm_cnt = 0;
943 errno_t rc;
944 list_t dev_id_list;
945
946 list_initialize(&dev_id_list);
947
948 if (cfg == NULL)
949 rc = hr_fill_disk_part_svcs_list(&dev_id_list);
950 else
951 rc = hr_fill_svcs_list_from_cfg(cfg, &dev_id_list);
952
953 if (rc != EOK)
954 goto error;
955
956 rc = block_init_dev_list(&dev_id_list);
957 if (rc != EOK)
958 goto error;
959
960 struct dev_list_member *iter;
961 while (!list_empty(&dev_id_list)) {
962 iter = list_pop(&dev_id_list, struct dev_list_member, link);
963
964 void *metadata_struct_main;
965 hr_metadata_type_t type;
966
967 rc = find_metadata(iter->svc_id, &metadata_struct_main, &type);
968 if (rc == ENOFS) {
969 block_fini(iter->svc_id);
970 free_dev_list_member(iter);
971 rc = EOK;
972 continue;
973 }
974
975 if (rc != EOK)
976 goto error;
977
978 char *svc_name = NULL;
979 rc = loc_service_get_name(iter->svc_id, &svc_name);
980 if (rc != EOK)
981 goto error;
982 HR_DEBUG("found valid metadata on %s (type = %s), matching "
983 "other extents\n",
984 svc_name, hr_get_metadata_type_str(type));
985 free(svc_name);
986
987 list_t matching_svcs_list;
988 list_initialize(&matching_svcs_list);
989
990 rc = hr_util_get_matching_md_svcs_list(&matching_svcs_list,
991 &dev_id_list, iter->svc_id, type, metadata_struct_main);
992 if (rc != EOK)
993 goto error;
994
995 /* add current iter to list as well */
996 rc = hr_add_svc_linked_to_list(&matching_svcs_list,
997 iter->svc_id, true, metadata_struct_main);
998 if (rc != EOK) {
999 free_svc_id_list(&matching_svcs_list);
1000 goto error;
1001 }
1002
1003 /* remove matching list members from dev_id_list */
1004 list_foreach(matching_svcs_list, link, struct dev_list_member,
1005 iter2) {
1006 struct dev_list_member *to_remove;
1007 list_foreach_safe(dev_id_list, cur_link, next_link) {
1008 to_remove = list_get_instance(cur_link,
1009 struct dev_list_member, link);
1010 if (to_remove->svc_id == iter2->svc_id) {
1011 list_remove(cur_link);
1012 free_dev_list_member(to_remove);
1013 }
1014 }
1015 }
1016
1017 rc = hr_util_assemble_from_matching_list(&matching_svcs_list,
1018 type);
1019 switch (rc) {
1020 case EOK:
1021 asm_cnt++;
1022 break;
1023 case ENOMEM:
1024 goto error;
1025 default:
1026 rc = EOK;
1027 }
1028 block_fini_dev_list(&matching_svcs_list);
1029 free_svc_id_list(&matching_svcs_list);
1030 }
1031
1032error:
1033 if (rassembled_cnt != NULL)
1034 *rassembled_cnt = asm_cnt;
1035
1036 block_fini_dev_list(&dev_id_list);
1037 free_svc_id_list(&dev_id_list);
1038
1039 return rc;
1040}
1041
1042errno_t hr_util_add_hotspare(hr_volume_t *vol, service_id_t hotspare)
1043{
1044 HR_DEBUG("%s()", __func__);
1045
1046 errno_t rc = EOK;
1047
1048 fibril_mutex_lock(&vol->hotspare_lock);
1049
1050 if (vol->hotspare_no >= HR_MAX_HOTSPARES) {
1051 HR_ERROR("%s(): cannot add more hotspares "
1052 "to \"%s\"\n", __func__, vol->devname);
1053 rc = ELIMIT;
1054 goto error;
1055 }
1056
1057 for (size_t i = 0; i < vol->hotspare_no; i++) {
1058 if (vol->hotspares[i].svc_id == hotspare) {
1059 HR_ERROR("%s(): hotspare (%" PRIun ") already used in "
1060 "%s\n", __func__, hotspare, vol->devname);
1061 rc = EEXIST;
1062 goto error;
1063 }
1064 }
1065
1066 rc = block_init(hotspare);
1067 if (rc != EOK)
1068 goto error;
1069
1070 uint64_t hs_blkno;
1071 rc = block_get_nblocks(hotspare, &hs_blkno);
1072 if (rc != EOK) {
1073 block_fini(hotspare);
1074 goto error;
1075 }
1076
1077 if (hs_blkno < vol->truncated_blkno) {
1078 HR_ERROR("%s(): hotspare (%" PRIun ") doesn't have enough "
1079 "blocks\n", __func__, hotspare);
1080
1081 rc = EINVAL;
1082 block_fini(hotspare);
1083 goto error;
1084 }
1085
1086 size_t hs_idx = vol->hotspare_no;
1087
1088 vol->hotspare_no++;
1089
1090 hr_update_hotspare_svc_id(vol, hs_idx, hotspare);
1091 hr_update_hotspare_state(vol, hs_idx, HR_EXT_HOTSPARE);
1092
1093 hr_mark_vol_state_dirty(vol);
1094error:
1095 fibril_mutex_unlock(&vol->hotspare_lock);
1096 return rc;
1097}
1098
1099/** @}
1100 */
Note: See TracBrowser for help on using the repository browser.