]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.0 patches
authorGreg Kroah-Hartman <gregkh@suse.de>
Tue, 2 Aug 2011 18:44:07 +0000 (11:44 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 2 Aug 2011 18:44:07 +0000 (11:44 -0700)
13 files changed:
queue-3.0/ecryptfs-make-inode-bdi-consistent-with-superblock-bdi.patch [new file with mode: 0644]
queue-3.0/ecryptfs-unlock-keys-needed-by-ecryptfsd.patch [new file with mode: 0644]
queue-3.0/ehci-fix-direction-handling-for-interrupt-data-toggles.patch [new file with mode: 0644]
queue-3.0/ehci-only-power-off-port-if-over-current-is-active.patch [new file with mode: 0644]
queue-3.0/n_gsm-fix-the-wrong-fcs-handling.patch [new file with mode: 0644]
queue-3.0/nfs-fix-spurious-readdir-cookie-loop-messages.patch [new file with mode: 0644]
queue-3.0/nfsd-don-t-break-lease-on-claim_delegate_cur.patch [new file with mode: 0644]
queue-3.0/nfsd4-fix-file-leak-on-open_downgrade.patch [new file with mode: 0644]
queue-3.0/nfsd4-remember-to-put-rw-access-on-stateid-destruction.patch [new file with mode: 0644]
queue-3.0/nfsv4-don-t-use-the-delegation-inode-in.patch [new file with mode: 0644]
queue-3.0/proc-fix-a-race-in-do_io_accounting.patch [new file with mode: 0644]
queue-3.0/series
queue-3.0/svcrpc-fix-list-corrupting-race-on-nfsd-shutdown.patch [new file with mode: 0644]

diff --git a/queue-3.0/ecryptfs-make-inode-bdi-consistent-with-superblock-bdi.patch b/queue-3.0/ecryptfs-make-inode-bdi-consistent-with-superblock-bdi.patch
new file mode 100644 (file)
index 0000000..0f4039d
--- /dev/null
@@ -0,0 +1,30 @@
+From 985ca0e626e195ea08a1a82b8dbeb6719747429a Mon Sep 17 00:00:00 2001
+From: Thieu Le <thieule@chromium.org>
+Date: Tue, 26 Jul 2011 16:15:10 -0700
+Subject: ecryptfs: Make inode bdi consistent with superblock bdi
+
+From: Thieu Le <thieule@chromium.org>
+
+commit 985ca0e626e195ea08a1a82b8dbeb6719747429a upstream.
+
+Make the inode mapping bdi consistent with the superblock bdi so that
+dirty pages are flushed properly.
+
+Signed-off-by: Thieu Le <thieule@chromium.org>
+Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/ecryptfs/inode.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/fs/ecryptfs/inode.c
++++ b/fs/ecryptfs/inode.c
+@@ -69,6 +69,7 @@ static int ecryptfs_inode_set(struct ino
+       inode->i_ino = lower_inode->i_ino;
+       inode->i_version++;
+       inode->i_mapping->a_ops = &ecryptfs_aops;
++      inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi;
+       if (S_ISLNK(inode->i_mode))
+               inode->i_op = &ecryptfs_symlink_iops;
diff --git a/queue-3.0/ecryptfs-unlock-keys-needed-by-ecryptfsd.patch b/queue-3.0/ecryptfs-unlock-keys-needed-by-ecryptfsd.patch
new file mode 100644 (file)
index 0000000..f99f0ff
--- /dev/null
@@ -0,0 +1,180 @@
+From b2987a5e05ec7a1af7ca42e5d5349d7a22753031 Mon Sep 17 00:00:00 2001
+From: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+Date: Tue, 26 Jul 2011 19:47:08 -0500
+Subject: eCryptfs: Unlock keys needed by ecryptfsd
+
+From: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+
+commit b2987a5e05ec7a1af7ca42e5d5349d7a22753031 upstream.
+
+Fixes a regression caused by b5695d04634fa4ccca7dcbc05bb4a66522f02e0b
+
+Kernel keyring keys containing eCryptfs authentication tokens should not
+be write locked when calling out to ecryptfsd to wrap and unwrap file
+encryption keys. The eCryptfs kernel code can not hold the key's write
+lock because ecryptfsd needs to request the key after receiving such a
+request from the kernel.
+
+Without this fix, all file opens and creates will timeout and fail when
+using the eCryptfs PKI infrastructure. This is not an issue when using
+passphrase-based mount keys, which is the most widely deployed eCryptfs
+configuration.
+
+Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
+Acked-by: Roberto Sassu <roberto.sassu@polito.it>
+Tested-by: Roberto Sassu <roberto.sassu@polito.it>
+Tested-by: Alexis Hafner1 <haf@zurich.ibm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/ecryptfs/keystore.c |   47 +++++++++++++++++++++++++----------------------
+ 1 file changed, 25 insertions(+), 22 deletions(-)
+
+--- a/fs/ecryptfs/keystore.c
++++ b/fs/ecryptfs/keystore.c
+@@ -1868,11 +1868,6 @@ int ecryptfs_parse_packet_set(struct ecr
+        * just one will be sufficient to decrypt to get the FEK. */
+ find_next_matching_auth_tok:
+       found_auth_tok = 0;
+-      if (auth_tok_key) {
+-              up_write(&(auth_tok_key->sem));
+-              key_put(auth_tok_key);
+-              auth_tok_key = NULL;
+-      }
+       list_for_each_entry(auth_tok_list_item, &auth_tok_list, list) {
+               candidate_auth_tok = &auth_tok_list_item->auth_tok;
+               if (unlikely(ecryptfs_verbosity > 0)) {
+@@ -1909,14 +1904,22 @@ found_matching_auth_tok:
+               memcpy(&(candidate_auth_tok->token.private_key),
+                      &(matching_auth_tok->token.private_key),
+                      sizeof(struct ecryptfs_private_key));
++              up_write(&(auth_tok_key->sem));
++              key_put(auth_tok_key);
+               rc = decrypt_pki_encrypted_session_key(candidate_auth_tok,
+                                                      crypt_stat);
+       } else if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD) {
+               memcpy(&(candidate_auth_tok->token.password),
+                      &(matching_auth_tok->token.password),
+                      sizeof(struct ecryptfs_password));
++              up_write(&(auth_tok_key->sem));
++              key_put(auth_tok_key);
+               rc = decrypt_passphrase_encrypted_session_key(
+                       candidate_auth_tok, crypt_stat);
++      } else {
++              up_write(&(auth_tok_key->sem));
++              key_put(auth_tok_key);
++              rc = -EINVAL;
+       }
+       if (rc) {
+               struct ecryptfs_auth_tok_list_item *auth_tok_list_item_tmp;
+@@ -1956,15 +1959,12 @@ found_matching_auth_tok:
+ out_wipe_list:
+       wipe_auth_tok_list(&auth_tok_list);
+ out:
+-      if (auth_tok_key) {
+-              up_write(&(auth_tok_key->sem));
+-              key_put(auth_tok_key);
+-      }
+       return rc;
+ }
+ static int
+-pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok,
++pki_encrypt_session_key(struct key *auth_tok_key,
++                      struct ecryptfs_auth_tok *auth_tok,
+                       struct ecryptfs_crypt_stat *crypt_stat,
+                       struct ecryptfs_key_record *key_rec)
+ {
+@@ -1979,6 +1979,8 @@ pki_encrypt_session_key(struct ecryptfs_
+                                        crypt_stat->cipher,
+                                        crypt_stat->key_size),
+                                crypt_stat, &payload, &payload_len);
++      up_write(&(auth_tok_key->sem));
++      key_put(auth_tok_key);
+       if (rc) {
+               ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n");
+               goto out;
+@@ -2008,6 +2010,8 @@ out:
+  * write_tag_1_packet - Write an RFC2440-compatible tag 1 (public key) packet
+  * @dest: Buffer into which to write the packet
+  * @remaining_bytes: Maximum number of bytes that can be writtn
++ * @auth_tok_key: The authentication token key to unlock and put when done with
++ *                @auth_tok
+  * @auth_tok: The authentication token used for generating the tag 1 packet
+  * @crypt_stat: The cryptographic context
+  * @key_rec: The key record struct for the tag 1 packet
+@@ -2018,7 +2022,7 @@ out:
+  */
+ static int
+ write_tag_1_packet(char *dest, size_t *remaining_bytes,
+-                 struct ecryptfs_auth_tok *auth_tok,
++                 struct key *auth_tok_key, struct ecryptfs_auth_tok *auth_tok,
+                  struct ecryptfs_crypt_stat *crypt_stat,
+                  struct ecryptfs_key_record *key_rec, size_t *packet_size)
+ {
+@@ -2039,12 +2043,15 @@ write_tag_1_packet(char *dest, size_t *r
+               memcpy(key_rec->enc_key,
+                      auth_tok->session_key.encrypted_key,
+                      auth_tok->session_key.encrypted_key_size);
++              up_write(&(auth_tok_key->sem));
++              key_put(auth_tok_key);
+               goto encrypted_session_key_set;
+       }
+       if (auth_tok->session_key.encrypted_key_size == 0)
+               auth_tok->session_key.encrypted_key_size =
+                       auth_tok->token.private_key.key_size;
+-      rc = pki_encrypt_session_key(auth_tok, crypt_stat, key_rec);
++      rc = pki_encrypt_session_key(auth_tok_key, auth_tok, crypt_stat,
++                                   key_rec);
+       if (rc) {
+               printk(KERN_ERR "Failed to encrypt session key via a key "
+                      "module; rc = [%d]\n", rc);
+@@ -2421,6 +2428,8 @@ ecryptfs_generate_key_packet_set(char *d
+                                               &max, auth_tok,
+                                               crypt_stat, key_rec,
+                                               &written);
++                      up_write(&(auth_tok_key->sem));
++                      key_put(auth_tok_key);
+                       if (rc) {
+                               ecryptfs_printk(KERN_WARNING, "Error "
+                                               "writing tag 3 packet\n");
+@@ -2438,8 +2447,8 @@ ecryptfs_generate_key_packet_set(char *d
+                       }
+                       (*len) += written;
+               } else if (auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) {
+-                      rc = write_tag_1_packet(dest_base + (*len),
+-                                              &max, auth_tok,
++                      rc = write_tag_1_packet(dest_base + (*len), &max,
++                                              auth_tok_key, auth_tok,
+                                               crypt_stat, key_rec, &written);
+                       if (rc) {
+                               ecryptfs_printk(KERN_WARNING, "Error "
+@@ -2448,14 +2457,13 @@ ecryptfs_generate_key_packet_set(char *d
+                       }
+                       (*len) += written;
+               } else {
++                      up_write(&(auth_tok_key->sem));
++                      key_put(auth_tok_key);
+                       ecryptfs_printk(KERN_WARNING, "Unsupported "
+                                       "authentication token type\n");
+                       rc = -EINVAL;
+                       goto out_free;
+               }
+-              up_write(&(auth_tok_key->sem));
+-              key_put(auth_tok_key);
+-              auth_tok_key = NULL;
+       }
+       if (likely(max > 0)) {
+               dest_base[(*len)] = 0x00;
+@@ -2468,11 +2476,6 @@ out_free:
+ out:
+       if (rc)
+               (*len) = 0;
+-      if (auth_tok_key) {
+-              up_write(&(auth_tok_key->sem));
+-              key_put(auth_tok_key);
+-      }
+-
+       mutex_unlock(&crypt_stat->keysig_list_mutex);
+       return rc;
+ }
diff --git a/queue-3.0/ehci-fix-direction-handling-for-interrupt-data-toggles.patch b/queue-3.0/ehci-fix-direction-handling-for-interrupt-data-toggles.patch
new file mode 100644 (file)
index 0000000..9287fab
--- /dev/null
@@ -0,0 +1,72 @@
+From e04f5f7e423018bcec84c11af2058cdce87816f3 Mon Sep 17 00:00:00 2001
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Tue, 19 Jul 2011 14:01:23 -0400
+Subject: EHCI: fix direction handling for interrupt data toggles
+
+From: Alan Stern <stern@rowland.harvard.edu>
+
+commit e04f5f7e423018bcec84c11af2058cdce87816f3 upstream.
+
+This patch (as1480) fixes a rather obscure bug in ehci-hcd.  The
+qh_update() routine needs to know the number and direction of the
+endpoint corresponding to its QH argument.  The number can be taken
+directly from the QH data structure, but the direction isn't stored
+there.  The direction is taken instead from the first qTD linked to
+the QH.
+
+However, it turns out that for interrupt transfers, qh_update() gets
+called before the qTDs are linked to the QH.  As a result, qh_update()
+computes a bogus direction value, which messes up the endpoint toggle
+handling.  Under the right combination of circumstances this causes
+usb_reset_endpoint() not to work correctly, which causes packets to be
+dropped and communications to fail.
+
+Now, it's silly for the QH structure not to have direct access to all
+the descriptor information for the corresponding endpoint.  Ultimately
+it may get a pointer to the usb_host_endpoint structure; for now,
+adding a copy of the direction flag solves the immediate problem.
+
+This allows the Spyder2 color-calibration system (a low-speed USB
+device that sends all its interrupt data packets with the toggle set
+to 0 and hance requires constant use of usb_reset_endpoint) to work
+when connected through a high-speed hub.  Thanks to Graeme Gill for
+supplying the hardware that allowed me to track down this bug.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Reported-by: Graeme Gill <graeme@argyllcms.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-q.c |    3 ++-
+ drivers/usb/host/ehci.h   |    1 +
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/host/ehci-q.c
++++ b/drivers/usb/host/ehci-q.c
+@@ -103,7 +103,7 @@ qh_update (struct ehci_hcd *ehci, struct
+       if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
+               unsigned        is_out, epnum;
+-              is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
++              is_out = qh->is_out;
+               epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
+               if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
+                       hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
+@@ -946,6 +946,7 @@ done:
+       hw = qh->hw;
+       hw->hw_info1 = cpu_to_hc32(ehci, info1);
+       hw->hw_info2 = cpu_to_hc32(ehci, info2);
++      qh->is_out = !is_input;
+       usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
+       qh_refresh (ehci, qh);
+       return qh;
+--- a/drivers/usb/host/ehci.h
++++ b/drivers/usb/host/ehci.h
+@@ -375,6 +375,7 @@ struct ehci_qh {
+ #define NO_FRAME ((unsigned short)~0)                 /* pick new start */
+       struct usb_device       *dev;           /* access to TT */
++      unsigned                is_out:1;       /* bulk or intr OUT */
+       unsigned                clearing_tt:1;  /* Clear-TT-Buf in progress */
+ };
diff --git a/queue-3.0/ehci-only-power-off-port-if-over-current-is-active.patch b/queue-3.0/ehci-only-power-off-port-if-over-current-is-active.patch
new file mode 100644 (file)
index 0000000..d50bbe4
--- /dev/null
@@ -0,0 +1,43 @@
+From 81463c1d707186adbbe534016cd1249edeab0dac Mon Sep 17 00:00:00 2001
+From: Sergei Shtylyov <sshtylyov@ru.mvista.com>
+Date: Wed, 6 Jul 2011 23:19:38 +0400
+Subject: EHCI: only power off port if over-current is active
+
+From: Sergei Shtylyov <sshtylyov@ru.mvista.com>
+
+commit 81463c1d707186adbbe534016cd1249edeab0dac upstream.
+
+MAX4967 USB power supply chip we use on our boards signals over-current when
+power is not enabled; once it's enabled, over-current signal returns to normal.
+That unfortunately caused the endless stream of "over-current change on port"
+messages. The EHCI root hub code reacts on every over-current signal change
+with powering off the port -- such change event is generated the moment the
+port power is enabled, so once enabled the power is immediately cut off.
+I think we should only cut off power when we're seeing the active over-current
+signal, so I'm adding such check to that code. I also think that the fact that
+we've cut off the port power should be reflected in the result of GetPortStatus
+request immediately, hence I'm adding a PORTSCn register readback after write...
+
+Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-hub.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/host/ehci-hub.c
++++ b/drivers/usb/host/ehci-hub.c
+@@ -891,10 +891,11 @@ static int ehci_hub_control (
+                        * power switching; they're allowed to just limit the
+                        * current.  khubd will turn the power back on.
+                        */
+-                      if (HCS_PPC (ehci->hcs_params)){
++                      if ((temp & PORT_OC) && HCS_PPC(ehci->hcs_params)) {
+                               ehci_writel(ehci,
+                                       temp & ~(PORT_RWC_BITS | PORT_POWER),
+                                       status_reg);
++                              temp = ehci_readl(ehci, status_reg);
+                       }
+               }
diff --git a/queue-3.0/n_gsm-fix-the-wrong-fcs-handling.patch b/queue-3.0/n_gsm-fix-the-wrong-fcs-handling.patch
new file mode 100644 (file)
index 0000000..b6103c0
--- /dev/null
@@ -0,0 +1,36 @@
+From f086ced17191fa0c5712539d2b680eae3dc972a1 Mon Sep 17 00:00:00 2001
+From: "Du, Alek" <alek.du@intel.com>
+Date: Thu, 7 Jul 2011 15:16:48 +0100
+Subject: n_gsm: fix the wrong FCS handling
+
+From: "Du, Alek" <alek.du@intel.com>
+
+commit f086ced17191fa0c5712539d2b680eae3dc972a1 upstream.
+
+FCS could be GSM0_SOF, so will break state machine...
+
+[This byte isn't quoted in any way so a SOF here doesn't imply an error
+ occurred.]
+
+Signed-off-by: Alek Du <alek.du@intel.com>
+Signed-off-by: Alan Cox <alan@linux.intel.com>
+[Trivial but best backported once its in 3.1rc I think]
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/tty/n_gsm.c |    4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -1823,10 +1823,6 @@ static void gsm0_receive(struct gsm_mux
+               break;
+       case GSM_FCS:           /* FCS follows the packet */
+               gsm->received_fcs = c;
+-              if (c == GSM0_SOF) {
+-                      gsm->state = GSM_SEARCH;
+-                      break;
+-              }
+               gsm_queue(gsm);
+               gsm->state = GSM_SSOF;
+               break;
diff --git a/queue-3.0/nfs-fix-spurious-readdir-cookie-loop-messages.patch b/queue-3.0/nfs-fix-spurious-readdir-cookie-loop-messages.patch
new file mode 100644 (file)
index 0000000..7416551
--- /dev/null
@@ -0,0 +1,183 @@
+From 0c0308066ca53fdf1423895f3a42838b67b3a5a8 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <Trond.Myklebust@netapp.com>
+Date: Sat, 30 Jul 2011 12:45:35 -0400
+Subject: NFS: Fix spurious readdir cookie loop messages
+
+From: Trond Myklebust <Trond.Myklebust@netapp.com>
+
+commit 0c0308066ca53fdf1423895f3a42838b67b3a5a8 upstream.
+
+If the directory contents change, then we have to accept that the
+file->f_pos value may shrink if we do a 'search-by-cookie'. In that
+case, we should turn off the loop detection and let the NFS client
+try to recover.
+
+The patch also fixes a second loop detection bug by ensuring
+that after turning on the ctx->duped flag, we read at least one new
+cookie into ctx->dir_cookie before attempting to match with
+ctx->dup_cookie.
+
+Reported-by: Petr Vandrovec <petr@vandrovec.name>
+Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/nfs/dir.c           |   56 ++++++++++++++++++++++++++++---------------------
+ include/linux/nfs_fs.h |    3 +-
+ 2 files changed, 35 insertions(+), 24 deletions(-)
+
+--- a/fs/nfs/dir.c
++++ b/fs/nfs/dir.c
+@@ -134,18 +134,19 @@ const struct inode_operations nfs4_dir_i
+ #endif /* CONFIG_NFS_V4 */
+-static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred)
++static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
+ {
+       struct nfs_open_dir_context *ctx;
+       ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+       if (ctx != NULL) {
+               ctx->duped = 0;
++              ctx->attr_gencount = NFS_I(dir)->attr_gencount;
+               ctx->dir_cookie = 0;
+               ctx->dup_cookie = 0;
+               ctx->cred = get_rpccred(cred);
+-      } else
+-              ctx = ERR_PTR(-ENOMEM);
+-      return ctx;
++              return ctx;
++      }
++      return  ERR_PTR(-ENOMEM);
+ }
+ static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
+@@ -173,7 +174,7 @@ nfs_opendir(struct inode *inode, struct
+       cred = rpc_lookup_cred();
+       if (IS_ERR(cred))
+               return PTR_ERR(cred);
+-      ctx = alloc_nfs_open_dir_context(cred);
++      ctx = alloc_nfs_open_dir_context(inode, cred);
+       if (IS_ERR(ctx)) {
+               res = PTR_ERR(ctx);
+               goto out;
+@@ -323,7 +324,6 @@ int nfs_readdir_search_for_pos(struct nf
+ {
+       loff_t diff = desc->file->f_pos - desc->current_index;
+       unsigned int index;
+-      struct nfs_open_dir_context *ctx = desc->file->private_data;
+       if (diff < 0)
+               goto out_eof;
+@@ -336,7 +336,6 @@ int nfs_readdir_search_for_pos(struct nf
+       index = (unsigned int)diff;
+       *desc->dir_cookie = array->array[index].cookie;
+       desc->cache_entry_index = index;
+-      ctx->duped = 0;
+       return 0;
+ out_eof:
+       desc->eof = 1;
+@@ -349,14 +348,33 @@ int nfs_readdir_search_for_cookie(struct
+       int i;
+       loff_t new_pos;
+       int status = -EAGAIN;
+-      struct nfs_open_dir_context *ctx = desc->file->private_data;
+       for (i = 0; i < array->size; i++) {
+               if (array->array[i].cookie == *desc->dir_cookie) {
++                      struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode);
++                      struct nfs_open_dir_context *ctx = desc->file->private_data;
++
+                       new_pos = desc->current_index + i;
+-                      if (new_pos < desc->file->f_pos) {
++                      if (ctx->attr_gencount != nfsi->attr_gencount
++                          || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) {
++                              ctx->duped = 0;
++                              ctx->attr_gencount = nfsi->attr_gencount;
++                      } else if (new_pos < desc->file->f_pos) {
++                              if (ctx->duped > 0
++                                  && ctx->dup_cookie == *desc->dir_cookie) {
++                                      if (printk_ratelimit()) {
++                                              pr_notice("NFS: directory %s/%s contains a readdir loop."
++                                                              "Please contact your server vendor.  "
++                                                              "Offending cookie: %llu\n",
++                                                              desc->file->f_dentry->d_parent->d_name.name,
++                                                              desc->file->f_dentry->d_name.name,
++                                                              *desc->dir_cookie);
++                                      }
++                                      status = -ELOOP;
++                                      goto out;
++                              }
+                               ctx->dup_cookie = *desc->dir_cookie;
+-                              ctx->duped = 1;
++                              ctx->duped = -1;
+                       }
+                       desc->file->f_pos = new_pos;
+                       desc->cache_entry_index = i;
+@@ -368,6 +386,7 @@ int nfs_readdir_search_for_cookie(struct
+               if (*desc->dir_cookie == array->last_cookie)
+                       desc->eof = 1;
+       }
++out:
+       return status;
+ }
+@@ -740,19 +759,6 @@ int nfs_do_filldir(nfs_readdir_descripto
+       struct nfs_cache_array *array = NULL;
+       struct nfs_open_dir_context *ctx = file->private_data;
+-      if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) {
+-              if (printk_ratelimit()) {
+-                      pr_notice("NFS: directory %s/%s contains a readdir loop.  "
+-                              "Please contact your server vendor.  "
+-                              "Offending cookie: %llu\n",
+-                              file->f_dentry->d_parent->d_name.name,
+-                              file->f_dentry->d_name.name,
+-                              *desc->dir_cookie);
+-              }
+-              res = -ELOOP;
+-              goto out;
+-      }
+-
+       array = nfs_readdir_get_array(desc->page);
+       if (IS_ERR(array)) {
+               res = PTR_ERR(array);
+@@ -774,6 +780,8 @@ int nfs_do_filldir(nfs_readdir_descripto
+                       *desc->dir_cookie = array->array[i+1].cookie;
+               else
+                       *desc->dir_cookie = array->last_cookie;
++              if (ctx->duped != 0)
++                      ctx->duped = 1;
+       }
+       if (array->eof_index >= 0)
+               desc->eof = 1;
+@@ -805,6 +813,7 @@ int uncached_readdir(nfs_readdir_descrip
+       struct page     *page = NULL;
+       int             status;
+       struct inode *inode = desc->file->f_path.dentry->d_inode;
++      struct nfs_open_dir_context *ctx = desc->file->private_data;
+       dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
+                       (unsigned long long)*desc->dir_cookie);
+@@ -818,6 +827,7 @@ int uncached_readdir(nfs_readdir_descrip
+       desc->page_index = 0;
+       desc->last_cookie = *desc->dir_cookie;
+       desc->page = page;
++      ctx->duped = 0;
+       status = nfs_readdir_xdr_to_array(desc, page, inode);
+       if (status < 0)
+--- a/include/linux/nfs_fs.h
++++ b/include/linux/nfs_fs.h
+@@ -99,9 +99,10 @@ struct nfs_open_context {
+ struct nfs_open_dir_context {
+       struct rpc_cred *cred;
++      unsigned long attr_gencount;
+       __u64 dir_cookie;
+       __u64 dup_cookie;
+-      int duped;
++      signed char duped;
+ };
+ /*
diff --git a/queue-3.0/nfsd-don-t-break-lease-on-claim_delegate_cur.patch b/queue-3.0/nfsd-don-t-break-lease-on-claim_delegate_cur.patch
new file mode 100644 (file)
index 0000000..2c7cb41
--- /dev/null
@@ -0,0 +1,68 @@
+From 0c12eaffdf09466f36a9ffe970dda8f4aeb6efc0 Mon Sep 17 00:00:00 2001
+From: Casey Bodley <cbodley@citi.umich.edu>
+Date: Sat, 23 Jul 2011 14:58:10 -0400
+Subject: nfsd: don't break lease on CLAIM_DELEGATE_CUR
+
+From: Casey Bodley <cbodley@citi.umich.edu>
+
+commit 0c12eaffdf09466f36a9ffe970dda8f4aeb6efc0 upstream.
+
+CLAIM_DELEGATE_CUR is used in response to a broken lease; allowing it
+to break the lease and return EAGAIN leaves the client unable to make
+progress in returning the delegation
+
+nfs4_get_vfs_file() now takes struct nfsd4_open for access to the
+claim type, and calls nfsd_open() with NFSD_MAY_NOT_BREAK_LEASE when
+claim type is CLAIM_DELEGATE_CUR
+
+Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
+Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/nfsd/nfs4state.c |   18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+--- a/fs/nfsd/nfs4state.c
++++ b/fs/nfsd/nfs4state.c
+@@ -2556,12 +2556,18 @@ static inline int nfs4_access_to_access(
+       return flags;
+ }
+-static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file
+-*fp, struct svc_fh *cur_fh, u32 nfs4_access)
++static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
++              struct svc_fh *cur_fh, struct nfsd4_open *open)
+ {
+       __be32 status;
+-      int oflag = nfs4_access_to_omode(nfs4_access);
+-      int access = nfs4_access_to_access(nfs4_access);
++      int oflag = nfs4_access_to_omode(open->op_share_access);
++      int access = nfs4_access_to_access(open->op_share_access);
++
++      /* CLAIM_DELEGATE_CUR is used in response to a broken lease;
++       * allowing it to break the lease and return EAGAIN leaves the
++       * client unable to make progress in returning the delegation */
++      if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR)
++              access |= NFSD_MAY_NOT_BREAK_LEASE;
+       if (!fp->fi_fds[oflag]) {
+               status = nfsd_open(rqstp, cur_fh, S_IFREG, access,
+@@ -2586,7 +2592,7 @@ nfs4_new_open(struct svc_rqst *rqstp, st
+       if (stp == NULL)
+               return nfserr_resource;
+-      status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open->op_share_access);
++      status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
+       if (status) {
+               kmem_cache_free(stateid_slab, stp);
+               return status;
+@@ -2619,7 +2625,7 @@ nfs4_upgrade_open(struct svc_rqst *rqstp
+       new_access = !test_bit(op_share_access, &stp->st_access_bmap);
+       if (new_access) {
+-              status = nfs4_get_vfs_file(rqstp, fp, cur_fh, op_share_access);
++              status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
+               if (status)
+                       return status;
+       }
diff --git a/queue-3.0/nfsd4-fix-file-leak-on-open_downgrade.patch b/queue-3.0/nfsd4-fix-file-leak-on-open_downgrade.patch
new file mode 100644 (file)
index 0000000..cd3dbc3
--- /dev/null
@@ -0,0 +1,103 @@
+From f197c27196a5e7631b89e2e92daa096fcf7c302c Mon Sep 17 00:00:00 2001
+From: "J. Bruce Fields" <bfields@redhat.com>
+Date: Wed, 29 Jun 2011 08:23:50 -0400
+Subject: nfsd4: fix file leak on open_downgrade
+
+From: "J. Bruce Fields" <bfields@redhat.com>
+
+commit f197c27196a5e7631b89e2e92daa096fcf7c302c upstream.
+
+Stateid's hold a read reference for a read open, a write reference for a
+write open, and an additional one of each for each read+write open.  The
+latter wasn't getting put on a downgrade, so something like:
+
+       open RW
+       open R
+       downgrade to R
+
+was resulting in a file leak.
+
+Also fix an imbalance in an error path.
+
+Regression from 7d94784293096c0a46897acdb83be5abd9278ece "nfsd4: fix
+downgrade/lock logic".
+
+Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/nfsd/nfs4state.c |   31 ++++++++-----------------------
+ 1 file changed, 8 insertions(+), 23 deletions(-)
+
+--- a/fs/nfsd/nfs4state.c
++++ b/fs/nfsd/nfs4state.c
+@@ -2332,15 +2332,6 @@ out:
+       return ret;
+ }
+-static inline void
+-nfs4_file_downgrade(struct nfs4_file *fp, unsigned int share_access)
+-{
+-      if (share_access & NFS4_SHARE_ACCESS_WRITE)
+-              nfs4_file_put_access(fp, O_WRONLY);
+-      if (share_access & NFS4_SHARE_ACCESS_READ)
+-              nfs4_file_put_access(fp, O_RDONLY);
+-}
+-
+ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
+ {
+       /* We're assuming the state code never drops its reference
+@@ -2627,7 +2618,7 @@ nfs4_upgrade_open(struct svc_rqst *rqstp
+       status = nfsd4_truncate(rqstp, cur_fh, open);
+       if (status) {
+               if (new_access) {
+-                      int oflag = nfs4_access_to_omode(new_access);
++                      int oflag = nfs4_access_to_omode(op_share_access);
+                       nfs4_file_put_access(fp, oflag);
+               }
+               return status;
+@@ -3385,18 +3376,15 @@ out:
+       return status;
+ }
+-
+-/*
+- * unset all bits in union bitmap (bmap) that
+- * do not exist in share (from successful OPEN_DOWNGRADE)
+- */
+-static void
+-reset_union_bmap_access(unsigned long access, unsigned long *bmap)
++static inline void nfs4_file_downgrade(struct nfs4_stateid *stp, unsigned int to_access)
+ {
+       int i;
++
+       for (i = 1; i < 4; i++) {
+-              if ((i & access) != i)
+-                      __clear_bit(i, bmap);
++              if (test_bit(i, &stp->st_access_bmap) && !(i & to_access)) {
++                      nfs4_file_put_access(stp->st_file, i);
++                      __clear_bit(i, &stp->st_access_bmap);
++              }
+       }
+ }
+@@ -3417,7 +3405,6 @@ nfsd4_open_downgrade(struct svc_rqst *rq
+ {
+       __be32 status;
+       struct nfs4_stateid *stp;
+-      unsigned int share_access;
+       dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", 
+                       (int)cstate->current_fh.fh_dentry->d_name.len,
+@@ -3446,10 +3433,8 @@ nfsd4_open_downgrade(struct svc_rqst *rq
+                       stp->st_deny_bmap, od->od_share_deny);
+               goto out;
+       }
+-      set_access(&share_access, stp->st_access_bmap);
+-      nfs4_file_downgrade(stp->st_file, share_access & ~od->od_share_access);
++      nfs4_file_downgrade(stp, od->od_share_access);
+-      reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap);
+       reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap);
+       update_stateid(&stp->st_stateid);
diff --git a/queue-3.0/nfsd4-remember-to-put-rw-access-on-stateid-destruction.patch b/queue-3.0/nfsd4-remember-to-put-rw-access-on-stateid-destruction.patch
new file mode 100644 (file)
index 0000000..dff1d6a
--- /dev/null
@@ -0,0 +1,62 @@
+From 499f3edc23ca0431f3a0a6736b3a40944c81bf3b Mon Sep 17 00:00:00 2001
+From: "J. Bruce Fields" <bfields@redhat.com>
+Date: Mon, 27 Jun 2011 16:57:12 -0400
+Subject: nfsd4: remember to put RW access on stateid destruction
+
+From: "J. Bruce Fields" <bfields@redhat.com>
+
+commit 499f3edc23ca0431f3a0a6736b3a40944c81bf3b upstream.
+
+Without this, for example,
+
+       open read
+       open read+write
+       close
+
+will result in a struct file leak.
+
+Regression from 7d94784293096c0a46897acdb83be5abd9278ece "nfsd4: fix
+downgrade/lock logic".
+
+Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/nfsd/nfs4state.c |   17 ++++++-----------
+ 1 file changed, 6 insertions(+), 11 deletions(-)
+
+--- a/fs/nfsd/nfs4state.c
++++ b/fs/nfsd/nfs4state.c
+@@ -381,14 +381,6 @@ static int nfs4_access_to_omode(u32 acce
+       BUG();
+ }
+-static int nfs4_access_bmap_to_omode(struct nfs4_stateid *stp)
+-{
+-      unsigned int access;
+-
+-      set_access(&access, stp->st_access_bmap);
+-      return nfs4_access_to_omode(access);
+-}
+-
+ static void unhash_generic_stateid(struct nfs4_stateid *stp)
+ {
+       list_del(&stp->st_hash);
+@@ -398,11 +390,14 @@ static void unhash_generic_stateid(struc
+ static void free_generic_stateid(struct nfs4_stateid *stp)
+ {
+-      int oflag;
++      int i;
+       if (stp->st_access_bmap) {
+-              oflag = nfs4_access_bmap_to_omode(stp);
+-              nfs4_file_put_access(stp->st_file, oflag);
++              for (i = 1; i < 4; i++) {
++                      if (test_bit(i, &stp->st_access_bmap))
++                              nfs4_file_put_access(stp->st_file,
++                                              nfs4_access_to_omode(i));
++              }
+       }
+       put_nfs4_file(stp->st_file);
+       kmem_cache_free(stateid_slab, stp);
diff --git a/queue-3.0/nfsv4-don-t-use-the-delegation-inode-in.patch b/queue-3.0/nfsv4-don-t-use-the-delegation-inode-in.patch
new file mode 100644 (file)
index 0000000..6c8a333
--- /dev/null
@@ -0,0 +1,78 @@
+From ed1e6211a0a134ff23592c6f057af982ad5dab52 Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <Trond.Myklebust@netapp.com>
+Date: Mon, 25 Jul 2011 15:37:29 -0400
+Subject: NFSv4: Don't use the delegation->inode in
+ nfs_mark_return_delegation()
+
+From: Trond Myklebust <Trond.Myklebust@netapp.com>
+
+commit ed1e6211a0a134ff23592c6f057af982ad5dab52 upstream.
+
+nfs_mark_return_delegation() is usually called without any locking, and
+so it is not safe to dereference delegation->inode. Since the inode is
+only used to discover the nfs_client anyway, it makes more sense to
+have the callers pass a valid pointer to the nfs_server as a parameter.
+
+Reported-by: Ian Kent <raven@themaw.net>
+Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/nfs/delegation.c |   16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/fs/nfs/delegation.c
++++ b/fs/nfs/delegation.c
+@@ -398,12 +398,11 @@ int nfs_inode_return_delegation(struct i
+       return err;
+ }
+-static void nfs_mark_return_delegation(struct nfs_delegation *delegation)
++static void nfs_mark_return_delegation(struct nfs_server *server,
++              struct nfs_delegation *delegation)
+ {
+-      struct nfs_client *clp = NFS_SERVER(delegation->inode)->nfs_client;
+-
+       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+-      set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
++      set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
+ }
+ /**
+@@ -441,7 +440,7 @@ static void nfs_mark_return_all_delegati
+               if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
+                       continue;
+               if (delegation->type & flags)
+-                      nfs_mark_return_delegation(delegation);
++                      nfs_mark_return_delegation(server, delegation);
+       }
+ }
+@@ -508,7 +507,7 @@ static void nfs_mark_return_unreferenced
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
+               if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
+                       continue;
+-              nfs_mark_return_delegation(delegation);
++              nfs_mark_return_delegation(server, delegation);
+       }
+ }
+@@ -539,7 +538,8 @@ void nfs_expire_unreferenced_delegations
+ int nfs_async_inode_return_delegation(struct inode *inode,
+                                     const nfs4_stateid *stateid)
+ {
+-      struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
++      struct nfs_server *server = NFS_SERVER(inode);
++      struct nfs_client *clp = server->nfs_client;
+       struct nfs_delegation *delegation;
+       rcu_read_lock();
+@@ -549,7 +549,7 @@ int nfs_async_inode_return_delegation(st
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+-      nfs_mark_return_delegation(delegation);
++      nfs_mark_return_delegation(server, delegation);
+       rcu_read_unlock();
+       nfs_delegation_run_state_manager(clp);
diff --git a/queue-3.0/proc-fix-a-race-in-do_io_accounting.patch b/queue-3.0/proc-fix-a-race-in-do_io_accounting.patch
new file mode 100644 (file)
index 0000000..5e15936
--- /dev/null
@@ -0,0 +1,77 @@
+From 293eb1e7772b25a93647c798c7b89bf26c2da2e0 Mon Sep 17 00:00:00 2001
+From: Vasiliy Kulikov <segoon@openwall.com>
+Date: Tue, 26 Jul 2011 16:08:38 -0700
+Subject: proc: fix a race in do_io_accounting()
+
+From: Vasiliy Kulikov <segoon@openwall.com>
+
+commit 293eb1e7772b25a93647c798c7b89bf26c2da2e0 upstream.
+
+If an inode's mode permits opening /proc/PID/io and the resulting file
+descriptor is kept across execve() of a setuid or similar binary, the
+ptrace_may_access() check tries to prevent using this fd against the
+task with escalated privileges.
+
+Unfortunately, there is a race in the check against execve().  If
+execve() is processed after the ptrace check, but before the actual io
+information gathering, io statistics will be gathered from the
+privileged process.  At least in theory this might lead to gathering
+sensible information (like ssh/ftp password length) that wouldn't be
+available otherwise.
+
+Holding task->signal->cred_guard_mutex while gathering the io
+information should protect against the race.
+
+The order of locking is similar to the one inside of ptrace_attach():
+first goes cred_guard_mutex, then lock_task_sighand().
+
+Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/proc/base.c |   16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -2707,9 +2707,16 @@ static int do_io_accounting(struct task_
+ {
+       struct task_io_accounting acct = task->ioac;
+       unsigned long flags;
++      int result;
+-      if (!ptrace_may_access(task, PTRACE_MODE_READ))
+-              return -EACCES;
++      result = mutex_lock_killable(&task->signal->cred_guard_mutex);
++      if (result)
++              return result;
++
++      if (!ptrace_may_access(task, PTRACE_MODE_READ)) {
++              result = -EACCES;
++              goto out_unlock;
++      }
+       if (whole && lock_task_sighand(task, &flags)) {
+               struct task_struct *t = task;
+@@ -2720,7 +2727,7 @@ static int do_io_accounting(struct task_
+               unlock_task_sighand(task, &flags);
+       }
+-      return sprintf(buffer,
++      result = sprintf(buffer,
+                       "rchar: %llu\n"
+                       "wchar: %llu\n"
+                       "syscr: %llu\n"
+@@ -2735,6 +2742,9 @@ static int do_io_accounting(struct task_
+                       (unsigned long long)acct.read_bytes,
+                       (unsigned long long)acct.write_bytes,
+                       (unsigned long long)acct.cancelled_write_bytes);
++out_unlock:
++      mutex_unlock(&task->signal->cred_guard_mutex);
++      return result;
+ }
+ static int proc_tid_io_accounting(struct task_struct *task, char *buffer)
index f88e3b354c86ed0cd3cecccd0edbbae08c4556dc..d115d5b66d18cc8328443feee937a55769b6bfca 100644 (file)
@@ -70,3 +70,15 @@ xtensa-prevent-arbitrary-read-in-ptrace.patch
 ext4-fix-i_blocks-quota-accounting-when-extent-insertion-fails.patch
 ext4-free-allocated-and-pre-allocated-blocks-when.patch
 ext3-fix-oops-in-ext3_try_to_allocate_with_rsv.patch
+ecryptfs-make-inode-bdi-consistent-with-superblock-bdi.patch
+ecryptfs-unlock-keys-needed-by-ecryptfsd.patch
+nfsd-don-t-break-lease-on-claim_delegate_cur.patch
+nfsd4-remember-to-put-rw-access-on-stateid-destruction.patch
+nfsd4-fix-file-leak-on-open_downgrade.patch
+svcrpc-fix-list-corrupting-race-on-nfsd-shutdown.patch
+nfsv4-don-t-use-the-delegation-inode-in.patch
+nfs-fix-spurious-readdir-cookie-loop-messages.patch
+proc-fix-a-race-in-do_io_accounting.patch
+n_gsm-fix-the-wrong-fcs-handling.patch
+ehci-only-power-off-port-if-over-current-is-active.patch
+ehci-fix-direction-handling-for-interrupt-data-toggles.patch
diff --git a/queue-3.0/svcrpc-fix-list-corrupting-race-on-nfsd-shutdown.patch b/queue-3.0/svcrpc-fix-list-corrupting-race-on-nfsd-shutdown.patch
new file mode 100644 (file)
index 0000000..0753504
--- /dev/null
@@ -0,0 +1,54 @@
+From ebc63e531cc6a457595dd110b07ac530eae788c3 Mon Sep 17 00:00:00 2001
+From: "J. Bruce Fields" <bfields@redhat.com>
+Date: Wed, 29 Jun 2011 16:49:04 -0400
+Subject: svcrpc: fix list-corrupting race on nfsd shutdown
+
+From: "J. Bruce Fields" <bfields@redhat.com>
+
+commit ebc63e531cc6a457595dd110b07ac530eae788c3 upstream.
+
+After commit 3262c816a3d7fb1eaabce633caa317887ed549ae "[PATCH] knfsd:
+split svc_serv into pools", svc_delete_xprt (then svc_delete_socket) no
+longer removed its xpt_ready (then sk_ready) field from whatever list it
+was on, noting that there was no point since the whole list was about to
+be destroyed anyway.
+
+That was mostly true, but forgot that a few svc_xprt_enqueue()'s might
+still be hanging around playing with the about-to-be-destroyed list, and
+could get themselves into trouble writing to freed memory if we left
+this xprt on the list after freeing it.
+
+(This is actually functionally identical to a patch made first by Ben
+Greear, but with more comments.)
+
+Cc: gnb@fmeh.org
+Reported-by: Ben Greear <greearb@candelatech.com>
+Tested-by: Ben Greear <greearb@candelatech.com>
+Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ net/sunrpc/svc_xprt.c |   11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+--- a/net/sunrpc/svc_xprt.c
++++ b/net/sunrpc/svc_xprt.c
+@@ -902,12 +902,13 @@ void svc_delete_xprt(struct svc_xprt *xp
+       if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags))
+               list_del_init(&xprt->xpt_list);
+       /*
+-       * We used to delete the transport from whichever list
+-       * it's sk_xprt.xpt_ready node was on, but we don't actually
+-       * need to.  This is because the only time we're called
+-       * while still attached to a queue, the queue itself
+-       * is about to be destroyed (in svc_destroy).
++       * The only time we're called while xpt_ready is still on a list
++       * is while the list itself is about to be destroyed (in
++       * svc_destroy).  BUT svc_xprt_enqueue could still be attempting
++       * to add new entries to the sp_sockets list, so we can't leave
++       * a freed xprt on it.
+        */
++      list_del_init(&xprt->xpt_ready);
+       if (test_bit(XPT_TEMP, &xprt->xpt_flags))
+               serv->sv_tmpcnt--;
+       spin_unlock_bh(&serv->sv_lock);