[patch 21/21] knfsd: Fix a race in closing NFSd connections.

2007-02-20 Thread Greg KH
-stable review patch.  If anyone has any objections, please let us know.

--
If you lose this race, it can iput a socket inode twice and you
get a BUG in fs/inode.c

When I added the option for user-space to close a socket,
I added some cruft to svc_delete_socket so that I could call
that function when closing a socket per user-space request.

This was the wrong thing to do.  I should have just set SK_CLOSE
and let normal mechanisms do the work.

Not only wrong, but buggy.  The locking is all wrong and it openned
up a race where-by a socket could be closed twice.

So this patch:
  Introduces svc_close_socket which sets SK_CLOSE then either leave
  the close up to a thread, or calls svc_delete_socket if it can
  get SK_BUSY.

  Adds a bias to sk_busy which is removed when SK_DEAD is set,
  This avoid races around shutting down the socket.

  Changes several 'spin_lock' to 'spin_lock_bh' where the _bh 
  was missing.

Bugzilla-url: http://bugzilla.kernel.org/show_bug.cgi?id=7916

Signed-off-by: Neil Brown <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>


---
 include/linux/sunrpc/svcsock.h |2 -
 net/sunrpc/svc.c   |4 +--
 net/sunrpc/svcsock.c   |   52 +
 3 files changed, 41 insertions(+), 17 deletions(-)

--- linux-2.6.19.4.orig/include/linux/sunrpc/svcsock.h
+++ linux-2.6.19.4/include/linux/sunrpc/svcsock.h
@@ -63,7 +63,7 @@ struct svc_sock {
  * Function prototypes.
  */
 intsvc_makesock(struct svc_serv *, int, unsigned short);
-void   svc_delete_socket(struct svc_sock *);
+void   svc_close_socket(struct svc_sock *);
 intsvc_recv(struct svc_rqst *, long);
 intsvc_send(struct svc_rqst *);
 void   svc_drop(struct svc_rqst *);
--- linux-2.6.19.4.orig/net/sunrpc/svc.c
+++ linux-2.6.19.4/net/sunrpc/svc.c
@@ -387,7 +387,7 @@ svc_destroy(struct svc_serv *serv)
svsk = list_entry(serv->sv_tempsocks.next,
  struct svc_sock,
  sk_list);
-   svc_delete_socket(svsk);
+   svc_close_socket(svsk);
}
if (serv->sv_shutdown)
serv->sv_shutdown(serv);
@@ -396,7 +396,7 @@ svc_destroy(struct svc_serv *serv)
svsk = list_entry(serv->sv_permsocks.next,
  struct svc_sock,
  sk_list);
-   svc_delete_socket(svsk);
+   svc_close_socket(svsk);
}

cache_clean_deferred(serv);
--- linux-2.6.19.4.orig/net/sunrpc/svcsock.c
+++ linux-2.6.19.4/net/sunrpc/svcsock.c
@@ -61,6 +61,12 @@
  * after a clear, the socket must be read/accepted
  *  if this succeeds, it must be set again.
  * SK_CLOSE can set at any time. It is never cleared.
+ *  sk_inuse contains a bias of '1' until SK_DEAD is set.
+ * so when sk_inuse hits zero, we know the socket is dead
+ * and no-one is using it.
+ *  SK_DEAD can only be set while SK_BUSY is held which ensures
+ * no other thread will be using the socket or will try to
+ *set SK_DEAD.
  *
  */
 
@@ -69,6 +75,7 @@
 
 static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
 int *errp, int pmap_reg);
+static voidsvc_delete_socket(struct svc_sock *svsk);
 static voidsvc_udp_data_ready(struct sock *, int);
 static int svc_udp_recvfrom(struct svc_rqst *);
 static int svc_udp_sendto(struct svc_rqst *);
@@ -299,8 +306,9 @@ void svc_reserve(struct svc_rqst *rqstp,
 static inline void
 svc_sock_put(struct svc_sock *svsk)
 {
-   if (atomic_dec_and_test(>sk_inuse) &&
-   test_bit(SK_DEAD, >sk_flags)) {
+   if (atomic_dec_and_test(>sk_inuse)) {
+   BUG_ON(! test_bit(SK_DEAD, >sk_flags));
+
dprintk("svc: releasing dead socket\n");
if (svsk->sk_sock->file)
sockfd_put(svsk->sk_sock);
@@ -490,7 +498,7 @@ svc_sock_names(char *buf, struct svc_ser
 
if (!serv)
return 0;
-   spin_lock(>sv_lock);
+   spin_lock_bh(>sv_lock);
list_for_each_entry(svsk, >sv_permsocks, sk_list) {
int onelen = one_sock_name(buf+len, svsk);
if (toclose && strcmp(toclose, buf+len) == 0)
@@ -498,12 +506,12 @@ svc_sock_names(char *buf, struct svc_ser
else
len += onelen;
}
-   spin_unlock(>sv_lock);
+   spin_unlock_bh(>sv_lock);
if (closesk)
/* Should unregister with portmap, but you cannot
 * unregister just one protocol...
 */
-   svc_delete_socket(closesk);
+   svc_close_socket(closesk);
else if (toclose)
return -ENOENT;
   

[patch 21/21] knfsd: Fix a race in closing NFSd connections.

2007-02-20 Thread Greg KH
-stable review patch.  If anyone has any objections, please let us know.

--
If you lose this race, it can iput a socket inode twice and you
get a BUG in fs/inode.c

When I added the option for user-space to close a socket,
I added some cruft to svc_delete_socket so that I could call
that function when closing a socket per user-space request.

This was the wrong thing to do.  I should have just set SK_CLOSE
and let normal mechanisms do the work.

Not only wrong, but buggy.  The locking is all wrong and it openned
up a race where-by a socket could be closed twice.

So this patch:
  Introduces svc_close_socket which sets SK_CLOSE then either leave
  the close up to a thread, or calls svc_delete_socket if it can
  get SK_BUSY.

  Adds a bias to sk_busy which is removed when SK_DEAD is set,
  This avoid races around shutting down the socket.

  Changes several 'spin_lock' to 'spin_lock_bh' where the _bh 
  was missing.

Bugzilla-url: http://bugzilla.kernel.org/show_bug.cgi?id=7916

Signed-off-by: Neil Brown [EMAIL PROTECTED]
Signed-off-by: Greg Kroah-Hartman [EMAIL PROTECTED]


---
 include/linux/sunrpc/svcsock.h |2 -
 net/sunrpc/svc.c   |4 +--
 net/sunrpc/svcsock.c   |   52 +
 3 files changed, 41 insertions(+), 17 deletions(-)

--- linux-2.6.19.4.orig/include/linux/sunrpc/svcsock.h
+++ linux-2.6.19.4/include/linux/sunrpc/svcsock.h
@@ -63,7 +63,7 @@ struct svc_sock {
  * Function prototypes.
  */
 intsvc_makesock(struct svc_serv *, int, unsigned short);
-void   svc_delete_socket(struct svc_sock *);
+void   svc_close_socket(struct svc_sock *);
 intsvc_recv(struct svc_rqst *, long);
 intsvc_send(struct svc_rqst *);
 void   svc_drop(struct svc_rqst *);
--- linux-2.6.19.4.orig/net/sunrpc/svc.c
+++ linux-2.6.19.4/net/sunrpc/svc.c
@@ -387,7 +387,7 @@ svc_destroy(struct svc_serv *serv)
svsk = list_entry(serv-sv_tempsocks.next,
  struct svc_sock,
  sk_list);
-   svc_delete_socket(svsk);
+   svc_close_socket(svsk);
}
if (serv-sv_shutdown)
serv-sv_shutdown(serv);
@@ -396,7 +396,7 @@ svc_destroy(struct svc_serv *serv)
svsk = list_entry(serv-sv_permsocks.next,
  struct svc_sock,
  sk_list);
-   svc_delete_socket(svsk);
+   svc_close_socket(svsk);
}

cache_clean_deferred(serv);
--- linux-2.6.19.4.orig/net/sunrpc/svcsock.c
+++ linux-2.6.19.4/net/sunrpc/svcsock.c
@@ -61,6 +61,12 @@
  * after a clear, the socket must be read/accepted
  *  if this succeeds, it must be set again.
  * SK_CLOSE can set at any time. It is never cleared.
+ *  sk_inuse contains a bias of '1' until SK_DEAD is set.
+ * so when sk_inuse hits zero, we know the socket is dead
+ * and no-one is using it.
+ *  SK_DEAD can only be set while SK_BUSY is held which ensures
+ * no other thread will be using the socket or will try to
+ *set SK_DEAD.
  *
  */
 
@@ -69,6 +75,7 @@
 
 static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
 int *errp, int pmap_reg);
+static voidsvc_delete_socket(struct svc_sock *svsk);
 static voidsvc_udp_data_ready(struct sock *, int);
 static int svc_udp_recvfrom(struct svc_rqst *);
 static int svc_udp_sendto(struct svc_rqst *);
@@ -299,8 +306,9 @@ void svc_reserve(struct svc_rqst *rqstp,
 static inline void
 svc_sock_put(struct svc_sock *svsk)
 {
-   if (atomic_dec_and_test(svsk-sk_inuse) 
-   test_bit(SK_DEAD, svsk-sk_flags)) {
+   if (atomic_dec_and_test(svsk-sk_inuse)) {
+   BUG_ON(! test_bit(SK_DEAD, svsk-sk_flags));
+
dprintk(svc: releasing dead socket\n);
if (svsk-sk_sock-file)
sockfd_put(svsk-sk_sock);
@@ -490,7 +498,7 @@ svc_sock_names(char *buf, struct svc_ser
 
if (!serv)
return 0;
-   spin_lock(serv-sv_lock);
+   spin_lock_bh(serv-sv_lock);
list_for_each_entry(svsk, serv-sv_permsocks, sk_list) {
int onelen = one_sock_name(buf+len, svsk);
if (toclose  strcmp(toclose, buf+len) == 0)
@@ -498,12 +506,12 @@ svc_sock_names(char *buf, struct svc_ser
else
len += onelen;
}
-   spin_unlock(serv-sv_lock);
+   spin_unlock_bh(serv-sv_lock);
if (closesk)
/* Should unregister with portmap, but you cannot
 * unregister just one protocol...
 */
-   svc_delete_socket(closesk);
+   svc_close_socket(closesk);
else if (toclose)