|
@@ -600,8 +600,13 @@ static void ep_remove_wait_queue(struct eppoll_entry *pwq)
|
|
|
wait_queue_head_t *whead;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- /* If it is cleared by POLLFREE, it should be rcu-safe */
|
|
|
- whead = rcu_dereference(pwq->whead);
|
|
|
+ /*
|
|
|
+ * If it is cleared by POLLFREE, it should be rcu-safe.
|
|
|
+ * If we read NULL we need a barrier paired with
|
|
|
+ * smp_store_release() in ep_poll_callback(), otherwise
|
|
|
+ * we rely on whead->lock.
|
|
|
+ */
|
|
|
+ whead = smp_load_acquire(&pwq->whead);
|
|
|
if (whead)
|
|
|
remove_wait_queue(whead, &pwq->wait);
|
|
|
rcu_read_unlock();
|
|
@@ -1134,17 +1139,6 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|
|
struct eventpoll *ep = epi->ep;
|
|
|
int ewake = 0;
|
|
|
|
|
|
- if ((unsigned long)key & POLLFREE) {
|
|
|
- ep_pwq_from_wait(wait)->whead = NULL;
|
|
|
- /*
|
|
|
- * whead = NULL above can race with ep_remove_wait_queue()
|
|
|
- * which can do another remove_wait_queue() after us, so we
|
|
|
- * can't use __remove_wait_queue(). whead->lock is held by
|
|
|
- * the caller.
|
|
|
- */
|
|
|
- list_del_init(&wait->entry);
|
|
|
- }
|
|
|
-
|
|
|
spin_lock_irqsave(&ep->lock, flags);
|
|
|
|
|
|
ep_set_busy_poll_napi_id(epi);
|
|
@@ -1228,10 +1222,26 @@ out_unlock:
|
|
|
if (pwake)
|
|
|
ep_poll_safewake(&ep->poll_wait);
|
|
|
|
|
|
- if (epi->event.events & EPOLLEXCLUSIVE)
|
|
|
- return ewake;
|
|
|
+ if (!(epi->event.events & EPOLLEXCLUSIVE))
|
|
|
+ ewake = 1;
|
|
|
+
|
|
|
+ if ((unsigned long)key & POLLFREE) {
|
|
|
+ /*
|
|
|
+ * If we race with ep_remove_wait_queue() it can miss
|
|
|
+ * ->whead = NULL and do another remove_wait_queue() after
|
|
|
+ * us, so we can't use __remove_wait_queue().
|
|
|
+ */
|
|
|
+ list_del_init(&wait->entry);
|
|
|
+ /*
|
|
|
+ * ->whead != NULL protects us from the race with ep_free()
|
|
|
+ * or ep_remove(), ep_remove_wait_queue() takes whead->lock
|
|
|
+ * held by the caller. Once we nullify it, nothing protects
|
|
|
+ * ep/epi or even wait.
|
|
|
+ */
|
|
|
+ smp_store_release(&ep_pwq_from_wait(wait)->whead, NULL);
|
|
|
+ }
|
|
|
|
|
|
- return 1;
|
|
|
+ return ewake;
|
|
|
}
|
|
|
|
|
|
/*
|