aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2012-10-17 08:47:07 -0700
committerBrian Behlendorf <[email protected]>2012-10-17 08:48:29 -0700
commit82f46731fd5a9eef4f87530e94922664b58a6138 (patch)
tree0ee40606a18abd4aed4c86bdde028a2a1a5cbd03
parent6cb7ab069d9079a5b4b955da883d5ab804c91319 (diff)
parenta298dbde92c68022a06ef51f2bb559b0975e4920 (diff)
Merge branch 'condvar'
Auditing the code to verify that all instances of cv_signal() and cv_broadcast() are called under the proper associated mutex turned up several races. None of these have been conclusively seen in the wild but the following patch set resolves them. For reference, from the cv_signal(9F) man page: cv_signal() signals the condition and wakes one blocked thread. All blocked threads can be unblocked by calling cv_broadcast(). You must acquire the mutex passed into cv_wait() before calling cv_signal() or cv_broadcast() Signed-off-by: Brian Behlendorf <[email protected]> Closes #1048
-rw-r--r--module/zfs/fm.c11
-rw-r--r--module/zfs/zfs_rlock.c9
-rw-r--r--module/zfs/zil.c3
3 files changed, 14 insertions, 9 deletions
diff --git a/module/zfs/fm.c b/module/zfs/fm.c
index ce0ebe0c1..c004032f8 100644
--- a/module/zfs/fm.c
+++ b/module/zfs/fm.c
@@ -481,14 +481,13 @@ zfs_zevent_drain_all(int *count)
static void
zfs_zevent_insert(zevent_t *ev)
{
- mutex_enter(&zevent_lock);
+ ASSERT(MUTEX_HELD(&zevent_lock));
list_insert_head(&zevent_list, ev);
+
if (zevent_len_cur >= zfs_zevent_len_max)
zfs_zevent_drain(list_tail(&zevent_list));
else
zevent_len_cur++;
-
- mutex_exit(&zevent_lock);
}
/*
@@ -528,8 +527,11 @@ zfs_zevent_post(nvlist_t *nvl, nvlist_t *detector, zevent_cb_t *cb)
ev->ev_nvl = nvl;
ev->ev_detector = detector;
ev->ev_cb = cb;
+
+ mutex_enter(&zevent_lock);
zfs_zevent_insert(ev);
cv_broadcast(&zevent_cv);
+ mutex_exit(&zevent_lock);
}
static int
@@ -1520,9 +1522,10 @@ fm_fini(void)
int count;
zfs_zevent_drain_all(&count);
- cv_broadcast(&zevent_cv);
mutex_enter(&zevent_lock);
+ cv_broadcast(&zevent_cv);
+
zevent_flags |= ZEVENT_SHUTDOWN;
while (zevent_waiters > 0) {
mutex_exit(&zevent_lock);
diff --git a/module/zfs/zfs_rlock.c b/module/zfs/zfs_rlock.c
index 208de10f3..c278035b1 100644
--- a/module/zfs/zfs_rlock.c
+++ b/module/zfs/zfs_rlock.c
@@ -486,7 +486,7 @@ zfs_range_unlock_reader(znode_t *zp, rl_t *remove, list_t *free_list)
*/
if (remove->r_cnt == 1) {
avl_remove(tree, remove);
- mutex_exit(&zp->z_range_lock);
+
if (remove->r_write_wanted)
cv_broadcast(&remove->r_wr_cv);
@@ -530,7 +530,6 @@ zfs_range_unlock_reader(znode_t *zp, rl_t *remove, list_t *free_list)
}
}
- mutex_exit(&zp->z_range_lock);
kmem_free(remove, sizeof (rl_t));
}
}
@@ -554,7 +553,6 @@ zfs_range_unlock(rl_t *rl)
if (rl->r_type == RL_WRITER) {
/* writer locks can't be shared or split */
avl_remove(&zp->z_range_avl, rl);
- mutex_exit(&zp->z_range_lock);
if (rl->r_write_wanted)
cv_broadcast(&rl->r_wr_cv);
@@ -569,6 +567,7 @@ zfs_range_unlock(rl_t *rl)
*/
zfs_range_unlock_reader(zp, rl, &free_list);
}
+ mutex_exit(&zp->z_range_lock);
while ((free_rl = list_head(&free_list)) != NULL) {
list_remove(&free_list, free_rl);
@@ -599,11 +598,13 @@ zfs_range_reduce(rl_t *rl, uint64_t off, uint64_t len)
mutex_enter(&zp->z_range_lock);
rl->r_off = off;
rl->r_len = len;
- mutex_exit(&zp->z_range_lock);
+
if (rl->r_write_wanted)
cv_broadcast(&rl->r_wr_cv);
if (rl->r_read_wanted)
cv_broadcast(&rl->r_rd_cv);
+
+ mutex_exit(&zp->z_range_lock);
}
/*
diff --git a/module/zfs/zil.c b/module/zfs/zil.c
index 292aea27d..e76e5ecf1 100644
--- a/module/zfs/zil.c
+++ b/module/zfs/zil.c
@@ -1560,13 +1560,14 @@ zil_commit(zilog_t *zilog, uint64_t foid)
zil_commit_writer(zilog);
zilog->zl_com_batch = mybatch;
zilog->zl_writer = B_FALSE;
- mutex_exit(&zilog->zl_lock);
/* wake up one thread to become the next writer */
cv_signal(&zilog->zl_cv_batch[(mybatch+1) & 1]);
/* wake up all threads waiting for this batch to be committed */
cv_broadcast(&zilog->zl_cv_batch[mybatch & 1]);
+
+ mutex_exit(&zilog->zl_lock);
}
/*