summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2011-08-02 11:44:07 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-02 11:44:07 -0700
commit0d1514e0157dd89564045969f05e3e0b5f4ddf8d (patch)
tree64433d52cce48695aa8c56aa4decca929169a7cc
parent74d959235354de64ad327d4952c65048bd09c067 (diff)
downloadstable-queue-0d1514e0157dd89564045969f05e3e0b5f4ddf8d.tar.gz
3.0 patches
-rw-r--r--queue-3.0/ecryptfs-make-inode-bdi-consistent-with-superblock-bdi.patch30
-rw-r--r--queue-3.0/ecryptfs-unlock-keys-needed-by-ecryptfsd.patch180
-rw-r--r--queue-3.0/ehci-fix-direction-handling-for-interrupt-data-toggles.patch72
-rw-r--r--queue-3.0/ehci-only-power-off-port-if-over-current-is-active.patch43
-rw-r--r--queue-3.0/n_gsm-fix-the-wrong-fcs-handling.patch36
-rw-r--r--queue-3.0/nfs-fix-spurious-readdir-cookie-loop-messages.patch183
-rw-r--r--queue-3.0/nfsd-don-t-break-lease-on-claim_delegate_cur.patch68
-rw-r--r--queue-3.0/nfsd4-fix-file-leak-on-open_downgrade.patch103
-rw-r--r--queue-3.0/nfsd4-remember-to-put-rw-access-on-stateid-destruction.patch62
-rw-r--r--queue-3.0/nfsv4-don-t-use-the-delegation-inode-in.patch78
-rw-r--r--queue-3.0/proc-fix-a-race-in-do_io_accounting.patch77
-rw-r--r--queue-3.0/series12
-rw-r--r--queue-3.0/svcrpc-fix-list-corrupting-race-on-nfsd-shutdown.patch54
13 files changed, 998 insertions, 0 deletions
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
index 0000000000..0f4039d7a6
--- /dev/null
+++ b/queue-3.0/ecryptfs-make-inode-bdi-consistent-with-superblock-bdi.patch
@@ -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
index 0000000000..f99f0ff704
--- /dev/null
+++ b/queue-3.0/ecryptfs-unlock-keys-needed-by-ecryptfsd.patch
@@ -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
index 0000000000..9287fab11f
--- /dev/null
+++ b/queue-3.0/ehci-fix-direction-handling-for-interrupt-data-toggles.patch
@@ -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
index 0000000000..d50bbe4dea
--- /dev/null
+++ b/queue-3.0/ehci-only-power-off-port-if-over-current-is-active.patch
@@ -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
index 0000000000..b6103c0d60
--- /dev/null
+++ b/queue-3.0/n_gsm-fix-the-wrong-fcs-handling.patch
@@ -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
index 0000000000..741655119c
--- /dev/null
+++ b/queue-3.0/nfs-fix-spurious-readdir-cookie-loop-messages.patch
@@ -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
index 0000000000..2c7cb41f51
--- /dev/null
+++ b/queue-3.0/nfsd-don-t-break-lease-on-claim_delegate_cur.patch
@@ -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
index 0000000000..cd3dbc3a46
--- /dev/null
+++ b/queue-3.0/nfsd4-fix-file-leak-on-open_downgrade.patch
@@ -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
index 0000000000..dff1d6a5a4
--- /dev/null
+++ b/queue-3.0/nfsd4-remember-to-put-rw-access-on-stateid-destruction.patch
@@ -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
index 0000000000..6c8a3338d3
--- /dev/null
+++ b/queue-3.0/nfsv4-don-t-use-the-delegation-inode-in.patch
@@ -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
index 0000000000..5e159365ae
--- /dev/null
+++ b/queue-3.0/proc-fix-a-race-in-do_io_accounting.patch
@@ -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)
diff --git a/queue-3.0/series b/queue-3.0/series
index f88e3b354c..d115d5b66d 100644
--- a/queue-3.0/series
+++ b/queue-3.0/series
@@ -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
index 0000000000..07535049a1
--- /dev/null
+++ b/queue-3.0/svcrpc-fix-list-corrupting-race-on-nfsd-shutdown.patch
@@ -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);