Skip to content

Commit 2fd2697

Browse files
imran-kngregkh
authored andcommitted
Revert "kernfs: Change kernfs_notify_list to llist."
This reverts commit b8f35fa. This is causing regression due to same kernfs_node getting added multiple times in kernfs_notify_list so revert it until safe way of using llist in this context is found. Reported-by: Nathan Chancellor <[email protected]> Reported-by: Michael Walle <[email protected]> Reported-by: Marek Szyprowski <[email protected]> Signed-off-by: Imran Khan <[email protected]> Cc: Tejun Heo <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 72b5d5a commit 2fd2697

File tree

2 files changed

+28
-21
lines changed

2 files changed

+28
-21
lines changed

fs/kernfs/file.c

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,18 @@ struct kernfs_open_node {
2525
struct list_head files; /* goes through kernfs_open_file.list */
2626
};
2727

28-
/**
29-
* attribute_to_node - get kernfs_node object corresponding to a kernfs attribute
30-
* @ptr: &struct kernfs_elem_attr
31-
* @type: struct kernfs_node
32-
* @member: name of member (i.e attr)
28+
/*
29+
* kernfs_notify() may be called from any context and bounces notifications
30+
* through a work item. To minimize space overhead in kernfs_node, the
31+
* pending queue is implemented as a singly linked list of kernfs_nodes.
32+
* The list is terminated with the self pointer so that whether a
33+
* kernfs_node is on the list or not can be determined by testing the next
34+
* pointer for NULL.
3335
*/
34-
#define attribute_to_node(ptr, type, member) \
35-
container_of(ptr, type, member)
36+
#define KERNFS_NOTIFY_EOL ((void *)&kernfs_notify_list)
3637

37-
static LLIST_HEAD(kernfs_notify_list);
38+
static DEFINE_SPINLOCK(kernfs_notify_lock);
39+
static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL;
3840

3941
static inline struct mutex *kernfs_open_file_mutex_ptr(struct kernfs_node *kn)
4042
{
@@ -909,16 +911,18 @@ static void kernfs_notify_workfn(struct work_struct *work)
909911
struct kernfs_node *kn;
910912
struct kernfs_super_info *info;
911913
struct kernfs_root *root;
912-
struct llist_node *free;
913-
struct kernfs_elem_attr *attr;
914914
repeat:
915915
/* pop one off the notify_list */
916-
free = llist_del_first(&kernfs_notify_list);
917-
if (free == NULL)
916+
spin_lock_irq(&kernfs_notify_lock);
917+
kn = kernfs_notify_list;
918+
if (kn == KERNFS_NOTIFY_EOL) {
919+
spin_unlock_irq(&kernfs_notify_lock);
918920
return;
921+
}
922+
kernfs_notify_list = kn->attr.notify_next;
923+
kn->attr.notify_next = NULL;
924+
spin_unlock_irq(&kernfs_notify_lock);
919925

920-
attr = llist_entry(free, struct kernfs_elem_attr, notify_next);
921-
kn = attribute_to_node(attr, struct kernfs_node, attr);
922926
root = kernfs_root(kn);
923927
/* kick fsnotify */
924928
down_write(&root->kernfs_rwsem);
@@ -974,14 +978,12 @@ static void kernfs_notify_workfn(struct work_struct *work)
974978
void kernfs_notify(struct kernfs_node *kn)
975979
{
976980
static DECLARE_WORK(kernfs_notify_work, kernfs_notify_workfn);
981+
unsigned long flags;
977982
struct kernfs_open_node *on;
978983

979984
if (WARN_ON(kernfs_type(kn) != KERNFS_FILE))
980985
return;
981986

982-
/* Because we are using llist for kernfs_notify_list */
983-
WARN_ON_ONCE(in_nmi());
984-
985987
/* kick poll immediately */
986988
rcu_read_lock();
987989
on = rcu_dereference(kn->attr.open);
@@ -992,9 +994,14 @@ void kernfs_notify(struct kernfs_node *kn)
992994
rcu_read_unlock();
993995

994996
/* schedule work to kick fsnotify */
995-
kernfs_get(kn);
996-
llist_add(&kn->attr.notify_next, &kernfs_notify_list);
997-
schedule_work(&kernfs_notify_work);
997+
spin_lock_irqsave(&kernfs_notify_lock, flags);
998+
if (!kn->attr.notify_next) {
999+
kernfs_get(kn);
1000+
kn->attr.notify_next = kernfs_notify_list;
1001+
kernfs_notify_list = kn;
1002+
schedule_work(&kernfs_notify_work);
1003+
}
1004+
spin_unlock_irqrestore(&kernfs_notify_lock, flags);
9981005
}
9991006
EXPORT_SYMBOL_GPL(kernfs_notify);
10001007

include/linux/kernfs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ struct kernfs_elem_attr {
173173
const struct kernfs_ops *ops;
174174
struct kernfs_open_node __rcu *open;
175175
loff_t size;
176-
struct llist_node notify_next; /* for kernfs_notify() */
176+
struct kernfs_node *notify_next; /* for kernfs_notify() */
177177
};
178178

179179
/*

0 commit comments

Comments
 (0)