linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH -next 0/6] epoll: some miscellaneous optimizations
@ 2018-11-08  5:10 Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 1/6] fs/epoll: remove max_nests argument from ep_call_nested() Davidlohr Bueso
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel

Hi,

The following are some incremental optimizations on some of the epoll
core. Each patch has the details, but together, the series is seen
to shave off measurable cycles on a number of systems and workloads.

For example, on a 40-core IB, a pipetest as well as parallel epoll_wait()
benchmark show around a 20-30% increase in raw operations per second when
the box is fully occupied (incremental thread counts), and up to 15%
performance improvement with lower counts.

Passes ltp epoll related testcases. Please consider for v4.21/5.0.

Thanks!

Davidlohr Bueso (6):
  fs/epoll: remove max_nests argument from ep_call_nested()
  fs/epoll: simplify ep_send_events_proc() ready-list loop
  fs/epoll: drop ovflist branch prediction
  fs/epoll: robustify ep->mtx held checks
  fs/epoll: reduce the scope of wq lock in epoll_wait()
  fs/epoll: avoid barrier after an epoll_wait(2) timeout

 fs/eventpoll.c | 206 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 108 insertions(+), 98 deletions(-)

-- 
2.16.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] fs/epoll: remove max_nests argument from ep_call_nested()
  2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
@ 2018-11-08  5:10 ` Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 2/6] fs/epoll: simplify ep_send_events_proc() ready-list loop Davidlohr Bueso
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel, Davidlohr Bueso

All callers pass the EP_MAX_NESTS constant already, so lets
simplify this a tad and get rid of the redundant parameter
for nested eventpolls.

Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
---
 fs/eventpoll.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 42bbe6824b4b..7ad020f1353b 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -471,7 +471,6 @@ static inline void ep_set_busy_poll_napi_id(struct epitem *epi)
  *                  no re-entered.
  *
  * @ncalls: Pointer to the nested_calls structure to be used for this call.
- * @max_nests: Maximum number of allowed nesting calls.
  * @nproc: Nested call core function pointer.
  * @priv: Opaque data to be passed to the @nproc callback.
  * @cookie: Cookie to be used to identify this nested call.
@@ -480,7 +479,7 @@ static inline void ep_set_busy_poll_napi_id(struct epitem *epi)
  * Returns: Returns the code returned by the @nproc callback, or -1 if
  *          the maximum recursion limit has been exceeded.
  */
-static int ep_call_nested(struct nested_calls *ncalls, int max_nests,
+static int ep_call_nested(struct nested_calls *ncalls,
 			  int (*nproc)(void *, void *, int), void *priv,
 			  void *cookie, void *ctx)
 {
@@ -499,7 +498,7 @@ static int ep_call_nested(struct nested_calls *ncalls, int max_nests,
 	 */
 	list_for_each_entry(tncur, lsthead, llink) {
 		if (tncur->ctx == ctx &&
-		    (tncur->cookie == cookie || ++call_nests > max_nests)) {
+		    (tncur->cookie == cookie || ++call_nests > EP_MAX_NESTS)) {
 			/*
 			 * Ops ... loop detected or maximum nest level reached.
 			 * We abort this wake by breaking the cycle itself.
@@ -573,7 +572,7 @@ static void ep_poll_safewake(wait_queue_head_t *wq)
 {
 	int this_cpu = get_cpu();
 
-	ep_call_nested(&poll_safewake_ncalls, EP_MAX_NESTS,
+	ep_call_nested(&poll_safewake_ncalls,
 		       ep_poll_wakeup_proc, NULL, wq, (void *) (long) this_cpu);
 
 	put_cpu();
@@ -1333,7 +1332,6 @@ static int reverse_path_check_proc(void *priv, void *cookie, int call_nests)
 				}
 			} else {
 				error = ep_call_nested(&poll_loop_ncalls,
-							EP_MAX_NESTS,
 							reverse_path_check_proc,
 							child_file, child_file,
 							current);
@@ -1367,7 +1365,7 @@ static int reverse_path_check(void)
 	/* let's call this for all tfiles */
 	list_for_each_entry(current_file, &tfile_check_list, f_tfile_llink) {
 		path_count_init();
-		error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
+		error = ep_call_nested(&poll_loop_ncalls,
 					reverse_path_check_proc, current_file,
 					current_file, current);
 		if (error)
@@ -1876,7 +1874,7 @@ static int ep_loop_check_proc(void *priv, void *cookie, int call_nests)
 			ep_tovisit = epi->ffd.file->private_data;
 			if (ep_tovisit->visited)
 				continue;
-			error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
+			error = ep_call_nested(&poll_loop_ncalls,
 					ep_loop_check_proc, epi->ffd.file,
 					ep_tovisit, current);
 			if (error != 0)
@@ -1916,7 +1914,7 @@ static int ep_loop_check(struct eventpoll *ep, struct file *file)
 	int ret;
 	struct eventpoll *ep_cur, *ep_next;
 
-	ret = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS,
+	ret = ep_call_nested(&poll_loop_ncalls,
 			      ep_loop_check_proc, file, ep, current);
 	/* clear visited list */
 	list_for_each_entry_safe(ep_cur, ep_next, &visited_list,
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/6] fs/epoll: simplify ep_send_events_proc() ready-list loop
  2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 1/6] fs/epoll: remove max_nests argument from ep_call_nested() Davidlohr Bueso
@ 2018-11-08  5:10 ` Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 3/6] fs/epoll: drop ovflist branch prediction Davidlohr Bueso
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel, Davidlohr Bueso

The current logic is a bit convoluted. Lets simplify this with
a standard list_for_each_entry_safe() loop instead and just
break out after maxevents is reached.

While at it, remove an unnecessary indentation level in the loop
when there are in fact ready events.

Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
---
 fs/eventpoll.c | 73 +++++++++++++++++++++++++++++-----------------------------
 1 file changed, 37 insertions(+), 36 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 7ad020f1353b..101d46b81f64 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1624,21 +1624,22 @@ static __poll_t ep_send_events_proc(struct eventpoll *ep, struct list_head *head
 {
 	struct ep_send_events_data *esed = priv;
 	__poll_t revents;
-	struct epitem *epi;
-	struct epoll_event __user *uevent;
+	struct epitem *epi, *tmp;
+	struct epoll_event __user *uevent = esed->events;
 	struct wakeup_source *ws;
 	poll_table pt;
 
 	init_poll_funcptr(&pt, NULL);
+	esed->res = 0;
 
 	/*
 	 * We can loop without lock because we are passed a task private list.
 	 * Items cannot vanish during the loop because ep_scan_ready_list() is
 	 * holding "mtx" during this call.
 	 */
-	for (esed->res = 0, uevent = esed->events;
-	     !list_empty(head) && esed->res < esed->maxevents;) {
-		epi = list_first_entry(head, struct epitem, rdllink);
+	list_for_each_entry_safe(epi, tmp, head, rdllink) {
+		if (esed->res >= esed->maxevents)
+			break;
 
 		/*
 		 * Activate ep->ws before deactivating epi->ws to prevent
@@ -1658,42 +1659,42 @@ static __poll_t ep_send_events_proc(struct eventpoll *ep, struct list_head *head
 
 		list_del_init(&epi->rdllink);
 
-		revents = ep_item_poll(epi, &pt, 1);
-
 		/*
 		 * If the event mask intersect the caller-requested one,
 		 * deliver the event to userspace. Again, ep_scan_ready_list()
-		 * is holding "mtx", so no operations coming from userspace
+		 * is holding ep->mtx, so no operations coming from userspace
 		 * can change the item.
 		 */
-		if (revents) {
-			if (__put_user(revents, &uevent->events) ||
-			    __put_user(epi->event.data, &uevent->data)) {
-				list_add(&epi->rdllink, head);
-				ep_pm_stay_awake(epi);
-				if (!esed->res)
-					esed->res = -EFAULT;
-				return 0;
-			}
-			esed->res++;
-			uevent++;
-			if (epi->event.events & EPOLLONESHOT)
-				epi->event.events &= EP_PRIVATE_BITS;
-			else if (!(epi->event.events & EPOLLET)) {
-				/*
-				 * If this file has been added with Level
-				 * Trigger mode, we need to insert back inside
-				 * the ready list, so that the next call to
-				 * epoll_wait() will check again the events
-				 * availability. At this point, no one can insert
-				 * into ep->rdllist besides us. The epoll_ctl()
-				 * callers are locked out by
-				 * ep_scan_ready_list() holding "mtx" and the
-				 * poll callback will queue them in ep->ovflist.
-				 */
-				list_add_tail(&epi->rdllink, &ep->rdllist);
-				ep_pm_stay_awake(epi);
-			}
+		revents = ep_item_poll(epi, &pt, 1);
+		if (!revents)
+			continue;
+
+		if (__put_user(revents, &uevent->events) ||
+		    __put_user(epi->event.data, &uevent->data)) {
+			list_add(&epi->rdllink, head);
+			ep_pm_stay_awake(epi);
+			if (!esed->res)
+				esed->res = -EFAULT;
+			return 0;
+		}
+		esed->res++;
+		uevent++;
+		if (epi->event.events & EPOLLONESHOT)
+			epi->event.events &= EP_PRIVATE_BITS;
+		else if (!(epi->event.events & EPOLLET)) {
+			/*
+			 * If this file has been added with Level
+			 * Trigger mode, we need to insert back inside
+			 * the ready list, so that the next call to
+			 * epoll_wait() will check again the events
+			 * availability. At this point, no one can insert
+			 * into ep->rdllist besides us. The epoll_ctl()
+			 * callers are locked out by
+			 * ep_scan_ready_list() holding "mtx" and the
+			 * poll callback will queue them in ep->ovflist.
+			 */
+			list_add_tail(&epi->rdllink, &ep->rdllist);
+			ep_pm_stay_awake(epi);
 		}
 	}
 
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/6] fs/epoll: drop ovflist branch prediction
  2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 1/6] fs/epoll: remove max_nests argument from ep_call_nested() Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 2/6] fs/epoll: simplify ep_send_events_proc() ready-list loop Davidlohr Bueso
@ 2018-11-08  5:10 ` Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 4/6] fs/epoll: robustify ep->mtx held checks Davidlohr Bueso
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel, Davidlohr Bueso

The ep->ovflist is a secondary ready-list to temporarily store
events that might occur when doing sproc without holding the
ep->wq.lock. This accounts for every time we check for ready
events and also send events back to userspace; both callbacks,
particularly the later because of copy_to_user, can account
for a non-trivial time.

As such, the unlikely() check to see if the pointer is being
used, seems both misleading and sub-optimal. In fact, we go
to an awful lot of trouble to sync both lists, and populating
the ovflist is far from an uncommon scenario.

For example, profiling a concurrent epoll_wait(2) benchmark,
with CONFIG_PROFILE_ANNOTATED_BRANCHES shows that for a two threads
a 33% incorrect rate was seen; and when incrementally increasing
the number of epoll instances (which is used, for example for
multiple queuing load balancing models), up to a 90% incorrect
rate was seen.

Similarly, by deleting the prediction, 3% throughput boost was
seen across incremental threads.

Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
---
 fs/eventpoll.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 101d46b81f64..347da3f4f5d3 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1153,7 +1153,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
 	 * semantics). All the events that happen during that period of time are
 	 * chained in ep->ovflist and requeued later on.
 	 */
-	if (unlikely(ep->ovflist != EP_UNACTIVE_PTR)) {
+	if (ep->ovflist != EP_UNACTIVE_PTR) {
 		if (epi->next == EP_UNACTIVE_PTR) {
 			epi->next = ep->ovflist;
 			ep->ovflist = epi;
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/6] fs/epoll: robustify ep->mtx held checks
  2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
                   ` (2 preceding siblings ...)
  2018-11-08  5:10 ` [PATCH 3/6] fs/epoll: drop ovflist branch prediction Davidlohr Bueso
@ 2018-11-08  5:10 ` Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 5/6] fs/epoll: reduce the scope of wq lock in epoll_wait() Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 6/6] fs/epoll: avoid barrier after an epoll_wait(2) timeout Davidlohr Bueso
  5 siblings, 0 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel, Davidlohr Bueso

Insted of just commenting how important it is, lets make
it more robust and add a lockdep_assert_held() call.

Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
---
 fs/eventpoll.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 347da3f4f5d3..6a0c2591e57e 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1637,6 +1637,8 @@ static __poll_t ep_send_events_proc(struct eventpoll *ep, struct list_head *head
 	 * Items cannot vanish during the loop because ep_scan_ready_list() is
 	 * holding "mtx" during this call.
 	 */
+	lockdep_assert_held(&ep->mtx);
+
 	list_for_each_entry_safe(epi, tmp, head, rdllink) {
 		if (esed->res >= esed->maxevents)
 			break;
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/6] fs/epoll: reduce the scope of wq lock in epoll_wait()
  2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
                   ` (3 preceding siblings ...)
  2018-11-08  5:10 ` [PATCH 4/6] fs/epoll: robustify ep->mtx held checks Davidlohr Bueso
@ 2018-11-08  5:10 ` Davidlohr Bueso
  2018-11-09 15:52   ` Davidlohr Bueso
  2018-11-08  5:10 ` [PATCH 6/6] fs/epoll: avoid barrier after an epoll_wait(2) timeout Davidlohr Bueso
  5 siblings, 1 reply; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel, Davidlohr Bueso

This patch aims at reducing ep wq.lock hold times in epoll_wait(2).
For the blocking case, there is no need to constantly take and drop
the spinlock, which is only needed to manipulate the waitqueue.

The call to ep_events_available() is now lockless, and only exposed
to benign races. Here, if false positive (returns available events
and does not see another thread deleting an epi from the list) we call
into send_events and then the list's state is correctly seen. Otoh,
if a false negative and we don't see a list_add_tail(), for example,
from irq callback, then it is rechecked again before blocking, which
will see the correct state.

In order for more accuracy to see concurrent list_del_init(), use
the list_empty_careful() variant  -- of course, this won't the safe
against insertions from wakeup.

For the overflow list we obviously need to prevent load/store tearing
as we don't want to see partial values while the ready list is disabled.

Signed-off-by: Davidlohr Bueso <dbueso@suse.de>
---
 fs/eventpoll.c | 114 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 60 insertions(+), 54 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 6a0c2591e57e..f6c023f085f6 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -381,7 +381,8 @@ static void ep_nested_calls_init(struct nested_calls *ncalls)
  */
 static inline int ep_events_available(struct eventpoll *ep)
 {
-	return !list_empty(&ep->rdllist) || ep->ovflist != EP_UNACTIVE_PTR;
+	return !list_empty(&ep->rdllist) ||
+		READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR;
 }
 
 #ifdef CONFIG_NET_RX_BUSY_POLL
@@ -698,7 +699,7 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
 	 */
 	spin_lock_irq(&ep->wq.lock);
 	list_splice_init(&ep->rdllist, &txlist);
-	ep->ovflist = NULL;
+	WRITE_ONCE(ep->ovflist, NULL);
 	spin_unlock_irq(&ep->wq.lock);
 
 	/*
@@ -712,7 +713,7 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
 	 * other events might have been queued by the poll callback.
 	 * We re-insert them inside the main ready-list here.
 	 */
-	for (nepi = ep->ovflist; (epi = nepi) != NULL;
+	for (nepi = READ_ONCE(ep->ovflist); (epi = nepi) != NULL;
 	     nepi = epi->next, epi->next = EP_UNACTIVE_PTR) {
 		/*
 		 * We need to check if the item is already in the list.
@@ -730,7 +731,7 @@ static __poll_t ep_scan_ready_list(struct eventpoll *ep,
 	 * releasing the lock, events will be queued in the normal way inside
 	 * ep->rdllist.
 	 */
-	ep->ovflist = EP_UNACTIVE_PTR;
+	WRITE_ONCE(ep->ovflist, EP_UNACTIVE_PTR);
 
 	/*
 	 * Quickly re-inject items left on "txlist".
@@ -1153,10 +1154,10 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
 	 * semantics). All the events that happen during that period of time are
 	 * chained in ep->ovflist and requeued later on.
 	 */
-	if (ep->ovflist != EP_UNACTIVE_PTR) {
+	if (READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR) {
 		if (epi->next == EP_UNACTIVE_PTR) {
-			epi->next = ep->ovflist;
-			ep->ovflist = epi;
+			epi->next = READ_ONCE(ep->ovflist);
+			WRITE_ONCE(ep->ovflist, epi);
 			if (epi->ws) {
 				/*
 				 * Activate ep->ws since epi->ws may get
@@ -1762,10 +1763,17 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
 	} else if (timeout == 0) {
 		/*
 		 * Avoid the unnecessary trip to the wait queue loop, if the
-		 * caller specified a non blocking operation.
+		 * caller specified a non blocking operation. We still need
+		 * lock because we could race and not see an epi being added
+		 * to the ready list while in irq callback. Thus incorrectly
+		 * returning 0 back to userspace.
 		 */
 		timed_out = 1;
+
 		spin_lock_irq(&ep->wq.lock);
+		eavail = ep_events_available(ep);
+		spin_unlock_irq(&ep->wq.lock);
+
 		goto check_events;
 	}
 
@@ -1774,64 +1782,62 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
 	if (!ep_events_available(ep))
 		ep_busy_loop(ep, timed_out);
 
+	eavail = ep_events_available(ep);
+	if (eavail)
+		goto check_events;
+
+	/*
+	 * Busy poll timed out.  Drop NAPI ID for now, we can add
+	 * it back in when we have moved a socket with a valid NAPI
+	 * ID onto the ready list.
+	 */
+	ep_reset_busy_poll_napi_id(ep);
+
+	/*
+	 * We don't have any available event to return to the caller.
+	 * We need to sleep here, and we will be wake up by
+	 * ep_poll_callback() when events will become available.
+	 */
+	init_waitqueue_entry(&wait, current);
 	spin_lock_irq(&ep->wq.lock);
+	__add_wait_queue_exclusive(&ep->wq, &wait);
+	spin_unlock_irq(&ep->wq.lock);
 
-	if (!ep_events_available(ep)) {
+	for (;;) {
 		/*
-		 * Busy poll timed out.  Drop NAPI ID for now, we can add
-		 * it back in when we have moved a socket with a valid NAPI
-		 * ID onto the ready list.
+		 * We don't want to sleep if the ep_poll_callback() sends us
+		 * a wakeup in between. That's why we set the task state
+		 * to TASK_INTERRUPTIBLE before doing the checks.
 		 */
-		ep_reset_busy_poll_napi_id(ep);
-
+		set_current_state(TASK_INTERRUPTIBLE);
 		/*
-		 * We don't have any available event to return to the caller.
-		 * We need to sleep here, and we will be wake up by
-		 * ep_poll_callback() when events will become available.
+		 * Always short-circuit for fatal signals to allow
+		 * threads to make a timely exit without the chance of
+		 * finding more events available and fetching
+		 * repeatedly.
 		 */
-		init_waitqueue_entry(&wait, current);
-		__add_wait_queue_exclusive(&ep->wq, &wait);
-
-		for (;;) {
-			/*
-			 * We don't want to sleep if the ep_poll_callback() sends us
-			 * a wakeup in between. That's why we set the task state
-			 * to TASK_INTERRUPTIBLE before doing the checks.
-			 */
-			set_current_state(TASK_INTERRUPTIBLE);
-			/*
-			 * Always short-circuit for fatal signals to allow
-			 * threads to make a timely exit without the chance of
-			 * finding more events available and fetching
-			 * repeatedly.
-			 */
-			if (fatal_signal_pending(current)) {
-				res = -EINTR;
-				break;
-			}
-			if (ep_events_available(ep) || timed_out)
-				break;
-			if (signal_pending(current)) {
-				res = -EINTR;
-				break;
-			}
-
-			spin_unlock_irq(&ep->wq.lock);
-			if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
-				timed_out = 1;
-
-			spin_lock_irq(&ep->wq.lock);
+		if (fatal_signal_pending(current)) {
+			res = -EINTR;
+			break;
+		}
+		if (ep_events_available(ep) || timed_out)
+			break;
+		if (signal_pending(current)) {
+			res = -EINTR;
+			break;
 		}
 
-		__remove_wait_queue(&ep->wq, &wait);
-		__set_current_state(TASK_RUNNING);
+		if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
+			timed_out = 1;
 	}
-check_events:
-	/* Is it worth to try to dig for events ? */
-	eavail = ep_events_available(ep);
 
+	__set_current_state(TASK_RUNNING);
+
+	spin_lock_irq(&ep->wq.lock);
+	__remove_wait_queue(&ep->wq, &wait);
 	spin_unlock_irq(&ep->wq.lock);
 
+check_events:
 	/*
 	 * Try to transfer events to user space. In case we get 0 events and
 	 * there's still timeout left over, we go trying again in search of
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/6] fs/epoll: avoid barrier after an epoll_wait(2) timeout
  2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
                   ` (4 preceding siblings ...)
  2018-11-08  5:10 ` [PATCH 5/6] fs/epoll: reduce the scope of wq lock in epoll_wait() Davidlohr Bueso
@ 2018-11-08  5:10 ` Davidlohr Bueso
  5 siblings, 0 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-08  5:10 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, dave, linux-fsdevel, linux-kernel

Upon timeout, we can just exit out of the loop, without
the cost of the changing the task's state smp_store_mb
call. Just exit out of the loop and be done - setting
the task state afterwards will be, of course, redundant.

Signed-off-by: Davidlohr Bueso <dave@stgolabs.net>
---
 fs/eventpoll.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index f6c023f085f6..ec14e5bcdaa9 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1820,15 +1820,18 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
 			res = -EINTR;
 			break;
 		}
-		if (ep_events_available(ep) || timed_out)
+		
+		if (ep_events_available(ep))
 			break;
 		if (signal_pending(current)) {
 			res = -EINTR;
 			break;
 		}
 
-		if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS))
+		if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) {
 			timed_out = 1;
+			break;
+		}
 	}
 
 	__set_current_state(TASK_RUNNING);
-- 
2.16.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 5/6] fs/epoll: reduce the scope of wq lock in epoll_wait()
  2018-11-08  5:10 ` [PATCH 5/6] fs/epoll: reduce the scope of wq lock in epoll_wait() Davidlohr Bueso
@ 2018-11-09 15:52   ` Davidlohr Bueso
  0 siblings, 0 replies; 8+ messages in thread
From: Davidlohr Bueso @ 2018-11-09 15:52 UTC (permalink / raw)
  To: akpm; +Cc: jbaron, viro, linux-fsdevel, linux-kernel, Davidlohr Bueso

Sorry, I just realized I forgot these fixlets when sending the v1.

diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index ec14e5bcdaa9..c37729658c03 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -381,7 +381,7 @@ static void ep_nested_calls_init(struct nested_calls *ncalls)
  */
 static inline int ep_events_available(struct eventpoll *ep)
 {
-	return !list_empty(&ep->rdllist) ||
+	return !list_empty_careful(&ep->rdllist) ||
 		READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR;
 }
 
@@ -1820,8 +1820,9 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
 			res = -EINTR;
 			break;
 		}
-		
-		if (ep_events_available(ep))
+
+		eavail = ep_events_available(ep);
+		if (eavail)
 			break;
 		if (signal_pending(current)) {
 			res = -EINTR;

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-11-10  1:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-08  5:10 [PATCH -next 0/6] epoll: some miscellaneous optimizations Davidlohr Bueso
2018-11-08  5:10 ` [PATCH 1/6] fs/epoll: remove max_nests argument from ep_call_nested() Davidlohr Bueso
2018-11-08  5:10 ` [PATCH 2/6] fs/epoll: simplify ep_send_events_proc() ready-list loop Davidlohr Bueso
2018-11-08  5:10 ` [PATCH 3/6] fs/epoll: drop ovflist branch prediction Davidlohr Bueso
2018-11-08  5:10 ` [PATCH 4/6] fs/epoll: robustify ep->mtx held checks Davidlohr Bueso
2018-11-08  5:10 ` [PATCH 5/6] fs/epoll: reduce the scope of wq lock in epoll_wait() Davidlohr Bueso
2018-11-09 15:52   ` Davidlohr Bueso
2018-11-08  5:10 ` [PATCH 6/6] fs/epoll: avoid barrier after an epoll_wait(2) timeout Davidlohr Bueso

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).