Skip to content
This repository has been archived by the owner on Jun 18, 2024. It is now read-only.

kernfs: Backport kernfs locking fix patchset #128

Merged
merged 3 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 27 additions & 21 deletions fs/kernfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ static DEFINE_RWLOCK(kernfs_rename_lock); /* kn->parent and ->name */
*/
static DEFINE_SPINLOCK(kernfs_pr_cont_lock);
static char kernfs_pr_cont_buf[PATH_MAX]; /* protected by pr_cont_lock */
static DEFINE_RAW_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */
static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */

#define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)

Expand Down Expand Up @@ -529,6 +529,20 @@ void kernfs_get(struct kernfs_node *kn)
}
EXPORT_SYMBOL_GPL(kernfs_get);

static void kernfs_free_rcu(struct rcu_head *rcu)
{
struct kernfs_node *kn = container_of(rcu, struct kernfs_node, rcu);

kfree_const(kn->name);

if (kn->iattr) {
simple_xattrs_free(&kn->iattr->xattrs, NULL);
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
}

kmem_cache_free(kernfs_node_cache, kn);
}

/**
* kernfs_put - put a reference count on a kernfs_node
* @kn: the target kernfs_node
Expand All @@ -539,7 +553,6 @@ void kernfs_put(struct kernfs_node *kn)
{
struct kernfs_node *parent;
struct kernfs_root *root;
unsigned long flags;

if (!kn || !atomic_dec_and_test(&kn->count))
return;
Expand All @@ -558,16 +571,11 @@ void kernfs_put(struct kernfs_node *kn)
if (kernfs_type(kn) == KERNFS_LINK)
kernfs_put(kn->symlink.target_kn);

kfree_const(kn->name);

if (kn->iattr) {
simple_xattrs_free(&kn->iattr->xattrs, NULL);
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
}
raw_spin_lock_irqsave(&kernfs_idr_lock, flags);
spin_lock(&kernfs_idr_lock);
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
raw_spin_unlock_irqrestore(&kernfs_idr_lock, flags);
kmem_cache_free(kernfs_node_cache, kn);
spin_unlock(&kernfs_idr_lock);

call_rcu(&kn->rcu, kernfs_free_rcu);

kn = parent;
if (kn) {
Expand All @@ -576,7 +584,7 @@ void kernfs_put(struct kernfs_node *kn)
} else {
/* just released the root kn, free @root too */
idr_destroy(&root->ino_idr);
kfree(root);
kfree_rcu(root, rcu);
}
}
EXPORT_SYMBOL_GPL(kernfs_put);
Expand Down Expand Up @@ -608,7 +616,6 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
struct kernfs_node *kn;
u32 id_highbits;
int ret;
unsigned long irqflags;

name = kstrdup_const(name, GFP_KERNEL);
if (!name)
Expand All @@ -619,13 +626,13 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
goto err_out1;

idr_preload(GFP_KERNEL);
raw_spin_lock_irqsave(&kernfs_idr_lock, irqflags);
spin_lock(&kernfs_idr_lock);
ret = idr_alloc_cyclic(&root->ino_idr, kn, 1, 0, GFP_ATOMIC);
if (ret >= 0 && ret < root->last_id_lowbits)
root->id_highbits++;
id_highbits = root->id_highbits;
root->last_id_lowbits = ret;
raw_spin_unlock_irqrestore(&kernfs_idr_lock, irqflags);
spin_unlock(&kernfs_idr_lock);
idr_preload_end();
if (ret < 0)
goto err_out2;
Expand Down Expand Up @@ -661,9 +668,9 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
return kn;

err_out3:
raw_spin_lock_irqsave(&kernfs_idr_lock, irqflags);
spin_lock(&kernfs_idr_lock);
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
raw_spin_unlock_irqrestore(&kernfs_idr_lock, irqflags);
spin_unlock(&kernfs_idr_lock);
err_out2:
kmem_cache_free(kernfs_node_cache, kn);
err_out1:
Expand Down Expand Up @@ -704,9 +711,8 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root,
struct kernfs_node *kn;
ino_t ino = kernfs_id_ino(id);
u32 gen = kernfs_id_gen(id);
unsigned long flags;

raw_spin_lock_irqsave(&kernfs_idr_lock, flags);
rcu_read_lock();

kn = idr_find(&root->ino_idr, (u32)ino);
if (!kn)
Expand All @@ -730,10 +736,10 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root,
if (unlikely(!__kernfs_active(kn) || !atomic_inc_not_zero(&kn->count)))
goto err_unlock;

raw_spin_unlock_irqrestore(&kernfs_idr_lock, flags);
rcu_read_unlock();
return kn;
err_unlock:
raw_spin_unlock_irqrestore(&kernfs_idr_lock, flags);
rcu_read_unlock();
return NULL;
}

Expand Down
2 changes: 2 additions & 0 deletions fs/kernfs/kernfs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct kernfs_root {
struct rw_semaphore kernfs_rwsem;
struct rw_semaphore kernfs_iattr_rwsem;
struct rw_semaphore kernfs_supers_rwsem;

struct rcu_head rcu;
};

/* +1 to avoid triggering overflow warning when negating it */
Expand Down
7 changes: 5 additions & 2 deletions include/linux/kernfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ struct kernfs_node {

const void *ns; /* namespace tag */
unsigned int hash; /* ns + name hash */
unsigned short flags;
umode_t mode;

union {
struct kernfs_elem_dir dir;
struct kernfs_elem_symlink symlink;
Expand All @@ -220,9 +223,9 @@ struct kernfs_node {
*/
u64 id;

unsigned short flags;
umode_t mode;
struct kernfs_iattrs *iattr;

struct rcu_head rcu;
};

/*
Expand Down