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

Last change on this file was ca48672, checked in by Jiri Svoboda <jiri@…>, 5 days ago

loc_service_register() needs to take a port ID argument.

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