source: mainline/uspace/srv/bd/vbd/disk.c@ d231a54

Last change on this file since d231a54 was ddfe233, checked in by Miroslav Cimerman <mc@…>, 12 months ago

vbd: free disk service name on disk remove

  • Property mode set to 100644
File size: 29.3 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
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 vbd
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <adt/list.h>
37#include <bd_srv.h>
38#include <block.h>
39#include <errno.h>
40#include <str_error.h>
41#include <io/log.h>
42#include <label/empty.h>
43#include <label/label.h>
44#include <loc.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <task.h>
48#include <vbd.h>
49
50#include "disk.h"
51#include "types/vbd.h"
52
53loc_srv_t *vbds_srv;
54
55static fibril_mutex_t vbds_disks_lock;
56static list_t vbds_disks; /* of vbds_disk_t */
57static fibril_mutex_t vbds_parts_lock;
58static list_t vbds_parts; /* of vbds_part_t */
59
60static category_id_t part_cid;
61
62static errno_t vbds_disk_parts_add(vbds_disk_t *, label_t *);
63static errno_t vbds_disk_parts_remove(vbds_disk_t *, vbds_rem_flag_t);
64
65static errno_t vbds_bd_open(bd_srvs_t *, bd_srv_t *);
66static errno_t vbds_bd_close(bd_srv_t *);
67static errno_t vbds_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
68static errno_t vbds_bd_sync_cache(bd_srv_t *, aoff64_t, size_t);
69static errno_t vbds_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *,
70 size_t);
71static errno_t vbds_bd_get_block_size(bd_srv_t *, size_t *);
72static errno_t vbds_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
73
74static errno_t vbds_bsa_translate(vbds_part_t *, aoff64_t, size_t, aoff64_t *);
75
76static errno_t vbds_part_svc_register(vbds_part_t *);
77static errno_t vbds_part_svc_unregister(vbds_part_t *);
78static errno_t vbds_part_indices_update(vbds_disk_t *);
79
80static vbd_part_id_t vbds_part_id = 1;
81
82static errno_t vbds_label_get_bsize(void *, size_t *);
83static errno_t vbds_label_get_nblocks(void *, aoff64_t *);
84static errno_t vbds_label_read(void *, aoff64_t, size_t, void *);
85static errno_t vbds_label_write(void *, aoff64_t, size_t, const void *);
86
87/** Block device operations provided by VBD */
88static bd_ops_t vbds_bd_ops = {
89 .open = vbds_bd_open,
90 .close = vbds_bd_close,
91 .read_blocks = vbds_bd_read_blocks,
92 .sync_cache = vbds_bd_sync_cache,
93 .write_blocks = vbds_bd_write_blocks,
94 .get_block_size = vbds_bd_get_block_size,
95 .get_num_blocks = vbds_bd_get_num_blocks
96};
97
98/** Provide disk access to liblabel */
99static label_bd_ops_t vbds_label_bd_ops = {
100 .get_bsize = vbds_label_get_bsize,
101 .get_nblocks = vbds_label_get_nblocks,
102 .read = vbds_label_read,
103 .write = vbds_label_write
104};
105
106static vbds_part_t *bd_srv_part(bd_srv_t *bd)
107{
108 return (vbds_part_t *)bd->srvs->sarg;
109}
110
111static vbds_disk_t *vbds_disk_first(void)
112{
113 link_t *link;
114
115 link = list_first(&vbds_disks);
116 if (link == NULL)
117 return NULL;
118
119 return list_get_instance(link, vbds_disk_t, ldisks);
120}
121
122static vbds_disk_t *vbds_disk_next(vbds_disk_t *disk)
123{
124 link_t *link;
125
126 if (disk == NULL)
127 return NULL;
128
129 link = list_next(&disk->ldisks, &vbds_disks);
130 if (link == NULL)
131 return NULL;
132
133 return list_get_instance(link, vbds_disk_t, ldisks);
134}
135
136errno_t vbds_disks_init(void)
137{
138 errno_t rc;
139
140 fibril_mutex_initialize(&vbds_disks_lock);
141 list_initialize(&vbds_disks);
142 fibril_mutex_initialize(&vbds_parts_lock);
143 list_initialize(&vbds_parts);
144
145 rc = loc_category_get_id("partition", &part_cid, 0);
146 if (rc != EOK) {
147 log_msg(LOG_DEFAULT, LVL_ERROR, "Error looking up partition "
148 "category.");
149 return EIO;
150 }
151
152 return EOK;
153}
154
155/** Check for new/removed disk devices */
156static errno_t vbds_disks_check_new(void)
157{
158 bool already_known;
159 category_id_t disk_cat;
160 service_id_t *svcs;
161 size_t count, i;
162 vbds_disk_t *cur, *next;
163 errno_t rc;
164
165 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disks_check_new()");
166
167 fibril_mutex_lock(&vbds_disks_lock);
168
169 rc = loc_category_get_id("disk", &disk_cat, IPC_FLAG_BLOCKING);
170 if (rc != EOK) {
171 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'disk'.");
172 fibril_mutex_unlock(&vbds_disks_lock);
173 return ENOENT;
174 }
175
176 rc = loc_category_get_svcs(disk_cat, &svcs, &count);
177 if (rc != EOK) {
178 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of disk "
179 "devices.");
180 fibril_mutex_unlock(&vbds_disks_lock);
181 return EIO;
182 }
183
184 list_foreach(vbds_disks, ldisks, vbds_disk_t, disk)
185 disk->present = false;
186
187 for (i = 0; i < count; i++) {
188 already_known = false;
189
190 list_foreach(vbds_disks, ldisks, vbds_disk_t, disk) {
191 if (disk->svc_id == svcs[i]) {
192 already_known = true;
193 disk->present = true;
194 break;
195 }
196 }
197
198 if (!already_known) {
199 log_msg(LOG_DEFAULT, LVL_NOTE, "Found disk '%lu'",
200 (unsigned long) svcs[i]);
201 rc = vbds_disk_add(svcs[i]);
202 if (rc != EOK) {
203 log_msg(LOG_DEFAULT, LVL_ERROR, "Could not add "
204 "disk.");
205 }
206 } else {
207 log_msg(LOG_DEFAULT, LVL_DEBUG, "Disk %lu already known",
208 (unsigned long) svcs[i]);
209 }
210 }
211
212 cur = vbds_disk_first();
213 while (cur != NULL) {
214 next = vbds_disk_next(cur);
215 if (!cur->present) {
216 log_msg(LOG_DEFAULT, LVL_NOTE, "Disk '%lu' is gone",
217 (unsigned long) cur->svc_id);
218 rc = vbds_disk_remove(cur->svc_id);
219 if (rc != EOK) {
220 log_msg(LOG_DEFAULT, LVL_ERROR, "Could not "
221 "remove disk.");
222 }
223 }
224
225 cur = next;
226 }
227
228 fibril_mutex_unlock(&vbds_disks_lock);
229 return EOK;
230}
231
232static errno_t vbds_disk_by_svcid(service_id_t sid, vbds_disk_t **rdisk)
233{
234 list_foreach(vbds_disks, ldisks, vbds_disk_t, disk) {
235 if (disk->svc_id == sid) {
236 *rdisk = disk;
237 return EOK;
238 }
239 }
240
241 return ENOENT;
242}
243
244static void vbds_part_add_ref(vbds_part_t *part)
245{
246 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_part_add_ref");
247 refcount_up(&part->refcnt);
248}
249
250static void vbds_part_del_ref(vbds_part_t *part)
251{
252 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_part_del_ref");
253 if (refcount_down(&part->refcnt)) {
254 log_msg(LOG_DEFAULT, LVL_DEBUG2, " - free part");
255 free(part);
256 }
257}
258
259static errno_t vbds_part_by_pid(vbds_part_id_t partid, vbds_part_t **rpart)
260{
261 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_by_pid(%zu)", partid);
262
263 fibril_mutex_lock(&vbds_parts_lock);
264
265 list_foreach(vbds_parts, lparts, vbds_part_t, part) {
266 log_msg(LOG_DEFAULT, LVL_DEBUG, "%zu == %zu ?", part->pid, partid);
267 if (part->pid == partid) {
268 log_msg(LOG_DEFAULT, LVL_DEBUG, "Found match.");
269 vbds_part_add_ref(part);
270 fibril_mutex_unlock(&vbds_parts_lock);
271 *rpart = part;
272 return EOK;
273 }
274 }
275
276 fibril_mutex_unlock(&vbds_parts_lock);
277 log_msg(LOG_DEFAULT, LVL_DEBUG, "No match.");
278 return ENOENT;
279}
280
281static errno_t vbds_part_by_svcid(service_id_t svcid, vbds_part_t **rpart)
282{
283 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_by_svcid(%zu)", svcid);
284
285 fibril_mutex_lock(&vbds_parts_lock);
286
287 list_foreach(vbds_parts, lparts, vbds_part_t, part) {
288 log_msg(LOG_DEFAULT, LVL_DEBUG, "%zu == %zu ?", part->svc_id, svcid);
289 if (part->svc_id == svcid) {
290 log_msg(LOG_DEFAULT, LVL_DEBUG, "Found match.");
291 vbds_part_add_ref(part);
292 fibril_mutex_unlock(&vbds_parts_lock);
293 *rpart = part;
294 return EOK;
295 }
296 }
297
298 fibril_mutex_unlock(&vbds_parts_lock);
299 log_msg(LOG_DEFAULT, LVL_DEBUG, "No match.");
300 return ENOENT;
301}
302
303/** Add partition to our inventory based on liblabel partition structure */
304static errno_t vbds_part_add(vbds_disk_t *disk, label_part_t *lpart,
305 vbds_part_t **rpart)
306{
307 vbds_part_t *part;
308 service_id_t psid = 0;
309 label_part_info_t lpinfo;
310 errno_t rc;
311
312 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_add(%s, %p)",
313 disk->svc_name, lpart);
314
315 label_part_get_info(lpart, &lpinfo);
316
317 part = calloc(1, sizeof(vbds_part_t));
318 if (part == NULL) {
319 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
320 return ENOMEM;
321 }
322
323 fibril_rwlock_initialize(&part->lock);
324
325 part->lpart = lpart;
326 part->disk = disk;
327 part->pid = ++vbds_part_id;
328 part->svc_id = psid;
329 part->block0 = lpinfo.block0;
330 part->nblocks = lpinfo.nblocks;
331 refcount_init(&part->refcnt);
332
333 bd_srvs_init(&part->bds);
334 part->bds.ops = &vbds_bd_ops;
335 part->bds.sarg = part;
336
337 if (lpinfo.pkind != lpk_extended) {
338 rc = vbds_part_svc_register(part);
339 if (rc != EOK) {
340 free(part);
341 return EIO;
342 }
343 }
344
345 list_append(&part->ldisk, &disk->parts);
346 fibril_mutex_lock(&vbds_parts_lock);
347 list_append(&part->lparts, &vbds_parts);
348 fibril_mutex_unlock(&vbds_parts_lock);
349
350 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_add -> %p", part);
351
352 if (rpart != NULL)
353 *rpart = part;
354 return EOK;
355}
356
357/** Remove partition from our inventory leaving only the underlying liblabel
358 * partition structure.
359 *
360 * @param part Partition
361 * @param flag If set to @c vrf_force, force removal even if partition is in use
362 * @param rlpart Place to store pointer to liblabel partition
363 *
364 */
365static errno_t vbds_part_remove(vbds_part_t *part, vbds_rem_flag_t flag,
366 label_part_t **rlpart)
367{
368 label_part_t *lpart;
369 errno_t rc;
370
371 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_remove(%p)", part);
372
373 fibril_rwlock_write_lock(&part->lock);
374 lpart = part->lpart;
375
376 if ((flag & vrf_force) == 0 && part->open_cnt > 0) {
377 fibril_rwlock_write_unlock(&part->lock);
378 log_msg(LOG_DEFAULT, LVL_DEBUG, "part->open_cnt = %d",
379 part->open_cnt);
380 return EBUSY;
381 }
382
383 if (part->svc_id != 0) {
384 rc = vbds_part_svc_unregister(part);
385 if (rc != EOK) {
386 fibril_rwlock_write_unlock(&part->lock);
387 return EIO;
388 }
389 }
390
391 list_remove(&part->ldisk);
392 fibril_mutex_lock(&vbds_parts_lock);
393 list_remove(&part->lparts);
394 fibril_mutex_unlock(&vbds_parts_lock);
395
396 vbds_part_del_ref(part);
397 part->lpart = NULL;
398 fibril_rwlock_write_unlock(&part->lock);
399
400 if (rlpart != NULL)
401 *rlpart = lpart;
402 return EOK;
403}
404
405/** Remove all disk partitions from our inventory leaving only the underlying
406 * liblabel partition structures.
407 */
408static errno_t vbds_disk_parts_add(vbds_disk_t *disk, label_t *label)
409{
410 label_part_t *part;
411 errno_t rc;
412
413 part = label_part_first(label);
414 while (part != NULL) {
415 rc = vbds_part_add(disk, part, NULL);
416 if (rc != EOK) {
417 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding partition "
418 "(disk %s)", disk->svc_name);
419 return rc;
420 }
421
422 part = label_part_next(part);
423 }
424
425 return EOK;
426}
427
428/** Remove all disk partitions from our inventory leaving only the underlying
429 * liblabel partition structures.
430 */
431static errno_t vbds_disk_parts_remove(vbds_disk_t *disk, vbds_rem_flag_t flag)
432{
433 link_t *link;
434 vbds_part_t *part;
435 errno_t rc;
436
437 link = list_first(&disk->parts);
438 while (link != NULL) {
439 part = list_get_instance(link, vbds_part_t, ldisk);
440 rc = vbds_part_remove(part, flag, NULL);
441 if (rc != EOK)
442 return rc;
443
444 link = list_first(&disk->parts);
445 }
446
447 return EOK;
448}
449
450static void vbds_disk_cat_change_cb(void *arg)
451{
452 (void) vbds_disks_check_new();
453}
454
455errno_t vbds_disk_discovery_start(void)
456{
457 errno_t rc;
458
459 rc = loc_register_cat_change_cb(vbds_disk_cat_change_cb, NULL);
460 if (rc != EOK) {
461 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback "
462 "for disk discovery: %s.", str_error(rc));
463 return rc;
464 }
465
466 return vbds_disks_check_new();
467}
468
469errno_t vbds_disk_add(service_id_t sid)
470{
471 label_t *label = NULL;
472 label_bd_t lbd;
473 vbds_disk_t *disk = NULL;
474 bool block_inited = false;
475 size_t block_size;
476 aoff64_t nblocks;
477 errno_t rc;
478
479 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_add(%zu)", sid);
480
481 /* Check for duplicates */
482 rc = vbds_disk_by_svcid(sid, &disk);
483 if (rc == EOK)
484 return EEXIST;
485
486 disk = calloc(1, sizeof(vbds_disk_t));
487 if (disk == NULL)
488 return ENOMEM;
489
490 /* Must be set before calling label_open */
491 disk->svc_id = sid;
492
493 rc = loc_service_get_name(sid, &disk->svc_name);
494 if (rc != EOK) {
495 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting disk service name.");
496 rc = EIO;
497 goto error;
498 }
499
500 log_msg(LOG_DEFAULT, LVL_DEBUG, "block_init(%zu)", sid);
501 rc = block_init(sid);
502 if (rc != EOK) {
503 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed opening block device %s.",
504 disk->svc_name);
505 rc = EIO;
506 goto error;
507 }
508
509 rc = block_get_bsize(sid, &block_size);
510 if (rc != EOK) {
511 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting block size of %s.",
512 disk->svc_name);
513 rc = EIO;
514 goto error;
515 }
516
517 rc = block_get_nblocks(sid, &nblocks);
518 if (rc != EOK) {
519 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting number of "
520 "blocks of %s.", disk->svc_name);
521 rc = EIO;
522 goto error;
523 }
524
525 block_inited = true;
526
527 lbd.ops = &vbds_label_bd_ops;
528 lbd.arg = (void *) disk;
529
530 rc = label_open(&lbd, &label);
531 if (rc != EOK) {
532 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to open label in disk %s.",
533 disk->svc_name);
534 rc = EIO;
535 goto error;
536 }
537
538 disk->label = label;
539 disk->block_size = block_size;
540 disk->nblocks = nblocks;
541 disk->present = true;
542
543 list_initialize(&disk->parts);
544 list_append(&disk->ldisks, &vbds_disks);
545
546 log_msg(LOG_DEFAULT, LVL_NOTE, "Recognized disk label. Adding partitions.");
547
548 (void) vbds_disk_parts_add(disk, label);
549 return EOK;
550error:
551 label_close(label);
552 if (block_inited) {
553 log_msg(LOG_DEFAULT, LVL_DEBUG, "block_fini(%zu)", sid);
554 block_fini(sid);
555 }
556 if (disk != NULL)
557 free(disk->svc_name);
558 free(disk);
559 return rc;
560}
561
562errno_t vbds_disk_remove(service_id_t sid)
563{
564 vbds_disk_t *disk;
565 errno_t rc;
566
567 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_remove(%zu)", sid);
568
569 rc = vbds_disk_by_svcid(sid, &disk);
570 if (rc != EOK)
571 return rc;
572
573 rc = vbds_disk_parts_remove(disk, vrf_force);
574 if (rc != EOK) {
575 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed removing disk.");
576 return rc;
577 }
578
579 list_remove(&disk->ldisks);
580 label_close(disk->label);
581 log_msg(LOG_DEFAULT, LVL_DEBUG, "block_fini(%zu)", sid);
582 block_fini(sid);
583 free(disk->svc_name);
584 free(disk);
585 return EOK;
586}
587
588/** Get list of disks as array of service IDs. */
589errno_t vbds_disk_get_ids(service_id_t *id_buf, size_t buf_size, size_t *act_size)
590{
591 size_t act_cnt;
592 size_t buf_cnt;
593
594 fibril_mutex_lock(&vbds_disks_lock);
595
596 buf_cnt = buf_size / sizeof(service_id_t);
597
598 act_cnt = list_count(&vbds_disks);
599 *act_size = act_cnt * sizeof(service_id_t);
600
601 if (buf_size % sizeof(service_id_t) != 0) {
602 fibril_mutex_unlock(&vbds_disks_lock);
603 return EINVAL;
604 }
605
606 size_t pos = 0;
607 list_foreach(vbds_disks, ldisks, vbds_disk_t, disk) {
608 if (pos < buf_cnt)
609 id_buf[pos] = disk->svc_id;
610 pos++;
611 }
612
613 fibril_mutex_unlock(&vbds_disks_lock);
614 return EOK;
615}
616
617errno_t vbds_disk_info(service_id_t sid, vbd_disk_info_t *info)
618{
619 vbds_disk_t *disk;
620 label_info_t linfo;
621 errno_t rc;
622
623 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_info(%zu)", sid);
624
625 rc = vbds_disk_by_svcid(sid, &disk);
626 if (rc != EOK)
627 return rc;
628
629 rc = label_get_info(disk->label, &linfo);
630 if (rc != EOK)
631 return rc;
632
633 info->ltype = linfo.ltype;
634 info->flags = linfo.flags;
635 info->ablock0 = linfo.ablock0;
636 info->anblocks = linfo.anblocks;
637 info->block_size = disk->block_size;
638 info->nblocks = disk->nblocks;
639 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_info - block_size=%zu",
640 info->block_size);
641 return EOK;
642}
643
644errno_t vbds_get_parts(service_id_t sid, service_id_t *id_buf, size_t buf_size,
645 size_t *act_size)
646{
647 vbds_disk_t *disk;
648 size_t act_cnt;
649 size_t buf_cnt;
650 errno_t rc;
651
652 rc = vbds_disk_by_svcid(sid, &disk);
653 if (rc != EOK)
654 return rc;
655
656 buf_cnt = buf_size / sizeof(service_id_t);
657
658 act_cnt = list_count(&disk->parts);
659 *act_size = act_cnt * sizeof(service_id_t);
660
661 if (buf_size % sizeof(service_id_t) != 0)
662 return EINVAL;
663
664 size_t pos = 0;
665 list_foreach(disk->parts, ldisk, vbds_part_t, part) {
666 if (pos < buf_cnt)
667 id_buf[pos] = part->pid;
668 pos++;
669 }
670
671 return EOK;
672}
673
674errno_t vbds_label_create(service_id_t sid, label_type_t ltype)
675{
676 label_t *label;
677 label_bd_t lbd;
678 label_info_t linfo;
679 vbds_disk_t *disk;
680 errno_t rc, rc2;
681
682 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu)", sid);
683
684 /* Find disk */
685 rc = vbds_disk_by_svcid(sid, &disk);
686 if (rc != EOK)
687 return rc;
688
689 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - label_close", sid);
690
691 /* Verify that current label is a dummy label */
692 rc = label_get_info(disk->label, &linfo);
693 if (rc != EOK)
694 return rc;
695
696 if (linfo.ltype != lt_none) {
697 log_msg(LOG_DEFAULT, LVL_ERROR, "Label already exists.");
698 return EEXIST;
699 }
700
701 /* Close dummy label first */
702 rc = vbds_disk_parts_remove(disk, vrf_none);
703 if (rc != EOK) {
704 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed removing disk.");
705 return rc;
706 }
707
708 label_close(disk->label);
709 disk->label = NULL;
710
711 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - label_create", sid);
712
713 lbd.ops = &vbds_label_bd_ops;
714 lbd.arg = (void *) disk;
715
716 rc = label_create(&lbd, ltype, &label);
717 if (rc != EOK)
718 goto error;
719
720 (void) vbds_disk_parts_add(disk, label);
721 disk->label = label;
722
723 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - success", sid);
724 return EOK;
725error:
726 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - failure", sid);
727 if (disk->label == NULL) {
728 lbd.ops = &vbds_label_bd_ops;
729 lbd.arg = (void *) disk;
730
731 rc2 = label_open(&lbd, &label);
732 if (rc2 != EOK) {
733 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to open label in disk %s.",
734 disk->svc_name);
735 return rc2;
736 }
737
738 disk->label = label;
739 (void) vbds_disk_parts_add(disk, label);
740 }
741
742 return rc;
743}
744
745errno_t vbds_label_delete(service_id_t sid)
746{
747 vbds_disk_t *disk;
748 label_t *label;
749 label_bd_t lbd;
750 errno_t rc;
751
752 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_delete(%zu)", sid);
753
754 rc = vbds_disk_by_svcid(sid, &disk);
755 if (rc != EOK)
756 return rc;
757
758 rc = vbds_disk_parts_remove(disk, vrf_none);
759 if (rc != EOK) {
760 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting label.");
761 return rc;
762 }
763
764 rc = label_destroy(disk->label);
765 if (rc != EOK) {
766 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting label.");
767 return rc;
768 }
769
770 disk->label = NULL;
771
772 lbd.ops = &vbds_label_bd_ops;
773 lbd.arg = (void *) disk;
774
775 rc = label_open(&lbd, &label);
776 if (rc != EOK) {
777 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to open label in disk %s.",
778 disk->svc_name);
779 return EIO;
780 }
781
782 (void) vbds_disk_parts_add(disk, label);
783 disk->label = label;
784 return EOK;
785}
786
787errno_t vbds_part_get_info(vbds_part_id_t partid, vbd_part_info_t *pinfo)
788{
789 vbds_part_t *part;
790 label_part_info_t lpinfo;
791 errno_t rc;
792
793 rc = vbds_part_by_pid(partid, &part);
794 if (rc != EOK)
795 return rc;
796
797 fibril_rwlock_read_lock(&part->lock);
798 if (part->lpart == NULL) {
799 fibril_rwlock_read_unlock(&part->lock);
800 return ENOENT;
801 }
802
803 label_part_get_info(part->lpart, &lpinfo);
804
805 pinfo->index = lpinfo.index;
806 pinfo->pkind = lpinfo.pkind;
807 pinfo->block0 = lpinfo.block0;
808 pinfo->nblocks = lpinfo.nblocks;
809 pinfo->svc_id = part->svc_id;
810 vbds_part_del_ref(part);
811 fibril_rwlock_read_unlock(&part->lock);
812
813 return EOK;
814}
815
816errno_t vbds_part_create(service_id_t sid, vbd_part_spec_t *pspec,
817 vbds_part_id_t *rpart)
818{
819 vbds_disk_t *disk;
820 vbds_part_t *part;
821 label_part_spec_t lpspec;
822 label_part_t *lpart;
823 errno_t rc;
824
825 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_create(%zu)", sid);
826
827 rc = vbds_disk_by_svcid(sid, &disk);
828 if (rc != EOK) {
829 log_msg(LOG_DEFAULT, LVL_ERROR, "Disk %zu not found",
830 sid);
831 goto error;
832 }
833
834 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_crate(%zu): "
835 "index=%d block0=%" PRIu64 " nblocks=%" PRIu64
836 " hdr_blocks=%" PRIu64 " pkind=%d",
837 sid, pspec->index, pspec->block0, pspec->nblocks, pspec->hdr_blocks,
838 pspec->pkind);
839
840 label_pspec_init(&lpspec);
841 lpspec.index = pspec->index;
842 lpspec.block0 = pspec->block0;
843 lpspec.nblocks = pspec->nblocks;
844 lpspec.hdr_blocks = pspec->hdr_blocks;
845 lpspec.pkind = pspec->pkind;
846 lpspec.ptype = pspec->ptype;
847
848 rc = label_part_create(disk->label, &lpspec, &lpart);
849 if (rc != EOK) {
850 log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating partition.");
851 goto error;
852 }
853
854 rc = label_part_empty(lpart);
855 if (rc != EOK) {
856 log_msg(LOG_DEFAULT, LVL_ERROR, "Error emptying partition.");
857 goto error;
858 }
859
860 rc = vbds_part_indices_update(disk);
861 if (rc != EOK) {
862 log_msg(LOG_DEFAULT, LVL_ERROR, "Error updating partition indices");
863 goto error;
864 }
865
866 rc = vbds_part_add(disk, lpart, &part);
867 if (rc != EOK) {
868 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed while creating "
869 "partition.");
870 rc = label_part_destroy(lpart);
871 if (rc != EOK)
872 log_msg(LOG_DEFAULT, LVL_ERROR,
873 "Cannot roll back partition creation.");
874 rc = EIO;
875 goto error;
876 }
877
878 if (rpart != NULL)
879 *rpart = part->pid;
880 return EOK;
881error:
882 return rc;
883}
884
885errno_t vbds_part_delete(vbds_part_id_t partid)
886{
887 vbds_part_t *part;
888 vbds_disk_t *disk;
889 label_part_t *lpart;
890 errno_t rc;
891
892 rc = vbds_part_by_pid(partid, &part);
893 if (rc != EOK)
894 return rc;
895
896 disk = part->disk;
897
898 rc = vbds_part_remove(part, vrf_none, &lpart);
899 if (rc != EOK)
900 return rc;
901
902 rc = label_part_destroy(lpart);
903 vbds_part_del_ref(part);
904 if (rc != EOK) {
905 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting partition");
906
907 /* Try rolling back */
908 rc = vbds_part_add(disk, lpart, NULL);
909 if (rc != EOK)
910 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed rolling back.");
911
912 return EIO;
913 }
914
915 rc = vbds_part_indices_update(disk);
916 if (rc != EOK) {
917 log_msg(LOG_DEFAULT, LVL_ERROR, "Error updating partition indices");
918 return EIO;
919 }
920
921 return EOK;
922}
923
924errno_t vbds_suggest_ptype(service_id_t sid, label_pcnt_t pcnt,
925 label_ptype_t *ptype)
926{
927 vbds_disk_t *disk;
928 errno_t rc;
929
930 rc = vbds_disk_by_svcid(sid, &disk);
931 if (rc != EOK) {
932 log_msg(LOG_DEFAULT, LVL_DEBUG, "Disk %zu not found",
933 sid);
934 goto error;
935 }
936
937 rc = label_suggest_ptype(disk->label, pcnt, ptype);
938 if (rc != EOK) {
939 log_msg(LOG_DEFAULT, LVL_DEBUG, "label_suggest_ptype() failed");
940 goto error;
941 }
942
943 return EOK;
944error:
945 return rc;
946}
947
948static errno_t vbds_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
949{
950 vbds_part_t *part = bd_srv_part(bd);
951
952 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_open()");
953 fibril_rwlock_write_lock(&part->lock);
954 part->open_cnt++;
955 fibril_rwlock_write_unlock(&part->lock);
956 return EOK;
957}
958
959static errno_t vbds_bd_close(bd_srv_t *bd)
960{
961 vbds_part_t *part = bd_srv_part(bd);
962
963 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_close()");
964
965 /* Grabbing writer lock also forces all I/O to complete */
966
967 fibril_rwlock_write_lock(&part->lock);
968 part->open_cnt--;
969 fibril_rwlock_write_unlock(&part->lock);
970 return EOK;
971}
972
973static errno_t vbds_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
974 void *buf, size_t size)
975{
976 vbds_part_t *part = bd_srv_part(bd);
977 aoff64_t gba;
978 errno_t rc;
979
980 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_read_blocks()");
981 fibril_rwlock_read_lock(&part->lock);
982
983 if (cnt * part->disk->block_size < size) {
984 fibril_rwlock_read_unlock(&part->lock);
985 return EINVAL;
986 }
987
988 if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) {
989 fibril_rwlock_read_unlock(&part->lock);
990 return ELIMIT;
991 }
992
993 rc = block_read_direct(part->disk->svc_id, gba, cnt, buf);
994 fibril_rwlock_read_unlock(&part->lock);
995
996 return rc;
997}
998
999static errno_t vbds_bd_sync_cache(bd_srv_t *bd, aoff64_t ba, size_t cnt)
1000{
1001 vbds_part_t *part = bd_srv_part(bd);
1002 aoff64_t gba;
1003 errno_t rc;
1004
1005 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_sync_cache()");
1006 fibril_rwlock_read_lock(&part->lock);
1007
1008 /* XXX Allow full-disk sync? */
1009 if (ba != 0 || cnt != 0) {
1010 if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) {
1011 fibril_rwlock_read_unlock(&part->lock);
1012 return ELIMIT;
1013 }
1014 } else {
1015 gba = 0;
1016 }
1017
1018 rc = block_sync_cache(part->disk->svc_id, gba, cnt);
1019 fibril_rwlock_read_unlock(&part->lock);
1020 return rc;
1021}
1022
1023static errno_t vbds_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
1024 const void *buf, size_t size)
1025{
1026 vbds_part_t *part = bd_srv_part(bd);
1027 aoff64_t gba;
1028 errno_t rc;
1029
1030 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_write_blocks()");
1031 fibril_rwlock_read_lock(&part->lock);
1032
1033 if (cnt * part->disk->block_size < size) {
1034 fibril_rwlock_read_unlock(&part->lock);
1035 return EINVAL;
1036 }
1037
1038 if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) {
1039 fibril_rwlock_read_unlock(&part->lock);
1040 return ELIMIT;
1041 }
1042
1043 rc = block_write_direct(part->disk->svc_id, gba, cnt, buf);
1044 fibril_rwlock_read_unlock(&part->lock);
1045 return rc;
1046}
1047
1048static errno_t vbds_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
1049{
1050 vbds_part_t *part = bd_srv_part(bd);
1051
1052 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_get_block_size()");
1053
1054 fibril_rwlock_read_lock(&part->lock);
1055 *rsize = part->disk->block_size;
1056 fibril_rwlock_read_unlock(&part->lock);
1057
1058 return EOK;
1059}
1060
1061static errno_t vbds_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
1062{
1063 vbds_part_t *part = bd_srv_part(bd);
1064
1065 log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_get_num_blocks()");
1066
1067 fibril_rwlock_read_lock(&part->lock);
1068 *rnb = part->nblocks;
1069 fibril_rwlock_read_unlock(&part->lock);
1070
1071 return EOK;
1072}
1073
1074void vbds_bd_conn(ipc_call_t *icall, void *arg)
1075{
1076 vbds_part_t *part;
1077 errno_t rc;
1078 service_id_t svcid;
1079
1080 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn()");
1081
1082 svcid = ipc_get_arg2(icall);
1083
1084 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn() - svcid=%zu", svcid);
1085
1086 rc = vbds_part_by_svcid(svcid, &part);
1087 if (rc != EOK) {
1088 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbd_bd_conn() - partition "
1089 "not found.");
1090 async_answer_0(icall, EINVAL);
1091 return;
1092 }
1093
1094 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn() - call bd_conn");
1095 bd_conn(icall, &part->bds);
1096 vbds_part_del_ref(part);
1097}
1098
1099/** Translate block segment address with range checking. */
1100static errno_t vbds_bsa_translate(vbds_part_t *part, aoff64_t ba, size_t cnt,
1101 aoff64_t *gba)
1102{
1103 if (ba + cnt > part->nblocks)
1104 return ELIMIT;
1105
1106 *gba = part->block0 + ba;
1107 return EOK;
1108}
1109
1110/** Register service for partition */
1111static errno_t vbds_part_svc_register(vbds_part_t *part)
1112{
1113 char *name;
1114 service_id_t psid;
1115 int idx;
1116 errno_t rc;
1117
1118 idx = part->lpart->index;
1119
1120 if (asprintf(&name, "%sp%u", part->disk->svc_name, idx) < 0) {
1121 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
1122 return ENOMEM;
1123 }
1124
1125 log_msg(LOG_DEFAULT, LVL_DEBUG, "loc_service_register('%s')",
1126 name);
1127 rc = loc_service_register(vbds_srv, name, &psid);
1128 if (rc != EOK) {
1129 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering "
1130 "service %s: %s.", name, str_error(rc));
1131 free(name);
1132 free(part);
1133 return EIO;
1134 }
1135
1136 rc = loc_service_add_to_cat(vbds_srv, psid, part_cid);
1137 if (rc != EOK) {
1138 log_msg(LOG_DEFAULT, LVL_ERROR, "Failled adding partition "
1139 "service %s to partition category.", name);
1140 free(name);
1141 free(part);
1142
1143 rc = loc_service_unregister(vbds_srv, psid);
1144 if (rc != EOK) {
1145 log_msg(LOG_DEFAULT, LVL_ERROR, "Error unregistering "
1146 "service. Rollback failed.");
1147 }
1148 return EIO;
1149 }
1150
1151 free(name);
1152 part->svc_id = psid;
1153 part->reg_idx = idx;
1154 return EOK;
1155}
1156
1157/** Unregister service for partition */
1158static errno_t vbds_part_svc_unregister(vbds_part_t *part)
1159{
1160 errno_t rc;
1161
1162 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_svc_unregister("
1163 "disk->svc_name='%s', id=%zu)", part->disk->svc_name, part->svc_id);
1164
1165 rc = loc_service_unregister(vbds_srv, part->svc_id);
1166 if (rc != EOK)
1167 return EIO;
1168
1169 part->svc_id = 0;
1170 part->reg_idx = 0;
1171 return EOK;
1172}
1173
1174/** Update service names for any partition whose index has changed. */
1175static errno_t vbds_part_indices_update(vbds_disk_t *disk)
1176{
1177 label_part_info_t lpinfo;
1178 errno_t rc;
1179
1180 log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_indices_update()");
1181
1182 fibril_mutex_lock(&vbds_parts_lock);
1183
1184 /* First unregister services for partitions whose index has changed */
1185 list_foreach(disk->parts, ldisk, vbds_part_t, part) {
1186 fibril_rwlock_write_lock(&part->lock);
1187 if (part->svc_id != 0 && part->lpart->index != part->reg_idx) {
1188 rc = vbds_part_svc_unregister(part);
1189 if (rc != EOK) {
1190 fibril_rwlock_write_unlock(&part->lock);
1191 fibril_mutex_unlock(&vbds_parts_lock);
1192 log_msg(LOG_DEFAULT, LVL_ERROR, "Error "
1193 "un-registering partition.");
1194 return EIO;
1195 }
1196 }
1197
1198 fibril_rwlock_write_unlock(&part->lock);
1199 }
1200
1201 /* Now re-register those services under the new indices */
1202 list_foreach(disk->parts, ldisk, vbds_part_t, part) {
1203 fibril_rwlock_write_lock(&part->lock);
1204 label_part_get_info(part->lpart, &lpinfo);
1205 if (part->svc_id == 0 && lpinfo.pkind != lpk_extended) {
1206 rc = vbds_part_svc_register(part);
1207 if (rc != EOK) {
1208 fibril_rwlock_write_unlock(&part->lock);
1209 fibril_mutex_unlock(&vbds_parts_lock);
1210 log_msg(LOG_DEFAULT, LVL_ERROR, "Error "
1211 "re-registering partition.");
1212 return EIO;
1213 }
1214 }
1215
1216 fibril_rwlock_write_unlock(&part->lock);
1217 }
1218
1219 fibril_mutex_unlock(&vbds_parts_lock);
1220
1221 return EOK;
1222}
1223
1224/** Get block size wrapper for liblabel */
1225static errno_t vbds_label_get_bsize(void *arg, size_t *bsize)
1226{
1227 vbds_disk_t *disk = (vbds_disk_t *)arg;
1228 return block_get_bsize(disk->svc_id, bsize);
1229}
1230
1231/** Get number of blocks wrapper for liblabel */
1232static errno_t vbds_label_get_nblocks(void *arg, aoff64_t *nblocks)
1233{
1234 vbds_disk_t *disk = (vbds_disk_t *)arg;
1235 return block_get_nblocks(disk->svc_id, nblocks);
1236}
1237
1238/** Read blocks wrapper for liblabel */
1239static errno_t vbds_label_read(void *arg, aoff64_t ba, size_t cnt, void *buf)
1240{
1241 vbds_disk_t *disk = (vbds_disk_t *)arg;
1242 return block_read_direct(disk->svc_id, ba, cnt, buf);
1243}
1244
1245/** Write blocks wrapper for liblabel */
1246static errno_t vbds_label_write(void *arg, aoff64_t ba, size_t cnt, const void *data)
1247{
1248 vbds_disk_t *disk = (vbds_disk_t *)arg;
1249 return block_write_direct(disk->svc_id, ba, cnt, data);
1250}
1251
1252/** @}
1253 */
Note: See TracBrowser for help on using the repository browser.