Commit c081a0c7 authored by Greg Banks's avatar Greg Banks Committed by Linus Torvalds

[PATCH] knfsd: test and set SK_BUSY atomically

The SK_BUSY bit in svc_sock->sk_flags ensures that we do not attempt to
enqueue a socket twice.  Currently, setting and clearing the bit is protected
by svc_serv->sv_lock.  As I intend to reduce the data that the lock protects
so it's not held when svc_sock_enqueue() tests and sets SK_BUSY, that test and
set needs to be atomic.
Signed-off-by: default avatarGreg Banks <gnb@melbourne.sgi.com>
Signed-off-by: default avatarNeil Brown <neilb@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5685f0fa
...@@ -46,14 +46,13 @@ ...@@ -46,14 +46,13 @@
/* SMP locking strategy: /* SMP locking strategy:
* *
* svc_serv->sv_lock protects most stuff for that service. * svc_serv->sv_lock protects most stuff for that service.
* svc_sock->sk_defer_lock protects the svc_sock->sk_deferred list * svc_sock->sk_defer_lock protects the svc_sock->sk_deferred list
* svc_sock->sk_flags.SK_BUSY prevents a svc_sock being enqueued multiply.
* *
* Some flags can be set to certain values at any time * Some flags can be set to certain values at any time
* providing that certain rules are followed: * providing that certain rules are followed:
* *
* SK_BUSY can be set to 0 at any time.
* svc_sock_enqueue must be called afterwards
* SK_CONN, SK_DATA, can be set or cleared at any time. * SK_CONN, SK_DATA, can be set or cleared at any time.
* after a set, svc_sock_enqueue must be called. * after a set, svc_sock_enqueue must be called.
* after a clear, the socket must be read/accepted * after a clear, the socket must be read/accepted
...@@ -170,8 +169,13 @@ svc_sock_enqueue(struct svc_sock *svsk) ...@@ -170,8 +169,13 @@ svc_sock_enqueue(struct svc_sock *svsk)
goto out_unlock; goto out_unlock;
} }
if (test_bit(SK_BUSY, &svsk->sk_flags)) { /* Mark socket as busy. It will remain in this state until the
/* Don't enqueue socket while daemon is receiving */ * server has processed all pending data and put the socket back
* on the idle list. We update SK_BUSY atomically because
* it also guards against trying to enqueue the svc_sock twice.
*/
if (test_and_set_bit(SK_BUSY, &svsk->sk_flags)) {
/* Don't enqueue socket while already enqueued */
dprintk("svc: socket %p busy, not enqueued\n", svsk->sk_sk); dprintk("svc: socket %p busy, not enqueued\n", svsk->sk_sk);
goto out_unlock; goto out_unlock;
} }
...@@ -185,15 +189,11 @@ svc_sock_enqueue(struct svc_sock *svsk) ...@@ -185,15 +189,11 @@ svc_sock_enqueue(struct svc_sock *svsk)
dprintk("svc: socket %p no space, %d*2 > %ld, not enqueued\n", dprintk("svc: socket %p no space, %d*2 > %ld, not enqueued\n",
svsk->sk_sk, atomic_read(&svsk->sk_reserved)+serv->sv_bufsz, svsk->sk_sk, atomic_read(&svsk->sk_reserved)+serv->sv_bufsz,
svc_sock_wspace(svsk)); svc_sock_wspace(svsk));
clear_bit(SK_BUSY, &svsk->sk_flags);
goto out_unlock; goto out_unlock;
} }
clear_bit(SOCK_NOSPACE, &svsk->sk_sock->flags); clear_bit(SOCK_NOSPACE, &svsk->sk_sock->flags);
/* Mark socket as busy. It will remain in this state until the
* server has processed all pending data and put the socket back
* on the idle list.
*/
set_bit(SK_BUSY, &svsk->sk_flags);
if (!list_empty(&serv->sv_threads)) { if (!list_empty(&serv->sv_threads)) {
rqstp = list_entry(serv->sv_threads.next, rqstp = list_entry(serv->sv_threads.next,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment