kernfs: Improve kernfs_notify() poll notification latency
authorRadu Rendec <radu.rendec@gmail.com>
Fri, 16 Nov 2018 02:09:54 +0000 (21:09 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 Nov 2018 10:59:33 +0000 (11:59 +0100)
kernfs_notify() does two notifications: poll and fsnotify. Originally,
both notifications were done from scheduled work context and all that
kernfs_notify() did was schedule the work.

This patch simply moves the poll notification from the scheduled work
handler to kernfs_notify(). The fsnotify notification still needs to be
done from scheduled work context because it can sleep (it needs to lock
a mutex).

If the poll notification is time critical (the notified thread needs to
wake as quickly as possible), it's better to do it from kernfs_notify()
directly. One example is calling sysfs_notify_dirent() from a hardware
interrupt handler to wake up a thread and handle the interrupt in user
space.

Signed-off-by: Radu Rendec <radu.rendec@gmail.com>
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/kernfs/file.c

index dbf5bc250bfdf39076066707f381d84e2857b0b4..f8d5021a652ead80f898ea5d4c421821ab8bae3b 100644 (file)
@@ -857,7 +857,6 @@ static __poll_t kernfs_fop_poll(struct file *filp, poll_table *wait)
 static void kernfs_notify_workfn(struct work_struct *work)
 {
        struct kernfs_node *kn;
-       struct kernfs_open_node *on;
        struct kernfs_super_info *info;
 repeat:
        /* pop one off the notify_list */
@@ -871,17 +870,6 @@ repeat:
        kn->attr.notify_next = NULL;
        spin_unlock_irq(&kernfs_notify_lock);
 
-       /* kick poll */
-       spin_lock_irq(&kernfs_open_node_lock);
-
-       on = kn->attr.open;
-       if (on) {
-               atomic_inc(&on->event);
-               wake_up_interruptible(&on->poll);
-       }
-
-       spin_unlock_irq(&kernfs_open_node_lock);
-
        /* kick fsnotify */
        mutex_lock(&kernfs_mutex);
 
@@ -934,10 +922,21 @@ void kernfs_notify(struct kernfs_node *kn)
 {
        static DECLARE_WORK(kernfs_notify_work, kernfs_notify_workfn);
        unsigned long flags;
+       struct kernfs_open_node *on;
 
        if (WARN_ON(kernfs_type(kn) != KERNFS_FILE))
                return;
 
+       /* kick poll immediately */
+       spin_lock_irqsave(&kernfs_open_node_lock, flags);
+       on = kn->attr.open;
+       if (on) {
+               atomic_inc(&on->event);
+               wake_up_interruptible(&on->poll);
+       }
+       spin_unlock_irqrestore(&kernfs_open_node_lock, flags);
+
+       /* schedule work to kick fsnotify */
        spin_lock_irqsave(&kernfs_notify_lock, flags);
        if (!kn->attr.notify_next) {
                kernfs_get(kn);