Index: uspace/srv/bd/hr/hr.c
===================================================================
--- uspace/srv/bd/hr/hr.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/hr.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -146,9 +146,9 @@
 		goto error;
 
-	rc = hr_metadata_save(new_volume);
-	if (rc != EOK)
-		goto error;
-
 	rc = new_volume->hr_ops.create(new_volume);
+	if (rc != EOK)
+		goto error;
+
+	rc = hr_metadata_save(new_volume, WITH_STATE_CALLBACK);
 	if (rc != EOK)
 		goto error;
Index: uspace/srv/bd/hr/io.c
===================================================================
--- uspace/srv/bd/hr/io.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/io.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -45,9 +45,37 @@
 #include "var.h"
 
+static errno_t exec_io_op(hr_io_t *);
+
 errno_t hr_io_worker(void *arg)
 {
 	hr_io_t *io = arg;
+
+	errno_t rc = exec_io_op(io);
+
+	/*
+	 * We don't have to invalidate extents who got ENOMEM
+	 * on READ/SYNC. But when we get ENOMEM on a WRITE, we have
+	 * to invalidate it, because there could have been
+	 * other writes, there is no way to rollback.
+	 */
+	if (rc != EOK && (rc != ENOMEM || io->type == HR_BD_WRITE))
+		io->vol->state_callback(io->vol, io->extent, rc);
+
+	return rc;
+}
+
+errno_t hr_io_worker_basic(void *arg)
+{
+	hr_io_t *io = arg;
+
+	errno_t rc = exec_io_op(io);
+
+	return rc;
+}
+
+static errno_t exec_io_op(hr_io_t *io)
+{
+	size_t ext_idx = io->extent;
 	hr_extent_t *extents = (hr_extent_t *)&io->vol->extents;
-	size_t ext_idx = io->extent;
 	errno_t rc;
 
@@ -90,13 +118,4 @@
 	HR_DEBUG("WORKER (%p) rc: %s\n", io, str_error(rc));
 
-	/*
-	 * We don't have to invalidate extents who got ENOMEM
-	 * on READ/SYNC. But when we get ENOMEM on a WRITE, we have
-	 * to invalidate it, because there could have been
-	 * other writes, there is no way to rollback.
-	 */
-	if (rc != EOK && (rc != ENOMEM || io->type == HR_BD_WRITE))
-		io->state_callback(io->vol, io->extent, rc);
-
 	return rc;
 }
Index: uspace/srv/bd/hr/io.h
===================================================================
--- uspace/srv/bd/hr/io.h	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/io.h	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -47,8 +47,8 @@
 	const void	*data_write;
 	hr_volume_t	*vol;
-	void		(*state_callback)(hr_volume_t *, size_t, errno_t);
 } hr_io_t;
 
 errno_t hr_io_worker(void *);
+errno_t hr_io_worker_basic(void *);
 
 #endif
Index: uspace/srv/bd/hr/raid0.c
===================================================================
--- uspace/srv/bd/hr/raid0.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/raid0.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -54,5 +54,5 @@
 
 static void	hr_raid0_update_vol_status(hr_volume_t *);
-static void	raid0_state_callback(hr_volume_t *, size_t, errno_t);
+static void	hr_raid0_state_callback(hr_volume_t *, size_t, errno_t);
 static errno_t	hr_raid0_bd_op(hr_bd_op_type_t, bd_srv_t *, aoff64_t, size_t,
     void *, const void *, size_t);
@@ -100,4 +100,6 @@
 	new_volume->hr_bds.sarg = new_volume;
 
+	new_volume->state_callback = hr_raid0_state_callback;
+
 	return EOK;
 }
@@ -196,4 +198,11 @@
 static void hr_raid0_update_vol_status(hr_volume_t *vol)
 {
+	fibril_mutex_lock(&vol->md_lock);
+
+	/* XXX: will be wrapped in md specific fcn ptrs */
+	vol->in_mem_md->counter++;
+
+	fibril_mutex_unlock(&vol->md_lock);
+
 	fibril_rwlock_read_lock(&vol->states_lock);
 
@@ -221,5 +230,5 @@
 }
 
-static void raid0_state_callback(hr_volume_t *vol, size_t extent, errno_t rc)
+static void hr_raid0_state_callback(hr_volume_t *vol, size_t extent, errno_t rc)
 {
 	if (rc == EOK)
@@ -272,5 +281,4 @@
 			io->type = type;
 			io->vol = vol;
-			io->state_callback = raid0_state_callback;
 
 			hr_fgroup_submit(group, hr_io_worker, io);
@@ -326,5 +334,4 @@
 		io->type = type;
 		io->vol = vol;
-		io->state_callback = raid0_state_callback;
 
 		hr_fgroup_submit(group, hr_io_worker, io);
Index: uspace/srv/bd/hr/raid1.c
===================================================================
--- uspace/srv/bd/hr/raid1.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/raid1.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -105,4 +105,6 @@
 	new_volume->hr_bds.sarg = new_volume;
 
+	new_volume->state_callback = hr_raid1_ext_state_callback;
+
 	/* force volume state update */
 	hr_mark_vol_state_dirty(new_volume);
@@ -222,4 +224,11 @@
 	if (!atomic_compare_exchange_strong(&vol->state_dirty, &exp, false))
 		return;
+
+	fibril_mutex_lock(&vol->md_lock);
+
+	/* XXX: will be wrapped in md specific fcn ptrs */
+	vol->in_mem_md->counter++;
+
+	fibril_mutex_unlock(&vol->md_lock);
 
 	fibril_rwlock_read_lock(&vol->extents_lock);
@@ -247,4 +256,5 @@
 
 		if (old_state != HR_VOL_REBUILD) {
+			/* XXX: allow REBUILD on INVALID extents */
 			if (vol->hotspare_no > 0) {
 				fid_t fib = fibril_create(hr_raid1_rebuild,
@@ -427,5 +437,4 @@
 			io->type = type;
 			io->vol = vol;
-			io->state_callback = hr_raid1_ext_state_callback;
 
 			hr_fgroup_submit(group, hr_io_worker, io);
@@ -541,5 +550,5 @@
 	fibril_rwlock_write_unlock(&vol->states_lock);
 
-	rc = hr_metadata_save(vol);
+	rc = hr_metadata_save(vol, WITH_STATE_CALLBACK);
 
 end:
@@ -576,4 +585,5 @@
 	fibril_mutex_lock(&vol->hotspare_lock);
 
+	/* XXX: allow REBUILD on INVALID extents */
 	if (vol->hotspare_no == 0) {
 		HR_WARN("hr_raid1_rebuild(): no free hotspares on \"%s\", "
Index: uspace/srv/bd/hr/raid5.c
===================================================================
--- uspace/srv/bd/hr/raid5.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/raid5.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -845,5 +845,5 @@
 	hr_update_ext_status(vol, bad, HR_EXT_ONLINE);
 
-	rc = hr_metadata_save(vol);
+	rc = hr_metadata_save(vol, WITH_STATE_CALLBACK);
 
 end:
Index: uspace/srv/bd/hr/superblock.c
===================================================================
--- uspace/srv/bd/hr/superblock.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/superblock.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -88,9 +88,9 @@
 
 /*
- * TODO: think about thread safety, if hr_metadata_save() can
- * be called from multiple threads, and if, maybe will need
- * md_lock... or whatever, but dont want to stall I/Os...
- */
-errno_t hr_metadata_save(hr_volume_t *vol)
+ * XXX: finish this fcn documentation
+ *
+ * Returns ENOMEM else EOK
+ */
+errno_t hr_metadata_save(hr_volume_t *vol, bool with_state_callback)
 {
 	HR_DEBUG("%s()", __func__);
@@ -102,6 +102,12 @@
 		return ENOMEM;
 
+	fibril_rwlock_read_lock(&vol->extents_lock);
+
+	fibril_mutex_lock(&vol->md_lock);
+
 	for (size_t i = 0; i < vol->extent_no; i++) {
 		hr_extent_t *ext = &vol->extents[i];
+
+		fibril_rwlock_read_lock(&vol->states_lock);
 
 		/* TODO: special case for REBUILD */
@@ -109,21 +115,22 @@
 			continue;
 
+		fibril_rwlock_read_unlock(&vol->states_lock);
+
 		vol->in_mem_md->index = i;
 		hr_encode_metadata_to_block(vol->in_mem_md, md_block);
 		rc = hr_write_metadata_block(ext->svc_id, md_block);
-		/*
-		 * XXX: here maybe call vol status event or the state
-		 * callback inside, same with read_block...
-		 *
-		 * also think about using FGE here... maybe a bit more
-		 * code, but faster and gratis state callback :-)
-		 */
-		if (rc != EOK)
-			goto error;
+		if (with_state_callback && rc != EOK)
+			vol->state_callback(vol, i, rc);
 	}
 
-error:
+	fibril_mutex_unlock(&vol->md_lock);
+
+	fibril_rwlock_read_unlock(&vol->extents_lock);
+
+	if (with_state_callback)
+		vol->hr_ops.status_event(vol);
+
 	free(md_block);
-	return rc;
+	return EOK;
 }
 
@@ -161,9 +168,4 @@
 
 	rc = block_write_direct(dev, blkno - 1, HR_META_SIZE, block);
-	/*
-	 * XXX: here maybe call vol status event or the state callback...
-	 *
-	 * but need to pass vol pointer
-	 */
 
 	return rc;
Index: uspace/srv/bd/hr/superblock.h
===================================================================
--- uspace/srv/bd/hr/superblock.h	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/superblock.h	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -80,5 +80,5 @@
 
 extern errno_t	hr_metadata_init(hr_volume_t *, hr_metadata_t *);
-extern errno_t	hr_metadata_save(hr_volume_t *);
+extern errno_t	hr_metadata_save(hr_volume_t *, bool);
 extern errno_t	hr_write_metadata_block(service_id_t, const void *);
 extern errno_t	hr_get_metadata_block(service_id_t, void **);
Index: uspace/srv/bd/hr/util.c
===================================================================
--- uspace/srv/bd/hr/util.c	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/util.c	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -145,4 +145,6 @@
 	fibril_mutex_initialize(&vol->lock); /* XXX: will remove this */
 
+	fibril_mutex_initialize(&vol->md_lock);
+
 	fibril_rwlock_initialize(&vol->extents_lock);
 	fibril_rwlock_initialize(&vol->states_lock);
@@ -219,4 +221,6 @@
 			fibril_rwlock_write_unlock(&hr_volumes_lock);
 
+			hr_metadata_save(vol, NO_STATE_CALLBACK);
+
 			hr_destroy_vol_struct(vol);
 
@@ -843,9 +847,9 @@
 	vol->in_mem_md->counter++;
 
-	hr_metadata_save(vol);
-
 	rc = vol->hr_ops.create(vol);
 	if (rc != EOK)
 		goto error;
+
+	hr_metadata_save(vol, WITH_STATE_CALLBACK);
 
 	fibril_rwlock_write_lock(&hr_volumes_lock);
Index: uspace/srv/bd/hr/var.h
===================================================================
--- uspace/srv/bd/hr/var.h	(revision ca7fa5bf714d68850bec6e4959902ff61d0ccfbe)
+++ uspace/srv/bd/hr/var.h	(revision 800d18892aaa4584df1fb5587aaa20c4f2325a69)
@@ -83,4 +83,5 @@
 
 	hr_metadata_t	*in_mem_md;
+	fibril_mutex_t	 md_lock;		/* lock protecting in_mem_md */
 
 	/* invariants */
@@ -111,4 +112,5 @@
 	_Atomic int	 open_cnt;		/* open/close() counter */
 	hr_vol_status_t	 status;		/* volume status */
+	void		 (*state_callback)(hr_volume_t *, size_t, errno_t);
 } hr_volume_t;
 
@@ -118,4 +120,8 @@
 	HR_BD_WRITE
 } hr_bd_op_type_t;
+
+/* macros for hr_metadata_save() */
+#define	WITH_STATE_CALLBACK true
+#define	NO_STATE_CALLBACK false
 
 typedef struct hr_range_lock {
