|
@@ -508,6 +508,10 @@ void kernfs_put(struct kernfs_node *kn)
|
|
|
struct kernfs_node *parent;
|
|
|
struct kernfs_root *root;
|
|
|
|
|
|
+ /*
|
|
|
+ * kernfs_node is freed with ->count 0, kernfs_find_and_get_node_by_ino
|
|
|
+ * depends on this to filter reused stale node
|
|
|
+ */
|
|
|
if (!kn || !atomic_dec_and_test(&kn->count))
|
|
|
return;
|
|
|
root = kernfs_root(kn);
|
|
@@ -649,6 +653,11 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
|
|
|
kn->ino = ret;
|
|
|
kn->generation = gen;
|
|
|
|
|
|
+ /*
|
|
|
+ * set ino first. This barrier is paired with atomic_inc_not_zero in
|
|
|
+ * kernfs_find_and_get_node_by_ino
|
|
|
+ */
|
|
|
+ smp_mb__before_atomic();
|
|
|
atomic_set(&kn->count, 1);
|
|
|
atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
|
|
|
RB_CLEAR_NODE(&kn->rb);
|
|
@@ -680,6 +689,54 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
|
|
|
return kn;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * kernfs_find_and_get_node_by_ino - get kernfs_node from inode number
|
|
|
+ * @root: the kernfs root
|
|
|
+ * @ino: inode number
|
|
|
+ *
|
|
|
+ * RETURNS:
|
|
|
+ * NULL on failure. Return a kernfs node with reference counter incremented
|
|
|
+ */
|
|
|
+struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
|
|
|
+ unsigned int ino)
|
|
|
+{
|
|
|
+ struct kernfs_node *kn;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ kn = idr_find(&root->ino_idr, ino);
|
|
|
+ if (!kn)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Since kernfs_node is freed in RCU, it's possible an old node for ino
|
|
|
+ * is freed, but reused before RCU grace period. But a freed node (see
|
|
|
+ * kernfs_put) or an incompletedly initialized node (see
|
|
|
+ * __kernfs_new_node) should have 'count' 0. We can use this fact to
|
|
|
+ * filter out such node.
|
|
|
+ */
|
|
|
+ if (!atomic_inc_not_zero(&kn->count)) {
|
|
|
+ kn = NULL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The node could be a new node or a reused node. If it's a new node,
|
|
|
+ * we are ok. If it's reused because of RCU (because of
|
|
|
+ * SLAB_TYPESAFE_BY_RCU), the __kernfs_new_node always sets its 'ino'
|
|
|
+ * before 'count'. So if 'count' is uptodate, 'ino' should be uptodate,
|
|
|
+ * hence we can use 'ino' to filter stale node.
|
|
|
+ */
|
|
|
+ if (kn->ino != ino)
|
|
|
+ goto out;
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return kn;
|
|
|
+out:
|
|
|
+ rcu_read_unlock();
|
|
|
+ kernfs_put(kn);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* kernfs_add_one - add kernfs_node to parent without warning
|
|
|
* @kn: kernfs_node to be added
|