struct list_head fllink;
 
        /* wakeup_source used when EPOLLWAKEUP is set */
-       struct wakeup_source *ws;
+       struct wakeup_source __rcu *ws;
 
        /* The structure that describe the interested events and the source fd */
        struct epoll_event event;
        }
 }
 
+/* call only when ep->mtx is held */
+static inline struct wakeup_source *ep_wakeup_source(struct epitem *epi)
+{
+       return rcu_dereference_check(epi->ws, lockdep_is_held(&epi->ep->mtx));
+}
+
+/* call only when ep->mtx is held */
+static inline void ep_pm_stay_awake(struct epitem *epi)
+{
+       struct wakeup_source *ws = ep_wakeup_source(epi);
+
+       if (ws)
+               __pm_stay_awake(ws);
+}
+
+static inline bool ep_has_wakeup_source(struct epitem *epi)
+{
+       return rcu_access_pointer(epi->ws) ? true : false;
+}
+
+/* call when ep->mtx cannot be held (ep_poll_callback) */
+static inline void ep_pm_stay_awake_rcu(struct epitem *epi)
+{
+       struct wakeup_source *ws;
+
+       rcu_read_lock();
+       ws = rcu_dereference(epi->ws);
+       if (ws)
+               __pm_stay_awake(ws);
+       rcu_read_unlock();
+}
+
 /**
  * ep_scan_ready_list - Scans the ready list in a way that makes possible for
  *                      the scan code, to call f_op->poll(). Also allows for
                 */
                if (!ep_is_linked(&epi->rdllink)) {
                        list_add_tail(&epi->rdllink, &ep->rdllist);
-                       __pm_stay_awake(epi->ws);
+                       ep_pm_stay_awake(epi);
                }
        }
        /*
                list_del_init(&epi->rdllink);
        spin_unlock_irqrestore(&ep->lock, flags);
 
-       wakeup_source_unregister(epi->ws);
+       wakeup_source_unregister(ep_wakeup_source(epi));
 
        /* At this point it is safe to free the eventpoll item */
        kmem_cache_free(epi_cache, epi);
                         * callback, but it's not actually ready, as far as
                         * caller requested events goes. We can remove it here.
                         */
-                       __pm_relax(epi->ws);
+                       __pm_relax(ep_wakeup_source(epi));
                        list_del_init(&epi->rdllink);
                }
        }
        /* If this file is already in the ready list we exit soon */
        if (!ep_is_linked(&epi->rdllink)) {
                list_add_tail(&epi->rdllink, &ep->rdllist);
-               __pm_stay_awake(epi->ws);
+               ep_pm_stay_awake_rcu(epi);
        }
 
        /*
 static int ep_create_wakeup_source(struct epitem *epi)
 {
        const char *name;
+       struct wakeup_source *ws;
 
        if (!epi->ep->ws) {
                epi->ep->ws = wakeup_source_register("eventpoll");
        }
 
        name = epi->ffd.file->f_path.dentry->d_name.name;
-       epi->ws = wakeup_source_register(name);
-       if (!epi->ws)
+       ws = wakeup_source_register(name);
+
+       if (!ws)
                return -ENOMEM;
+       rcu_assign_pointer(epi->ws, ws);
 
        return 0;
 }
 
-static void ep_destroy_wakeup_source(struct epitem *epi)
+/* rare code path, only used when EPOLL_CTL_MOD removes a wakeup source */
+static noinline void ep_destroy_wakeup_source(struct epitem *epi)
 {
-       wakeup_source_unregister(epi->ws);
-       epi->ws = NULL;
+       struct wakeup_source *ws = ep_wakeup_source(epi);
+
+       rcu_assign_pointer(epi->ws, NULL);
+
+       /*
+        * wait for ep_pm_stay_awake_rcu to finish, synchronize_rcu is
+        * used internally by wakeup_source_remove, too (called by
+        * wakeup_source_unregister), so we cannot use call_rcu
+        */
+       synchronize_rcu();
+       wakeup_source_unregister(ws);
 }
 
 /*
                if (error)
                        goto error_create_wakeup_source;
        } else {
-               epi->ws = NULL;
+               RCU_INIT_POINTER(epi->ws, NULL);
        }
 
        /* Initialize the poll table using the queue callback */
        /* If the file is already "ready" we drop it inside the ready list */
        if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) {
                list_add_tail(&epi->rdllink, &ep->rdllist);
-               __pm_stay_awake(epi->ws);
+               ep_pm_stay_awake(epi);
 
                /* Notify waiting tasks that events are available */
                if (waitqueue_active(&ep->wq))
                list_del_init(&epi->rdllink);
        spin_unlock_irqrestore(&ep->lock, flags);
 
-       wakeup_source_unregister(epi->ws);
+       wakeup_source_unregister(ep_wakeup_source(epi));
 
 error_create_wakeup_source:
        kmem_cache_free(epi_cache, epi);
        pt._key = event->events;
        epi->event.data = event->data; /* protected by mtx */
        if (epi->event.events & EPOLLWAKEUP) {
-               if (!epi->ws)
+               if (!ep_has_wakeup_source(epi))
                        ep_create_wakeup_source(epi);
-       } else if (epi->ws) {
+       } else if (ep_has_wakeup_source(epi)) {
                ep_destroy_wakeup_source(epi);
        }
 
                spin_lock_irq(&ep->lock);
                if (!ep_is_linked(&epi->rdllink)) {
                        list_add_tail(&epi->rdllink, &ep->rdllist);
-                       __pm_stay_awake(epi->ws);
+                       ep_pm_stay_awake(epi);
 
                        /* Notify waiting tasks that events are available */
                        if (waitqueue_active(&ep->wq))
        unsigned int revents;
        struct epitem *epi;
        struct epoll_event __user *uevent;
+       struct wakeup_source *ws;
        poll_table pt;
 
        init_poll_funcptr(&pt, NULL);
                 * instead, but then epi->ws would temporarily be out of sync
                 * with ep_is_linked().
                 */
-               if (epi->ws && epi->ws->active)
-                       __pm_stay_awake(ep->ws);
-               __pm_relax(epi->ws);
+               ws = ep_wakeup_source(epi);
+               if (ws) {
+                       if (ws->active)
+                               __pm_stay_awake(ep->ws);
+                       __pm_relax(ws);
+               }
+
                list_del_init(&epi->rdllink);
 
                pt._key = epi->event.events;
                        if (__put_user(revents, &uevent->events) ||
                            __put_user(epi->event.data, &uevent->data)) {
                                list_add(&epi->rdllink, head);
-                               __pm_stay_awake(epi->ws);
+                               ep_pm_stay_awake(epi);
                                return eventcnt ? eventcnt : -EFAULT;
                        }
                        eventcnt++;
                                 * poll callback will queue them in ep->ovflist.
                                 */
                                list_add_tail(&epi->rdllink, &ep->rdllist);
-                               __pm_stay_awake(epi->ws);
+                               ep_pm_stay_awake(epi);
                        }
                }
        }