Index: uspace/srv/bd/vbd/disk.c
===================================================================
--- uspace/srv/bd/vbd/disk.c	(revision 3a9cf3536b79bfa327a5a14448447c2b19f029f9)
+++ uspace/srv/bd/vbd/disk.c	(revision e11c527196d4fd9ad3e9f116763b8eb9e0d3fcc0)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2015 Jiri Svoboda
+ * Copyright (c) 2016 Jiri Svoboda
  * All rights reserved.
  *
@@ -50,4 +50,5 @@
 static fibril_mutex_t vbds_disks_lock;
 static list_t vbds_disks; /* of vbds_disk_t */
+static fibril_mutex_t vbds_parts_lock;
 static list_t vbds_parts; /* of vbds_part_t */
 
@@ -55,5 +56,5 @@
 
 static int vbds_disk_parts_add(vbds_disk_t *, label_t *);
-static int vbds_disk_parts_remove(vbds_disk_t *);
+static int vbds_disk_parts_remove(vbds_disk_t *, vbds_rem_flag_t);
 
 static int vbds_bd_open(bd_srvs_t *, bd_srv_t *);
@@ -120,4 +121,5 @@
 	fibril_mutex_initialize(&vbds_disks_lock);
 	list_initialize(&vbds_disks);
+	fibril_mutex_initialize(&vbds_parts_lock);
 	list_initialize(&vbds_parts);
 
@@ -222,7 +224,24 @@
 }
 
+static void vbds_part_add_ref(vbds_part_t *part)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_part_add_ref");
+	atomic_inc(&part->refcnt);
+}
+
+static void vbds_part_del_ref(vbds_part_t *part)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_part_del_ref");
+	if (atomic_predec(&part->refcnt) == 0) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG2, " - free part");
+		free(part);
+	}
+}
+
 static int vbds_part_by_pid(vbds_part_id_t partid, vbds_part_t **rpart)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_by_pid(%zu)", partid);
+
+	fibril_mutex_lock(&vbds_parts_lock);
 
 	list_foreach(vbds_parts, lparts, vbds_part_t, part) {
@@ -230,4 +249,6 @@
 		if (part->pid == partid) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "Found match.");
+			vbds_part_add_ref(part);
+			fibril_mutex_unlock(&vbds_parts_lock);
 			*rpart = part;
 			return EOK;
@@ -235,4 +256,5 @@
 	}
 
+	fibril_mutex_unlock(&vbds_parts_lock);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "No match.");
 	return ENOENT;
@@ -242,4 +264,6 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_by_svcid(%zu)", svcid);
+
+	fibril_mutex_lock(&vbds_parts_lock);
 
 	list_foreach(vbds_parts, lparts, vbds_part_t, part) {
@@ -247,4 +271,6 @@
 		if (part->svc_id == svcid) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "Found match.");
+			vbds_part_add_ref(part);
+			fibril_mutex_unlock(&vbds_parts_lock);
 			*rpart = part;
 			return EOK;
@@ -252,4 +278,5 @@
 	}
 
+	fibril_mutex_unlock(&vbds_parts_lock);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "No match.");
 	return ENOENT;
@@ -275,4 +302,6 @@
 		return ENOMEM;
 	}
+
+	fibril_rwlock_initialize(&part->lock);
 
 	part->lpart = lpart;
@@ -282,4 +311,5 @@
 	part->block0 = lpinfo.block0;
 	part->nblocks = lpinfo.nblocks;
+	atomic_set(&part->refcnt, 1);
 
 	bd_srvs_init(&part->bds);
@@ -289,10 +319,14 @@
 	if (lpinfo.pkind != lpk_extended) {
 		rc = vbds_part_svc_register(part);
-		if (rc != EOK)
+		if (rc != EOK) {
+			free(part);
 			return EIO;
+		}
 	}
 
 	list_append(&part->ldisk, &disk->parts);
+	fibril_mutex_lock(&vbds_parts_lock);
 	list_append(&part->lparts, &vbds_parts);
+	fibril_mutex_unlock(&vbds_parts_lock);
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_add -> %p", part);
@@ -305,6 +339,11 @@
 /** Remove partition from our inventory leaving only the underlying liblabel
  * partition structure.
+ *
+ * @param part Partition
+ * @param flag If set to @c vrf_force, force removal even if partition is in use
+ * @param rlpart Place to store pointer to liblabel partition
  */
-static int vbds_part_remove(vbds_part_t *part, label_part_t **rlpart)
+static int vbds_part_remove(vbds_part_t *part, vbds_rem_flag_t flag,
+    label_part_t **rlpart)
 {
 	label_part_t *lpart;
@@ -313,7 +352,9 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_remove(%p)", part);
 
+	fibril_rwlock_write_lock(&part->lock);
 	lpart = part->lpart;
 
-	if (part->open_cnt > 0) {
+	if ((flag & vrf_force) == 0 && part->open_cnt > 0) {
+		fibril_rwlock_write_unlock(&part->lock);
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "part->open_cnt = %d",
 		    part->open_cnt);
@@ -323,11 +364,18 @@
 	if (part->svc_id != 0) {
 		rc = vbds_part_svc_unregister(part);
-		if (rc != EOK)
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&part->lock);
 			return EIO;
+		}
 	}
 
 	list_remove(&part->ldisk);
+	fibril_mutex_lock(&vbds_parts_lock);
 	list_remove(&part->lparts);
-	free(part);
+	fibril_mutex_unlock(&vbds_parts_lock);
+
+	vbds_part_del_ref(part);
+	part->lpart = NULL;
+	fibril_rwlock_write_unlock(&part->lock);
 
 	if (rlpart != NULL)
@@ -360,5 +408,5 @@
 /** Remove all disk partitions from our inventory leaving only the underlying
  * liblabel partition structures. */
-static int vbds_disk_parts_remove(vbds_disk_t *disk)
+static int vbds_disk_parts_remove(vbds_disk_t *disk, vbds_rem_flag_t flag)
 {
 	link_t *link;
@@ -369,5 +417,5 @@
 	while (link != NULL) {
 		part = list_get_instance(link, vbds_part_t, ldisk);
-		rc = vbds_part_remove(part, NULL);
+		rc = vbds_part_remove(part, flag, NULL);
 		if (rc != EOK)
 			return rc;
@@ -486,5 +534,5 @@
 		return rc;
 
-	rc = vbds_disk_parts_remove(disk);
+	rc = vbds_disk_parts_remove(disk, vrf_force);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed removing disk.");
@@ -612,5 +660,5 @@
 
 	/* Close dummy label first */
-	rc = vbds_disk_parts_remove(disk);
+	rc = vbds_disk_parts_remove(disk, vrf_none);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed removing disk.");
@@ -661,5 +709,5 @@
 		return rc;
 
-	rc = vbds_disk_parts_remove(disk);
+	rc = vbds_disk_parts_remove(disk, vrf_none);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting label.");
@@ -696,4 +744,10 @@
 	if (rc != EOK)
 		return rc;
+
+	fibril_rwlock_read_lock(&part->lock);
+	if (part->lpart == NULL) {
+		fibril_rwlock_read_unlock(&part->lock);
+		return ENOENT;
+	}
 
 	label_part_get_info(part->lpart, &lpinfo);
@@ -704,4 +758,7 @@
 	pinfo->nblocks = lpinfo.nblocks;
 	pinfo->svc_id = part->svc_id;
+	vbds_part_del_ref(part);
+	fibril_rwlock_read_unlock(&part->lock);
+
 	return EOK;
 }
@@ -783,9 +840,10 @@
 	disk = part->disk;
 
-	rc = vbds_part_remove(part, &lpart);
+	rc = vbds_part_remove(part, vrf_none, &lpart);
 	if (rc != EOK)
 		return rc;
 
 	rc = label_part_destroy(lpart);
+	vbds_part_del_ref(part);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting partition");
@@ -837,5 +895,7 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_open()");
+	fibril_rwlock_write_lock(&part->lock);
 	part->open_cnt++;
+	fibril_rwlock_write_unlock(&part->lock);
 	return EOK;
 }
@@ -846,5 +906,10 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_close()");
+
+	/* Grabbing writer lock also forces all I/O to complete */
+
+	fibril_rwlock_write_lock(&part->lock);
 	part->open_cnt--;
+	fibril_rwlock_write_unlock(&part->lock);
 	return EOK;
 }
@@ -855,14 +920,23 @@
 	vbds_part_t *part = bd_srv_part(bd);
 	aoff64_t gba;
+	int rc;
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_read_blocks()");
-
-	if (cnt * part->disk->block_size < size)
+	fibril_rwlock_read_lock(&part->lock);
+
+	if (cnt * part->disk->block_size < size) {
+		fibril_rwlock_read_unlock(&part->lock);
 		return EINVAL;
-
-	if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK)
+	}
+
+	if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) {
+		fibril_rwlock_read_unlock(&part->lock);
 		return ELIMIT;
-
-	return block_read_direct(part->disk->svc_id, gba, cnt, buf);
+	}
+
+	rc = block_read_direct(part->disk->svc_id, gba, cnt, buf);
+	fibril_rwlock_read_unlock(&part->lock);
+
+	return rc;
 }
 
@@ -871,16 +945,22 @@
 	vbds_part_t *part = bd_srv_part(bd);
 	aoff64_t gba;
+	int rc;
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_sync_cache()");
+	fibril_rwlock_read_lock(&part->lock);
 
 	/* XXX Allow full-disk sync? */
 	if (ba != 0 || cnt != 0) {
-		if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK)
+		if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) {
+			fibril_rwlock_read_unlock(&part->lock);
 			return ELIMIT;
+		}
 	} else {
 		gba = 0;
 	}
 
-	return block_sync_cache(part->disk->svc_id, gba, cnt);
+	rc = block_sync_cache(part->disk->svc_id, gba, cnt);
+	fibril_rwlock_read_unlock(&part->lock);
+	return rc;
 }
 
@@ -890,14 +970,22 @@
 	vbds_part_t *part = bd_srv_part(bd);
 	aoff64_t gba;
+	int rc;
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_write_blocks()");
-
-	if (cnt * part->disk->block_size < size)
+	fibril_rwlock_read_lock(&part->lock);
+
+	if (cnt * part->disk->block_size < size) {
+		fibril_rwlock_read_unlock(&part->lock);
 		return EINVAL;
-
-	if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK)
+	}
+
+	if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) {
+		fibril_rwlock_read_unlock(&part->lock);
 		return ELIMIT;
-
-	return block_write_direct(part->disk->svc_id, gba, cnt, buf);
+	}
+
+	rc = block_write_direct(part->disk->svc_id, gba, cnt, buf);
+	fibril_rwlock_read_unlock(&part->lock);
+	return rc;
 }
 
@@ -907,5 +995,9 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_get_block_size()");
+
+	fibril_rwlock_read_lock(&part->lock);
 	*rsize = part->disk->block_size;
+	fibril_rwlock_read_unlock(&part->lock);
+
 	return EOK;
 }
@@ -916,5 +1008,9 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_get_num_blocks()");
+
+	fibril_rwlock_read_lock(&part->lock);
 	*rnb = part->nblocks;
+	fibril_rwlock_read_unlock(&part->lock);
+
 	return EOK;
 }
@@ -942,4 +1038,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn() - call bd_conn");
 	bd_conn(iid, icall, &part->bds);
+	vbds_part_del_ref(part);
 }
 
@@ -1028,9 +1125,14 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_indices_update()");
 
+	fibril_mutex_lock(&vbds_parts_lock);
+
 	/* First unregister services for partitions whose index has changed */
 	list_foreach(disk->parts, ldisk, vbds_part_t, part) {
+		fibril_rwlock_write_lock(&part->lock);
 		if (part->svc_id != 0 && part->lpart->index != part->reg_idx) {
 			rc = vbds_part_svc_unregister(part);
 			if (rc != EOK) {
+				fibril_rwlock_write_unlock(&part->lock);
+				fibril_mutex_unlock(&vbds_parts_lock);
 				log_msg(LOG_DEFAULT, LVL_ERROR, "Error "
 				    "un-registering partition.");
@@ -1038,12 +1140,17 @@
 			}
 		}
+
+		fibril_rwlock_write_unlock(&part->lock);
 	}
 
 	/* Now re-register those services under the new indices */
 	list_foreach(disk->parts, ldisk, vbds_part_t, part) {
+		fibril_rwlock_write_lock(&part->lock);
 		label_part_get_info(part->lpart, &lpinfo);
 		if (part->svc_id == 0 && lpinfo.pkind != lpk_extended) {
 			rc = vbds_part_svc_register(part);
 			if (rc != EOK) {
+				fibril_rwlock_write_unlock(&part->lock);
+				fibril_mutex_unlock(&vbds_parts_lock);
 				log_msg(LOG_DEFAULT, LVL_ERROR, "Error "
 				    "re-registering partition.");
@@ -1051,5 +1158,9 @@
 			}
 		}
-	}
+
+		fibril_rwlock_write_unlock(&part->lock);
+	}
+
+	fibril_mutex_unlock(&vbds_parts_lock);
 
 	return EOK;
