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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 09ab0a9a was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

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