summaryrefslogtreecommitdiff
path: root/fs/eventpoll.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2012-02-27 17:59:05 (GMT)
committerArnd Bergmann <arnd@arndb.de>2012-02-27 17:59:11 (GMT)
commita0008bf73c12212769b0d0b19cdec5c432de94ba (patch)
treee53c67efd4aad97ab9f2addc1fc0d62879b41c55 /fs/eventpoll.c
parent5a8c271b26b83a09924c99aaa5c607021aed142b (diff)
parentcfac337b0fbcad5181096ab72bb24a05bd444562 (diff)
downloadlinux-fsl-qoriq-a0008bf73c12212769b0d0b19cdec5c432de94ba.tar.xz
Merge branch 'lpc32xx/fixes-non-critical' of git://git.antcom.de/linux-2.6 into next/fixes-non-critical
* 'lpc32xx/fixes-non-critical' of git://git.antcom.de/linux-2.6: (1 commit) ARM: LPC32xx: Fix missing and bad LPC32XX macros Update to Linux 3.3-rc5 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'fs/eventpoll.c')
-rw-r--r--fs/eventpoll.c30
1 files changed, 29 insertions, 1 deletions
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index aabdfc3..ea54cde 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -320,6 +320,11 @@ static inline int ep_is_linked(struct list_head *p)
return !list_empty(p);
}
+static inline struct eppoll_entry *ep_pwq_from_wait(wait_queue_t *p)
+{
+ return container_of(p, struct eppoll_entry, wait);
+}
+
/* Get the "struct epitem" from a wait queue pointer */
static inline struct epitem *ep_item_from_wait(wait_queue_t *p)
{
@@ -467,6 +472,18 @@ static void ep_poll_safewake(wait_queue_head_t *wq)
put_cpu();
}
+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 (whead)
+ remove_wait_queue(whead, &pwq->wait);
+ rcu_read_unlock();
+}
+
/*
* This function unregisters poll callbacks from the associated file
* descriptor. Must be called with "mtx" held (or "epmutex" if called from
@@ -481,7 +498,7 @@ static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi)
pwq = list_first_entry(lsthead, struct eppoll_entry, llink);
list_del(&pwq->llink);
- remove_wait_queue(pwq->whead, &pwq->wait);
+ ep_remove_wait_queue(pwq);
kmem_cache_free(pwq_cache, pwq);
}
}
@@ -842,6 +859,17 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k
struct epitem *epi = ep_item_from_wait(wait);
struct eventpoll *ep = epi->ep;
+ 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->task_list);
+ }
+
spin_lock_irqsave(&ep->lock, flags);
/*