aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorJeff Layton <jlayton@primarydata.com>2014-11-21 14:19:30 -0500
committerJ. Bruce Fields <bfields@redhat.com>2014-12-09 11:22:22 -0500
commitb1691bc03d4eddb959234409167bef9be9e62d74 (patch)
tree2ccfca41ffcffc69a3bacc0bb1e9e5754595a0e6 /include
parent403c7b44441d60aba7f8a134c31279ffa60ea769 (diff)
sunrpc: convert to lockless lookup of queued server threads
Testing has shown that the pool->sp_lock can be a bottleneck on a busy server. Every time data is received on a socket, the server must take that lock in order to dequeue a thread from the sp_threads list. Address this problem by eliminating the sp_threads list (which contains threads that are currently idle) and replacing it with a RQ_BUSY flag in svc_rqst. This allows us to walk the sp_all_threads list under the rcu_read_lock and find a suitable thread for the xprt by doing a test_and_set_bit. Note that we do still have a potential atomicity problem however with this approach. We don't want svc_xprt_do_enqueue to set the rqst->rq_xprt pointer unless a test_and_set_bit of RQ_BUSY returned zero (which indicates that the thread was idle). But, by the time we check that, the bit could be flipped by a waking thread. To address this, we acquire a new per-rqst spinlock (rq_lock) and take that before doing the test_and_set_bit. If that returns false, then we can set rq_xprt and drop the spinlock. Then, when the thread wakes up, it must set the bit under the same spinlock and can trust that if it was already set then the rq_xprt is also properly set. With this scheme, the case where we have an idle thread no longer needs to take the highly contended pool->sp_lock at all, and that removes the bottleneck. That still leaves one issue: What of the case where we walk the whole sp_all_threads list and don't find an idle thread? Because the search is lockess, it's possible for the queueing to race with a thread that is going to sleep. To address that, we queue the xprt and then search again. If we find an idle thread at that point, we can't attach the xprt to it directly since that might race with a different thread waking up and finding it. All we can do is wake the idle thread back up and let it attempt to find the now-queued xprt. Signed-off-by: Jeff Layton <jlayton@primarydata.com> Tested-by: Chris Worley <chris.worley@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'include')
-rw-r--r--include/linux/sunrpc/svc.h4
-rw-r--r--include/trace/events/sunrpc.h3
2 files changed, 4 insertions, 3 deletions
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 513957eba0a5..6f22cfeef5e3 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -45,7 +45,6 @@ struct svc_pool_stats {
45struct svc_pool { 45struct svc_pool {
46 unsigned int sp_id; /* pool id; also node id on NUMA */ 46 unsigned int sp_id; /* pool id; also node id on NUMA */
47 spinlock_t sp_lock; /* protects all fields */ 47 spinlock_t sp_lock; /* protects all fields */
48 struct list_head sp_threads; /* idle server threads */
49 struct list_head sp_sockets; /* pending sockets */ 48 struct list_head sp_sockets; /* pending sockets */
50 unsigned int sp_nrthreads; /* # of threads in pool */ 49 unsigned int sp_nrthreads; /* # of threads in pool */
51 struct list_head sp_all_threads; /* all server threads */ 50 struct list_head sp_all_threads; /* all server threads */
@@ -221,7 +220,6 @@ static inline void svc_putu32(struct kvec *iov, __be32 val)
221 * processed. 220 * processed.
222 */ 221 */
223struct svc_rqst { 222struct svc_rqst {
224 struct list_head rq_list; /* idle list */
225 struct list_head rq_all; /* all threads list */ 223 struct list_head rq_all; /* all threads list */
226 struct rcu_head rq_rcu_head; /* for RCU deferred kfree */ 224 struct rcu_head rq_rcu_head; /* for RCU deferred kfree */
227 struct svc_xprt * rq_xprt; /* transport ptr */ 225 struct svc_xprt * rq_xprt; /* transport ptr */
@@ -264,6 +262,7 @@ struct svc_rqst {
264 * to prevent encrypting page 262 * to prevent encrypting page
265 * cache pages */ 263 * cache pages */
266#define RQ_VICTIM (5) /* about to be shut down */ 264#define RQ_VICTIM (5) /* about to be shut down */
265#define RQ_BUSY (6) /* request is busy */
267 unsigned long rq_flags; /* flags field */ 266 unsigned long rq_flags; /* flags field */
268 267
269 void * rq_argp; /* decoded arguments */ 268 void * rq_argp; /* decoded arguments */
@@ -285,6 +284,7 @@ struct svc_rqst {
285 struct auth_domain * rq_gssclient; /* "gss/"-style peer info */ 284 struct auth_domain * rq_gssclient; /* "gss/"-style peer info */
286 struct svc_cacherep * rq_cacherep; /* cache info */ 285 struct svc_cacherep * rq_cacherep; /* cache info */
287 struct task_struct *rq_task; /* service thread */ 286 struct task_struct *rq_task; /* service thread */
287 spinlock_t rq_lock; /* per-request lock */
288}; 288};
289 289
290#define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net) 290#define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net)
diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h
index 08a5fed50f34..ee4438a63a48 100644
--- a/include/trace/events/sunrpc.h
+++ b/include/trace/events/sunrpc.h
@@ -419,7 +419,8 @@ TRACE_EVENT(xs_tcp_data_recv,
419 { (1UL << RQ_USEDEFERRAL), "RQ_USEDEFERRAL"}, \ 419 { (1UL << RQ_USEDEFERRAL), "RQ_USEDEFERRAL"}, \
420 { (1UL << RQ_DROPME), "RQ_DROPME"}, \ 420 { (1UL << RQ_DROPME), "RQ_DROPME"}, \
421 { (1UL << RQ_SPLICE_OK), "RQ_SPLICE_OK"}, \ 421 { (1UL << RQ_SPLICE_OK), "RQ_SPLICE_OK"}, \
422 { (1UL << RQ_VICTIM), "RQ_VICTIM"}) 422 { (1UL << RQ_VICTIM), "RQ_VICTIM"}, \
423 { (1UL << RQ_BUSY), "RQ_BUSY"})
423 424
424TRACE_EVENT(svc_recv, 425TRACE_EVENT(svc_recv,
425 TP_PROTO(struct svc_rqst *rqst, int status), 426 TP_PROTO(struct svc_rqst *rqst, int status),