summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorPaton J. Lewis <palewis@adobe.com>2013-03-02 15:25:29 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2013-03-07 14:26:33 +1100
commit6e940be35e63d230ed45cb885de1a1f4f935f512 (patch)
tree5065d86e0485b2c1d10116273c41fab44e02f7e7 /fs
parent95371c706835228a049cff43fd20e4da2eb865b5 (diff)
epoll: support for disabling items, and a self-test app
It is not currently possible to reliably delete epoll items when using the same epoll set from multiple threads. After calling epoll_ctl with EPOLL_CTL_DEL, another thread might still be executing code related to an event for that epoll item (in response to epoll_wait). Therefore the deleting thread does not know when it is safe to delete resources pertaining to the associated epoll item because another thread might be using those resources. The deleting thread could wait an arbitrary amount of time after calling epoll_ctl with EPOLL_CTL_DEL and before deleting the item, but this is inefficient and could result in the destruction of resources before another thread is done handling an event returned by epoll_wait. This patch enhances epoll_ctl to support EPOLL_CTL_DISABLE, which disables an epoll item. If epoll_ctl returns -EBUSY in this case, then another thread may handling a return from epoll_wait for this item. Otherwise if epoll_ctl returns 0, then it is safe to delete the epoll item. This allows multiple threads to use a mutex to determine when it is safe to delete an epoll item and its associated resources, which allows epoll items to be deleted both efficiently and without error in a multi-threaded environment. Note that EPOLL_CTL_DISABLE is only useful in conjunction with EPOLLONESHOT, and using EPOLL_CTL_DISABLE on an epoll item without EPOLLONESHOT returns -EINVAL. This patch also adds a new test_epoll self-test program to both demonstrate the need for this feature and test it. Signed-off-by: Paton J. Lewis <palewis@adobe.com> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Jason Baron <jbaron@redhat.com> Cc: Paul Holland <pholland@adobe.com> Cc: Davide Libenzi <davidel@xmailserver.org> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/eventpoll.c40
1 files changed, 37 insertions, 3 deletions
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 495d15558f42..3026c2485da2 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -349,7 +349,7 @@ static inline struct epitem *ep_item_from_epqueue(poll_table *p)
/* Tells if the epoll_ctl(2) operation needs an event copy from userspace */
static inline int ep_op_has_event(int op)
{
- return op != EPOLL_CTL_DEL;
+ return op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD;
}
/* Initialize the poll safe wake up structure */
@@ -679,6 +679,36 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi)
return 0;
}
+/*
+ * Disables a "struct epitem" in the eventpoll set. Returns -EBUSY if the item
+ * had no event flags set, indicating that another thread may be currently
+ * handling that item's events (in the case that EPOLLONESHOT was being
+ * used). Otherwise a zero result indicates that the item has been disabled
+ * from receiving events. A disabled item may be re-enabled via
+ * EPOLL_CTL_MOD. Must be called with "mtx" held.
+ */
+static int ep_disable(struct eventpoll *ep, struct epitem *epi)
+{
+ int result = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ if (epi->event.events & EPOLLONESHOT) {
+ if (epi->event.events & ~EP_PRIVATE_BITS) {
+ if (ep_is_linked(&epi->rdllink))
+ list_del_init(&epi->rdllink);
+ /* Ensure ep_poll_callback will not add epi back onto
+ ready list: */
+ epi->event.events &= EP_PRIVATE_BITS;
+ } else
+ result = -EBUSY;
+ } else
+ result = -EINVAL;
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ return result;
+}
+
static void ep_free(struct eventpoll *ep)
{
struct rb_node *rbp;
@@ -1049,8 +1079,6 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi)
rb_insert_color(&epi->rbn, &ep->rbr);
}
-
-
#define PATH_ARR_SIZE 5
/*
* These are the number paths of length 1 to 5, that we are allowing to emanate
@@ -1836,6 +1864,12 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
} else
error = -ENOENT;
break;
+ case EPOLL_CTL_DISABLE:
+ if (epi)
+ error = ep_disable(ep, epi);
+ else
+ error = -ENOENT;
+ break;
}
mutex_unlock(&ep->mtx);