From: Tejun Heo <tj@kernel.org>
To: gregkh@linuxfoundation.org
Cc: linux-kernel@vger.kernel.org, schwidefsky@de.ibm.com,
heiko.carstens@de.ibm.com, stern@rowland.harvard.edu,
JBottomley@parallels.com, bhelgaas@google.com,
Tejun Heo <tj@kernel.org>
Subject: [PATCH 09/14] kernfs: implement kernfs_{de|re}activate[_self]()
Date: Fri, 10 Jan 2014 08:46:55 -0500 [thread overview]
Message-ID: <1389361620-5086-10-git-send-email-tj@kernel.org> (raw)
In-Reply-To: <1389361620-5086-1-git-send-email-tj@kernel.org>
This patch implements four functions to manipulate deactivation state
- deactivate, reactivate and the _self suffixed pair. A new fields
kernfs_node->deact_depth is added so that concurrent and nested
deactivations are handled properly. kernfs_node->hash is moved so
that it's paired with the new field so that it doesn't increase the
size of kernfs_node.
A kernfs user's lock would normally nest inside active ref but during
removal the user may want to perform kernfs_remove() while holding the
said lock, which would introduce a reverse locking dependency. This
function can be used to break such reverse dependency by allowing
deactivation step to performed separately outside user's critical
section.
This will also be used implement kernfs_remove_self().
Signed-off-by: Tejun Heo <tj@kernel.org>
---
fs/kernfs/dir.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++---
include/linux/kernfs.h | 7 ++-
2 files changed, 121 insertions(+), 6 deletions(-)
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index f18fc5e..cc76af4 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -396,6 +396,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name,
atomic_set(&kn->count, 1);
atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
+ kn->deact_depth = 1;
RB_CLEAR_NODE(&kn->rb);
kn->name = name;
@@ -461,6 +462,7 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent)
/* Mark the entry added into directory tree */
atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
+ kn->deact_depth--;
ret = 0;
out_unlock:
mutex_unlock(&kernfs_mutex);
@@ -561,6 +563,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv)
}
atomic_sub(KN_DEACTIVATED_BIAS, &kn->active);
+ kn->deact_depth--;
kn->priv = priv;
kn->dir.root = root;
@@ -773,7 +776,8 @@ static void __kernfs_deactivate(struct kernfs_node *kn)
/* prevent any new usage under @kn by deactivating all nodes */
pos = NULL;
while ((pos = kernfs_next_descendant_post(pos, kn))) {
- if (atomic_read(&pos->active) >= 0) {
+ if (!pos->deact_depth++) {
+ WARN_ON_ONCE(atomic_read(&pos->active) < 0);
atomic_add(KN_DEACTIVATED_BIAS, &pos->active);
pos->flags |= KERNFS_JUST_DEACTIVATED;
}
@@ -797,9 +801,118 @@ static void __kernfs_deactivate(struct kernfs_node *kn)
}
}
+static void __kernfs_reactivate(struct kernfs_node *kn)
+{
+ struct kernfs_node *pos;
+
+ pos = NULL;
+ while ((pos = kernfs_next_descendant_post(pos, kn))) {
+ if (!--pos->deact_depth) {
+ WARN_ON_ONCE(atomic_read(&pos->active) >= 0);
+ atomic_sub(KN_DEACTIVATED_BIAS, &pos->active);
+ }
+ WARN_ON_ONCE(pos->deact_depth < 0);
+ }
+
+ /* some nodes reactivated, kick get_active waiters */
+ wake_up_all(&kernfs_root(kn)->deactivate_waitq);
+}
+
+static void __kernfs_deactivate_self(struct kernfs_node *kn)
+{
+ /*
+ * Take out ourself out of the active ref dependency chain and
+ * deactivate. If we're called without an active ref, lockdep will
+ * complain.
+ */
+ kernfs_put_active(kn);
+ __kernfs_deactivate(kn);
+}
+
+static void __kernfs_reactivate_self(struct kernfs_node *kn)
+{
+ __kernfs_reactivate(kn);
+ /*
+ * Restore active ref dropped by deactivate_self() so that it's
+ * balanced on return. put_active() will soon be called on @kn, so
+ * this can't break anything regardless of @kn's state.
+ */
+ atomic_inc(&kn->active);
+ if (kernfs_lockdep(kn))
+ rwsem_acquire(&kn->dep_map, 0, 1, _RET_IP_);
+}
+
+/**
+ * kernfs_deactivate - deactivate subtree of a node
+ * @kn: kernfs_node to deactivate subtree of
+ *
+ * Deactivate the subtree of @kn. On return, there's no active operation
+ * going on under @kn and creation or renaming of a node under @kn is
+ * blocked until @kn is reactivated or removed. This function can be
+ * called multiple times and nests properly. Each invocation should be
+ * paired with kernfs_reactivate().
+ *
+ * For a kernfs user which uses simple locking, the subsystem lock would
+ * nest inside active reference. This becomes problematic if the user
+ * tries to remove nodes while holding the subystem lock as it would create
+ * a reverse locking dependency from the subsystem lock to active ref.
+ * This function can be used to break such reverse dependency. The user
+ * can call this function outside the subsystem lock and then proceed to
+ * invoke kernfs_remove() while holding the subsystem lock without
+ * introducing such reverse dependency.
+ */
+void kernfs_deactivate(struct kernfs_node *kn)
+{
+ mutex_lock(&kernfs_mutex);
+ __kernfs_deactivate(kn);
+ mutex_unlock(&kernfs_mutex);
+}
+
+/**
+ * kernfs_reactivate - reactivate subtree of a node
+ * @kn: kernfs_node to reactivate subtree of
+ *
+ * Undo kernfs_deactivate().
+ */
+void kernfs_reactivate(struct kernfs_node *kn)
+{
+ mutex_lock(&kernfs_mutex);
+ __kernfs_reactivate(kn);
+ mutex_unlock(&kernfs_mutex);
+}
+
+/**
+ * kernfs_deactivate_self - deactivate subtree of a node from its own method
+ * @kn: the self kernfs_node to deactivate subtree of
+ *
+ * The caller must be running off of a kernfs operation which is invoked
+ * with an active reference - e.g. one of kernfs_ops. Once this function
+ * is called, @kn may be removed by someone else while the enclosing method
+ * is in progress. Other than that, this function is equivalent to
+ * kernfs_deactivate() and should be paired with kernfs_reactivate_self().
+ */
+void kernfs_deactivate_self(struct kernfs_node *kn)
+{
+ mutex_lock(&kernfs_mutex);
+ __kernfs_deactivate_self(kn);
+ mutex_unlock(&kernfs_mutex);
+}
+
+/**
+ * kernfs_reactivate_self - reactivate subtree of a node from its own method
+ * @kn: the self kernfs_node to reactivate subtree of
+ *
+ * Undo kernfs_deactivate_self().
+ */
+void kernfs_reactivate_self(struct kernfs_node *kn)
+{
+ mutex_lock(&kernfs_mutex);
+ __kernfs_reactivate_self(kn);
+ mutex_unlock(&kernfs_mutex);
+}
+
static void __kernfs_remove(struct kernfs_node *kn)
{
- struct kernfs_root *root = kernfs_root(kn);
struct kernfs_node *pos;
lockdep_assert_held(&kernfs_mutex);
@@ -844,9 +957,6 @@ 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);
}
/**
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 9b5a4bb..ac86930 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -80,6 +80,8 @@ struct kernfs_elem_attr {
struct kernfs_node {
atomic_t count;
atomic_t active;
+ int deact_depth;
+ unsigned int hash; /* ns + name hash */
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
@@ -90,7 +92,6 @@ struct kernfs_node {
struct rb_node rb;
const void *ns; /* namespace tag */
- unsigned int hash; /* ns + name hash */
union {
struct kernfs_elem_dir dir;
struct kernfs_elem_symlink symlink;
@@ -233,6 +234,10 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
const char *name,
struct kernfs_node *target);
+void kernfs_deactivate(struct kernfs_node *kn);
+void kernfs_reactivate(struct kernfs_node *kn);
+void kernfs_deactivate_self(struct kernfs_node *kn);
+void kernfs_reactivate_self(struct kernfs_node *kn);
void kernfs_remove(struct kernfs_node *kn);
int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,
const void *ns);
--
1.8.4.2
next prev parent reply other threads:[~2014-01-10 13:49 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-01-10 13:46 [PATCHSET v2 driver-core-next] kernfs, sysfs, driver-core: implement synchronous self-removal Tejun Heo
2014-01-10 13:46 ` [PATCH 01/14] kernfs: fix get_active failure handling in kernfs_seq_*() Tejun Heo
2014-01-10 13:46 ` [PATCH 02/14] kernfs: replace kernfs_node->u.completion with kernfs_root->deactivate_waitq Tejun Heo
2014-01-10 13:46 ` [PATCH 03/14] kernfs: remove KERNFS_ACTIVE_REF and add kernfs_lockdep() Tejun Heo
2014-01-10 13:46 ` [PATCH 04/14] kernfs: remove KERNFS_REMOVED Tejun Heo
2014-01-10 13:46 ` [PATCH 05/14] kernfs: restructure removal path to fix possible premature return Tejun Heo
2014-01-10 13:46 ` [PATCH 06/14] kernfs: invoke kernfs_unmap_bin_file() directly from __kernfs_remove() Tejun Heo
2014-01-10 13:46 ` [PATCH 07/14] kernfs: remove kernfs_addrm_cxt Tejun Heo
2014-01-10 13:46 ` [PATCH 08/14] kernfs: make kernfs_get_active() block if the node is deactivated but not removed Tejun Heo
2014-01-10 13:46 ` Tejun Heo [this message]
2014-01-10 13:46 ` [PATCH 10/14] kernfs, sysfs, driver-core: implement kernfs_remove_self() and its wrappers Tejun Heo
2014-01-10 13:46 ` [PATCH 11/14] pci: use device_remove_file_self() instead of device_schedule_callback() Tejun Heo
2014-01-10 13:46 ` [PATCH 12/14] scsi: " Tejun Heo
2014-01-10 13:46 ` [PATCH 13/14] s390: " Tejun Heo
2014-01-10 13:47 ` [PATCH 14/14] sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner() Tejun Heo
2014-01-10 13:54 ` [PATCHSET v2 driver-core-next] kernfs, sysfs, driver-core: implement synchronous self-removal Tejun Heo
2014-01-10 15:46 ` Alan Stern
2014-01-11 18:51 ` Tejun Heo
2014-01-11 20:19 ` Greg KH
2014-01-11 22:52 ` Alan Stern
2014-01-11 23:37 ` Greg KH
-- strict thread matches above, loose matches on Subject: below --
2014-01-10 13:57 [PATCHSET v3 " Tejun Heo
2014-01-10 13:57 ` [PATCH 09/14] kernfs: implement kernfs_{de|re}activate[_self]() Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1389361620-5086-10-git-send-email-tj@kernel.org \
--to=tj@kernel.org \
--cc=JBottomley@parallels.com \
--cc=bhelgaas@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=heiko.carstens@de.ibm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=schwidefsky@de.ibm.com \
--cc=stern@rowland.harvard.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox