summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2014-01-10 08:57:25 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-10 13:48:08 -0800
commit895a068a524e134900b9d98b519309b7aae7bbb1 (patch)
tree3989057a5801f0d72dd2640f70479f7593504f7f
parent99177a34110889a8f2c36420c34e3bcc9bfd8a70 (diff)
downloadlinux-895a068a524e134900b9d98b519309b7aae7bbb1.tar.gz
linux-895a068a524e134900b9d98b519309b7aae7bbb1.tar.bz2
linux-895a068a524e134900b9d98b519309b7aae7bbb1.zip
kernfs: make kernfs_get_active() block if the node is deactivated but not removed
Currently, kernfs_get_active() fails if the target node is deactivated. This is fine as a node always gets removed after deactivation; however, we're gonna add reactivation so the assumption won't hold. It'd be incorrect for kernfs_get_active() to fail for a node which was deactivated only temporarily. This patch makes kernfs_get_active() block if the node is deactivated but not removed. If the node gets reactivated (not yet implemented), it will be retried and succeed. If the node gets removed, it will be woken up and fail. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/kernfs/dir.c25
1 files changed, 21 insertions, 4 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 770d687ee9f3..37dd6408f5f6 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -149,12 +149,25 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn)
if (unlikely(!kn))
return NULL;
- if (!atomic_inc_unless_negative(&kn->active))
- return NULL;
-
if (kernfs_lockdep(kn))
rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_);
- return kn;
+
+ /*
+ * Try to obtain an active ref. If @kn is deactivated, we block
+ * till either it's reactivated or killed.
+ */
+ do {
+ if (atomic_inc_unless_negative(&kn->active))
+ return kn;
+
+ wait_event(kernfs_root(kn)->deactivate_waitq,
+ atomic_read(&kn->active) >= 0 ||
+ RB_EMPTY_NODE(&kn->rb));
+ } while (!RB_EMPTY_NODE(&kn->rb));
+
+ if (kernfs_lockdep(kn))
+ rwsem_release(&kn->dep_map, 1, _RET_IP_);
+ return NULL;
}
/**
@@ -786,6 +799,7 @@ static void __kernfs_deactivate(struct kernfs_node *kn)
static void __kernfs_remove(struct kernfs_node *kn)
{
+ struct kernfs_root *root = kernfs_root(kn);
struct kernfs_node *pos;
lockdep_assert_held(&kernfs_mutex);
@@ -837,6 +851,9 @@ static void __kernfs_remove(struct kernfs_node *kn)
kernfs_put(pos);
} while (pos != kn);
+
+ /* some nodes killed, kick get_active waiters */
+ wake_up_all(&root->deactivate_waitq);
}
/**