]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge tag 'nfs-for-4.11-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Mar 2017 00:10:30 +0000 (16:10 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Mar 2017 00:10:30 +0000 (16:10 -0800)
Pull NFS client updates from Anna Schumaker:
 "Highlights include:

  Stable bugfixes:
   - NFSv4: Fix memory and state leak in _nfs4_open_and_get_state
   - xprtrdma: Fix Read chunk padding
   - xprtrdma: Per-connection pad optimization
   - xprtrdma: Disable pad optimization by default
   - xprtrdma: Reduce required number of send SGEs
   - nlm: Ensure callback code also checks that the files match
   - pNFS/flexfiles: If the layout is invalid, it must be updated before
     retrying
   - NFSv4: Fix reboot recovery in copy offload
   - Revert "NFSv4.1: Handle NFS4ERR_BADSESSION/NFS4ERR_DEADSESSION
     replies to OP_SEQUENCE"
   - NFSv4: fix getacl head length estimation
   - NFSv4: fix getacl ERANGE for sum ACL buffer sizes

  Features:
   - Add and use dprintk_cont macros
   - Various cleanups to NFS v4.x to reduce code duplication and
     complexity
   - Remove unused cr_magic related code
   - Improvements to sunrpc "read from buffer" code
   - Clean up sunrpc timeout code and allow changing TCP timeout
     parameters
   - Remove duplicate mw_list management code in xprtrdma
   - Add generic functions for encoding and decoding xdr streams

  Bugfixes:
   - Clean up nfs_show_mountd_netid
   - Make layoutreturn_ops static and use NULL instead of 0 to fix
     sparse warnings
   - Properly handle -ERESTARTSYS in nfs_rename()
   - Check if register_shrinker() failed during rpcauth_init()
   - Properly clean up procfs/pipefs entries
   - Various NFS over RDMA related fixes
   - Silence unititialized variable warning in sunrpc"

* tag 'nfs-for-4.11-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (64 commits)
  NFSv4: fix getacl ERANGE for some ACL buffer sizes
  NFSv4: fix getacl head length estimation
  Revert "NFSv4.1: Handle NFS4ERR_BADSESSION/NFS4ERR_DEADSESSION replies to OP_SEQUENCE"
  NFSv4: Fix reboot recovery in copy offload
  pNFS/flexfiles: If the layout is invalid, it must be updated before retrying
  NFSv4: Clean up owner/group attribute decode
  SUNRPC: Add a helper function xdr_stream_decode_string_dup()
  NFSv4: Remove bogus "struct nfs_client" argument from decode_ace()
  NFSv4: Fix the underestimation of delegation XDR space reservation
  NFSv4: Replace callback string decode function with a generic
  NFSv4: Replace the open coded decode_opaque_inline() with the new generic
  NFSv4: Replace ad-hoc xdr encode/decode helpers with xdr_stream_* generics
  SUNRPC: Add generic helpers for xdr_stream encode/decode
  sunrpc: silence uninitialized variable warning
  nlm: Ensure callback code also checks that the files match
  sunrpc: Allow xprt->ops->timer method to sleep
  xprtrdma: Refactor management of mw_list field
  xprtrdma: Handle stale connection rejection
  xprtrdma: Properly recover FRWRs with in-flight FASTREG WRs
  xprtrdma: Shrink send SGEs array
  ...

1  2 
fs/nfs/callback_xdr.c
fs/nfs/filelayout/filelayout.c
fs/nfs/flexfilelayout/flexfilelayout.c
include/linux/sunrpc/cache.h
net/sunrpc/cache.c
net/sunrpc/xprtsock.c

diff --combined fs/nfs/callback_xdr.c
index fd0284c1dc328b92520aa0c39b4ca2a4b9899915,2ade5cb52b8efc91e0fb60473bb1a14ebc1b241c..d051fc3583a9097d46d8159860ea6c22a7ec116a
@@@ -83,23 -83,15 +83,15 @@@ static __be32 *read_buf(struct xdr_stre
        return p;
  }
  
- static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str)
+ static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len,
+               const char **str, size_t maxlen)
  {
-       __be32 *p;
-       p = read_buf(xdr, 4);
-       if (unlikely(p == NULL))
-               return htonl(NFS4ERR_RESOURCE);
-       *len = ntohl(*p);
-       if (*len != 0) {
-               p = read_buf(xdr, *len);
-               if (unlikely(p == NULL))
-                       return htonl(NFS4ERR_RESOURCE);
-               *str = (const char *)p;
-       } else
-               *str = NULL;
+       ssize_t err;
  
+       err = xdr_stream_decode_opaque_inline(xdr, (void **)str, maxlen);
+       if (err < 0)
+               return cpu_to_be32(NFS4ERR_RESOURCE);
+       *len = err;
        return 0;
  }
  
@@@ -162,15 -154,9 +154,9 @@@ static __be32 decode_compound_hdr_arg(s
        __be32 *p;
        __be32 status;
  
-       status = decode_string(xdr, &hdr->taglen, &hdr->tag);
+       status = decode_string(xdr, &hdr->taglen, &hdr->tag, CB_OP_TAGLEN_MAXSZ);
        if (unlikely(status != 0))
                return status;
-       /* We do not like overly long tags! */
-       if (hdr->taglen > CB_OP_TAGLEN_MAXSZ) {
-               printk("NFS: NFSv4 CALLBACK %s: client sent tag of length %u\n",
-                               __func__, hdr->taglen);
-               return htonl(NFS4ERR_RESOURCE);
-       }
        p = read_buf(xdr, 12);
        if (unlikely(p == NULL))
                return htonl(NFS4ERR_RESOURCE);
@@@ -582,12 -568,8 +568,8 @@@ out
  
  static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
  {
-       __be32 *p;
-       p = xdr_reserve_space(xdr, 4 + len);
-       if (unlikely(p == NULL))
-               return htonl(NFS4ERR_RESOURCE);
-       xdr_encode_opaque(p, str, len);
+       if (unlikely(xdr_stream_encode_opaque(xdr, str, len) < 0))
+               return cpu_to_be32(NFS4ERR_RESOURCE);
        return 0;
  }
  
@@@ -1083,8 -1065,7 +1065,8 @@@ struct svc_version nfs4_callback_versio
        .vs_proc = nfs4_callback_procedures1,
        .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
        .vs_dispatch = NULL,
 -      .vs_hidden = 1,
 +      .vs_hidden = true,
 +      .vs_need_cong_ctrl = true,
  };
  
  struct svc_version nfs4_callback_version4 = {
        .vs_proc = nfs4_callback_procedures1,
        .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
        .vs_dispatch = NULL,
 -      .vs_hidden = 1,
 +      .vs_hidden = true,
 +      .vs_need_cong_ctrl = true,
  };
index 18f98e08544db97757a7701838996f368c27555d,7aff350f15b1ced36e751ffd4574d91eea0a76a3..44347f4bdc1516f54f030ca9f0d95332ab816116
@@@ -305,7 -305,7 +305,7 @@@ static void filelayout_read_prepare(str
        }
        hdr->pgio_done_cb = filelayout_read_done_cb;
  
-       if (nfs41_setup_sequence(hdr->ds_clp->cl_session,
+       if (nfs4_setup_sequence(hdr->ds_clp,
                        &hdr->args.seq_args,
                        &hdr->res.seq_res,
                        task))
@@@ -403,7 -403,7 +403,7 @@@ static void filelayout_write_prepare(st
                rpc_exit(task, 0);
                return;
        }
-       if (nfs41_setup_sequence(hdr->ds_clp->cl_session,
+       if (nfs4_setup_sequence(hdr->ds_clp,
                        &hdr->args.seq_args,
                        &hdr->res.seq_res,
                        task))
@@@ -438,7 -438,7 +438,7 @@@ static void filelayout_commit_prepare(s
  {
        struct nfs_commit_data *wdata = data;
  
-       nfs41_setup_sequence(wdata->ds_clp->cl_session,
+       nfs4_setup_sequence(wdata->ds_clp,
                        &wdata->args.seq_args,
                        &wdata->res.seq_res,
                        task);
@@@ -482,7 -482,7 +482,7 @@@ filelayout_read_pagelist(struct nfs_pgi
        u32 j, idx;
        struct nfs_fh *fh;
  
 -      dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n",
 +      dprintk("--> %s ino %lu pgbase %u req %zu@%llu\n",
                __func__, hdr->inode->i_ino,
                hdr->args.pgbase, (size_t)hdr->args.count, offset);
  
@@@ -540,7 -540,7 +540,7 @@@ filelayout_write_pagelist(struct nfs_pg
        if (IS_ERR(ds_clnt))
                return PNFS_NOT_ATTEMPTED;
  
 -      dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d\n",
 +      dprintk("%s ino %lu sync %d req %zu@%llu DS: %s cl_count %d\n",
                __func__, hdr->inode->i_ino, sync, (size_t) hdr->args.count,
                offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count));
  
index d6acc688df7ed74334ae823af65a24c73f6cb4ff,78f9a3081127068e32ad7e961a73de867843df76..42dedf2d625fca62a418c5e4f82935dc41a53987
@@@ -1053,9 -1053,6 +1053,6 @@@ static int ff_layout_async_handle_error
        struct nfs_client *mds_client = mds_server->nfs_client;
        struct nfs4_slot_table *tbl = &clp->cl_session->fc_slot_table;
  
-       if (task->tk_status >= 0)
-               return 0;
        switch (task->tk_status) {
        /* MDS state errors */
        case -NFS4ERR_DELEG_REVOKED:
@@@ -1157,9 -1154,6 +1154,6 @@@ static int ff_layout_async_handle_error
  {
        struct nfs4_deviceid_node *devid = FF_LAYOUT_DEVID_NODE(lseg, idx);
  
-       if (task->tk_status >= 0)
-               return 0;
        switch (task->tk_status) {
        /* File access problems. Don't mark the device as unavailable */
        case -EACCES:
@@@ -1195,6 -1189,13 +1189,13 @@@ static int ff_layout_async_handle_error
  {
        int vers = clp->cl_nfs_mod->rpc_vers->number;
  
+       if (task->tk_status >= 0)
+               return 0;
+       /* Handle the case of an invalid layout segment */
+       if (!pnfs_is_valid_lseg(lseg))
+               return -NFS4ERR_RESET_TO_PNFS;
        switch (vers) {
        case 3:
                return ff_layout_async_handle_error_v3(task, lseg, idx);
@@@ -1384,30 -1385,14 +1385,14 @@@ static void ff_layout_read_prepare_v3(s
        rpc_call_start(task);
  }
  
- static int ff_layout_setup_sequence(struct nfs_client *ds_clp,
-                                   struct nfs4_sequence_args *args,
-                                   struct nfs4_sequence_res *res,
-                                   struct rpc_task *task)
- {
-       if (ds_clp->cl_session)
-               return nfs41_setup_sequence(ds_clp->cl_session,
-                                          args,
-                                          res,
-                                          task);
-       return nfs40_setup_sequence(ds_clp->cl_slot_tbl,
-                                  args,
-                                  res,
-                                  task);
- }
  static void ff_layout_read_prepare_v4(struct rpc_task *task, void *data)
  {
        struct nfs_pgio_header *hdr = data;
  
-       if (ff_layout_setup_sequence(hdr->ds_clp,
-                                    &hdr->args.seq_args,
-                                    &hdr->res.seq_res,
-                                    task))
+       if (nfs4_setup_sequence(hdr->ds_clp,
+                               &hdr->args.seq_args,
+                               &hdr->res.seq_res,
+                               task))
                return;
  
        if (ff_layout_read_prepare_common(task, hdr))
@@@ -1578,10 -1563,10 +1563,10 @@@ static void ff_layout_write_prepare_v4(
  {
        struct nfs_pgio_header *hdr = data;
  
-       if (ff_layout_setup_sequence(hdr->ds_clp,
-                                    &hdr->args.seq_args,
-                                    &hdr->res.seq_res,
-                                    task))
+       if (nfs4_setup_sequence(hdr->ds_clp,
+                               &hdr->args.seq_args,
+                               &hdr->res.seq_res,
+                               task))
                return;
  
        if (ff_layout_write_prepare_common(task, hdr))
@@@ -1667,10 -1652,10 +1652,10 @@@ static void ff_layout_commit_prepare_v4
  {
        struct nfs_commit_data *wdata = data;
  
-       if (ff_layout_setup_sequence(wdata->ds_clp,
-                                &wdata->args.seq_args,
-                                &wdata->res.seq_res,
-                                task))
+       if (nfs4_setup_sequence(wdata->ds_clp,
+                               &wdata->args.seq_args,
+                               &wdata->res.seq_res,
+                               task))
                return;
        ff_layout_commit_prepare_common(task, data);
  }
@@@ -1751,7 -1736,7 +1736,7 @@@ ff_layout_read_pagelist(struct nfs_pgio
        int vers;
        struct nfs_fh *fh;
  
 -      dprintk("--> %s ino %lu pgbase %u req %Zu@%llu\n",
 +      dprintk("--> %s ino %lu pgbase %u req %zu@%llu\n",
                __func__, hdr->inode->i_ino,
                hdr->args.pgbase, (size_t)hdr->args.count, offset);
  
@@@ -1828,7 -1813,7 +1813,7 @@@ ff_layout_write_pagelist(struct nfs_pgi
  
        vers = nfs4_ff_layout_ds_version(lseg, idx);
  
 -      dprintk("%s ino %lu sync %d req %Zu@%llu DS: %s cl_count %d vers %d\n",
 +      dprintk("%s ino %lu sync %d req %zu@%llu DS: %s cl_count %d vers %d\n",
                __func__, hdr->inode->i_ino, sync, (size_t) hdr->args.count,
                offset, ds->ds_remotestr, atomic_read(&ds->ds_clp->cl_count),
                vers);
@@@ -1965,10 -1950,7 +1950,7 @@@ static int ff_layout_encode_ioerr(struc
  static void
  encode_opaque_fixed(struct xdr_stream *xdr, const void *buf, size_t len)
  {
-       __be32 *p;
-       p = xdr_reserve_space(xdr, len);
-       xdr_encode_opaque_fixed(p, buf, len);
+       WARN_ON_ONCE(xdr_stream_encode_opaque_fixed(xdr, buf, len) < 0);
  }
  
  static void
@@@ -2092,7 -2074,7 +2074,7 @@@ ff_layout_free_layoutreturn(struct nfs4
        kfree(ff_args);
  }
  
- const struct nfs4_xdr_opaque_ops layoutreturn_ops = {
static const struct nfs4_xdr_opaque_ops layoutreturn_ops = {
        .encode = ff_layout_encode_layoutreturn,
        .free = ff_layout_free_layoutreturn,
  };
index 20d157a518a7dcb14763f5bfd7e317c0a6537387,bb5c9c80f12ea5c1473128098c9b9c81ac3ada85..270bad0e1bed137e9ce44727d9d79fde830e0006
@@@ -63,15 -63,6 +63,6 @@@ struct cache_head 
  
  #define       CACHE_NEW_EXPIRY 120    /* keep new things pending confirmation for 120 seconds */
  
- struct cache_detail_procfs {
-       struct proc_dir_entry   *proc_ent;
-       struct proc_dir_entry   *flush_ent, *channel_ent, *content_ent;
- };
- struct cache_detail_pipefs {
-       struct dentry *dir;
- };
  struct cache_detail {
        struct module *         owner;
        int                     hash_size;
        time_t                  last_warn;              /* when we last warned about no readers */
  
        union {
-               struct cache_detail_procfs procfs;
-               struct cache_detail_pipefs pipefs;
-       } u;
+               struct proc_dir_entry   *procfs;
+               struct dentry           *pipefs;
+       };
        struct net              *net;
  };
  
@@@ -198,17 -189,14 +189,17 @@@ static inline struct cache_head  *cache
  
  static inline void cache_put(struct cache_head *h, struct cache_detail *cd)
  {
 -      if (atomic_read(&h->ref.refcount) <= 2 &&
 +      if (kref_read(&h->ref) <= 2 &&
            h->expiry_time < cd->nextcheck)
                cd->nextcheck = h->expiry_time;
        kref_put(&h->ref, cd->cache_put);
  }
  
 -static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
 +static inline bool cache_is_expired(struct cache_detail *detail, struct cache_head *h)
  {
 +      if (!test_bit(CACHE_VALID, &h->flags))
 +              return false;
 +
        return  (h->expiry_time < seconds_since_boot()) ||
                (detail->flush_time >= h->last_refresh);
  }
@@@ -230,7 -218,6 +221,7 @@@ extern void sunrpc_destroy_cache_detail
  extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
                                        umode_t, struct cache_detail *);
  extern void sunrpc_cache_unregister_pipefs(struct cache_detail *);
 +extern void sunrpc_cache_unhash(struct cache_detail *, struct cache_head *);
  
  /* Must store cache_detail in seq_file->private if using next three functions */
  extern void *cache_seq_start(struct seq_file *file, loff_t *pos);
diff --combined net/sunrpc/cache.c
index d8639da06d9cd4815a407ef4dec4340bee68caf4,2f06f510b570ded38122be36bccf659ca3a88e18..79d55d949d9a794a1501aee45f4807e76c7bfa1d
@@@ -362,6 -362,11 +362,6 @@@ void sunrpc_destroy_cache_detail(struc
        cache_purge(cd);
        spin_lock(&cache_list_lock);
        write_lock(&cd->hash_lock);
 -      if (cd->entries) {
 -              write_unlock(&cd->hash_lock);
 -              spin_unlock(&cache_list_lock);
 -              goto out;
 -      }
        if (current_detail == cd)
                current_detail = NULL;
        list_del_init(&cd->others);
                /* module must be being unloaded so its safe to kill the worker */
                cancel_delayed_work_sync(&cache_cleaner);
        }
 -      return;
 -out:
 -      printk(KERN_ERR "RPC: failed to unregister %s cache\n", cd->name);
  }
  EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail);
  
@@@ -489,32 -497,13 +489,32 @@@ EXPORT_SYMBOL_GPL(cache_flush)
  
  void cache_purge(struct cache_detail *detail)
  {
 -      time_t now = seconds_since_boot();
 -      if (detail->flush_time >= now)
 -              now = detail->flush_time + 1;
 -      /* 'now' is the maximum value any 'last_refresh' can have */
 -      detail->flush_time = now;
 -      detail->nextcheck = seconds_since_boot();
 -      cache_flush();
 +      struct cache_head *ch = NULL;
 +      struct hlist_head *head = NULL;
 +      struct hlist_node *tmp = NULL;
 +      int i = 0;
 +
 +      write_lock(&detail->hash_lock);
 +      if (!detail->entries) {
 +              write_unlock(&detail->hash_lock);
 +              return;
 +      }
 +
 +      dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name);
 +      for (i = 0; i < detail->hash_size; i++) {
 +              head = &detail->hash_table[i];
 +              hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
 +                      hlist_del_init(&ch->cache_list);
 +                      detail->entries--;
 +
 +                      set_bit(CACHE_CLEANED, &ch->flags);
 +                      write_unlock(&detail->hash_lock);
 +                      cache_fresh_unlocked(ch, detail);
 +                      cache_put(ch, detail);
 +                      write_lock(&detail->hash_lock);
 +              }
 +      }
 +      write_unlock(&detail->hash_lock);
  }
  EXPORT_SYMBOL_GPL(cache_purge);
  
@@@ -728,7 -717,7 +728,7 @@@ void cache_clean_deferred(void *owner
  /*
   * communicate with user-space
   *
-  * We have a magic /proc file - /proc/sunrpc/<cachename>/channel.
+  * We have a magic /proc file - /proc/net/rpc/<cachename>/channel.
   * On read, you get a full request, or block.
   * On write, an update request is processed.
   * Poll works if anything to read, and always allows write.
@@@ -1283,7 -1272,7 +1283,7 @@@ EXPORT_SYMBOL_GPL(qword_get)
  
  
  /*
-  * support /proc/sunrpc/cache/$CACHENAME/content
+  * support /proc/net/rpc/$CACHENAME/content
   * as a seqfile.
   * We call ->cache_show passing NULL for the item to
   * get a header, then pass each real item in the cache
@@@ -1369,7 -1358,7 +1369,7 @@@ static int c_show(struct seq_file *m, v
        ifdebug(CACHE)
                seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n",
                           convert_to_wallclock(cp->expiry_time),
 -                         atomic_read(&cp->ref.refcount), cp->flags);
 +                         kref_read(&cp->ref), cp->flags);
        cache_get(cp);
        if (cache_check(cd, cp, NULL))
                /* cache_check does a cache_put on failure */
@@@ -1438,20 -1427,11 +1438,11 @@@ static ssize_t read_flush(struct file *
                          struct cache_detail *cd)
  {
        char tbuf[22];
-       unsigned long p = *ppos;
        size_t len;
  
-       snprintf(tbuf, sizeof(tbuf), "%lu\n", convert_to_wallclock(cd->flush_time));
-       len = strlen(tbuf);
-       if (p >= len)
-               return 0;
-       len -= p;
-       if (len > count)
-               len = count;
-       if (copy_to_user(buf, (void*)(tbuf+p), len))
-               return -EFAULT;
-       *ppos += len;
-       return len;
+       len = snprintf(tbuf, sizeof(tbuf), "%lu\n",
+                       convert_to_wallclock(cd->flush_time));
+       return simple_read_from_buffer(buf, count, ppos, tbuf, len);
  }
  
  static ssize_t write_flush(struct file *file, const char __user *buf,
@@@ -1611,21 -1591,12 +1602,12 @@@ static const struct file_operations cac
        .llseek         = no_llseek,
  };
  
- static void remove_cache_proc_entries(struct cache_detail *cd, struct net *net)
+ static void remove_cache_proc_entries(struct cache_detail *cd)
  {
-       struct sunrpc_net *sn;
-       if (cd->u.procfs.proc_ent == NULL)
-               return;
-       if (cd->u.procfs.flush_ent)
-               remove_proc_entry("flush", cd->u.procfs.proc_ent);
-       if (cd->u.procfs.channel_ent)
-               remove_proc_entry("channel", cd->u.procfs.proc_ent);
-       if (cd->u.procfs.content_ent)
-               remove_proc_entry("content", cd->u.procfs.proc_ent);
-       cd->u.procfs.proc_ent = NULL;
-       sn = net_generic(net, sunrpc_net_id);
-       remove_proc_entry(cd->name, sn->proc_net_rpc);
+       if (cd->procfs) {
+               proc_remove(cd->procfs);
+               cd->procfs = NULL;
+       }
  }
  
  #ifdef CONFIG_PROC_FS
@@@ -1635,38 -1606,30 +1617,30 @@@ static int create_cache_proc_entries(st
        struct sunrpc_net *sn;
  
        sn = net_generic(net, sunrpc_net_id);
-       cd->u.procfs.proc_ent = proc_mkdir(cd->name, sn->proc_net_rpc);
-       if (cd->u.procfs.proc_ent == NULL)
+       cd->procfs = proc_mkdir(cd->name, sn->proc_net_rpc);
+       if (cd->procfs == NULL)
                goto out_nomem;
-       cd->u.procfs.channel_ent = NULL;
-       cd->u.procfs.content_ent = NULL;
  
        p = proc_create_data("flush", S_IFREG|S_IRUSR|S_IWUSR,
-                            cd->u.procfs.proc_ent,
-                            &cache_flush_operations_procfs, cd);
-       cd->u.procfs.flush_ent = p;
+                            cd->procfs, &cache_flush_operations_procfs, cd);
        if (p == NULL)
                goto out_nomem;
  
        if (cd->cache_request || cd->cache_parse) {
                p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR,
-                                    cd->u.procfs.proc_ent,
-                                    &cache_file_operations_procfs, cd);
-               cd->u.procfs.channel_ent = p;
+                               cd->procfs, &cache_file_operations_procfs, cd);
                if (p == NULL)
                        goto out_nomem;
        }
        if (cd->cache_show) {
                p = proc_create_data("content", S_IFREG|S_IRUSR,
-                               cd->u.procfs.proc_ent,
-                               &content_file_operations_procfs, cd);
-               cd->u.procfs.content_ent = p;
+                               cd->procfs, &content_file_operations_procfs, cd);
                if (p == NULL)
                        goto out_nomem;
        }
        return 0;
  out_nomem:
-       remove_cache_proc_entries(cd, net);
+       remove_cache_proc_entries(cd);
        return -ENOMEM;
  }
  #else /* CONFIG_PROC_FS */
@@@ -1695,7 -1658,7 +1669,7 @@@ EXPORT_SYMBOL_GPL(cache_register_net)
  
  void cache_unregister_net(struct cache_detail *cd, struct net *net)
  {
-       remove_cache_proc_entries(cd, net);
+       remove_cache_proc_entries(cd);
        sunrpc_destroy_cache_detail(cd);
  }
  EXPORT_SYMBOL_GPL(cache_unregister_net);
@@@ -1854,27 -1817,17 +1828,29 @@@ int sunrpc_cache_register_pipefs(struc
        struct dentry *dir = rpc_create_cache_dir(parent, name, umode, cd);
        if (IS_ERR(dir))
                return PTR_ERR(dir);
-       cd->u.pipefs.dir = dir;
+       cd->pipefs = dir;
        return 0;
  }
  EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
  
  void sunrpc_cache_unregister_pipefs(struct cache_detail *cd)
  {
-       rpc_remove_cache_dir(cd->u.pipefs.dir);
-       cd->u.pipefs.dir = NULL;
+       if (cd->pipefs) {
+               rpc_remove_cache_dir(cd->pipefs);
+               cd->pipefs = NULL;
+       }
  }
  EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
  
 +void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
 +{
 +      write_lock(&cd->hash_lock);
 +      if (!hlist_unhashed(&h->cache_list)){
 +              hlist_del_init(&h->cache_list);
 +              cd->entries--;
 +              write_unlock(&cd->hash_lock);
 +              cache_put(h, cd);
 +      } else
 +              write_unlock(&cd->hash_lock);
 +}
 +EXPORT_SYMBOL_GPL(sunrpc_cache_unhash);
diff --combined net/sunrpc/xprtsock.c
index 956c7bce80d1b2184c6a61708052d33b7e9bea2d,5cbabf2c75b2243eb36ba65bdb24cd4f238783b4..16aff8ddc16f8f3e66e31a86ce227b3ac49857bf
@@@ -52,6 -52,8 +52,8 @@@
  #include "sunrpc.h"
  
  static void xs_close(struct rpc_xprt *xprt);
+ static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
+               struct socket *sock);
  
  /*
   * xprtsock tunables
@@@ -666,6 -668,9 +668,9 @@@ static int xs_tcp_send_request(struct r
        if (task->tk_flags & RPC_TASK_SENT)
                zerocopy = false;
  
+       if (test_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state))
+               xs_tcp_set_socket_timeouts(xprt, transport->sock);
        /* Continue transmitting the packet/record. We must be careful
         * to cope with writespace callbacks arriving _after_ we have
         * called sendmsg(). */
@@@ -1188,7 -1193,7 +1193,7 @@@ static inline void xs_tcp_read_xid(stru
        char *p;
  
        len = sizeof(transport->tcp_xid) - transport->tcp_offset;
 -      dprintk("RPC:       reading XID (%Zu bytes)\n", len);
 +      dprintk("RPC:       reading XID (%zu bytes)\n", len);
        p = ((char *) &transport->tcp_xid) + transport->tcp_offset;
        used = xdr_skb_read_bits(desc, p, len);
        transport->tcp_offset += used;
@@@ -1219,7 -1224,7 +1224,7 @@@ static inline void xs_tcp_read_calldir(
         */
        offset = transport->tcp_offset - sizeof(transport->tcp_xid);
        len = sizeof(transport->tcp_calldir) - offset;
 -      dprintk("RPC:       reading CALL/REPLY flag (%Zu bytes)\n", len);
 +      dprintk("RPC:       reading CALL/REPLY flag (%zu bytes)\n", len);
        p = ((char *) &transport->tcp_calldir) + offset;
        used = xdr_skb_read_bits(desc, p, len);
        transport->tcp_offset += used;
@@@ -1310,7 -1315,7 +1315,7 @@@ static inline void xs_tcp_read_common(s
                return;
        }
  
 -      dprintk("RPC:       XID %08x read %Zd bytes\n",
 +      dprintk("RPC:       XID %08x read %zd bytes\n",
                        ntohl(transport->tcp_xid), r);
        dprintk("RPC:       xprt = %p, tcp_copied = %lu, tcp_offset = %u, "
                        "tcp_reclen = %u\n", xprt, transport->tcp_copied,
@@@ -1456,7 -1461,7 +1461,7 @@@ static inline void xs_tcp_read_discard(
        desc->count -= len;
        desc->offset += len;
        transport->tcp_offset += len;
 -      dprintk("RPC:       discarded %Zu bytes\n", len);
 +      dprintk("RPC:       discarded %zu bytes\n", len);
        xs_tcp_check_fraghdr(transport);
  }
  
@@@ -1734,7 -1739,9 +1739,9 @@@ static void xs_udp_set_buffer_size(stru
   */
  static void xs_udp_timer(struct rpc_xprt *xprt, struct rpc_task *task)
  {
+       spin_lock_bh(&xprt->transport_lock);
        xprt_adjust_cwnd(xprt, task, -ETIMEDOUT);
+       spin_unlock_bh(&xprt->transport_lock);
  }
  
  static unsigned short xs_get_random_port(void)
@@@ -2235,6 -2242,66 +2242,66 @@@ static void xs_tcp_shutdown(struct rpc_
                xs_reset_transport(transport);
  }
  
+ static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
+               struct socket *sock)
+ {
+       struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
+       unsigned int keepidle;
+       unsigned int keepcnt;
+       unsigned int opt_on = 1;
+       unsigned int timeo;
+       spin_lock_bh(&xprt->transport_lock);
+       keepidle = DIV_ROUND_UP(xprt->timeout->to_initval, HZ);
+       keepcnt = xprt->timeout->to_retries + 1;
+       timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
+               (xprt->timeout->to_retries + 1);
+       clear_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
+       spin_unlock_bh(&xprt->transport_lock);
+       /* TCP Keepalive options */
+       kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
+                       (char *)&opt_on, sizeof(opt_on));
+       kernel_setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,
+                       (char *)&keepidle, sizeof(keepidle));
+       kernel_setsockopt(sock, SOL_TCP, TCP_KEEPINTVL,
+                       (char *)&keepidle, sizeof(keepidle));
+       kernel_setsockopt(sock, SOL_TCP, TCP_KEEPCNT,
+                       (char *)&keepcnt, sizeof(keepcnt));
+       /* TCP user timeout (see RFC5482) */
+       kernel_setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT,
+                       (char *)&timeo, sizeof(timeo));
+ }
+ static void xs_tcp_set_connect_timeout(struct rpc_xprt *xprt,
+               unsigned long connect_timeout,
+               unsigned long reconnect_timeout)
+ {
+       struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
+       struct rpc_timeout to;
+       unsigned long initval;
+       spin_lock_bh(&xprt->transport_lock);
+       if (reconnect_timeout < xprt->max_reconnect_timeout)
+               xprt->max_reconnect_timeout = reconnect_timeout;
+       if (connect_timeout < xprt->connect_timeout) {
+               memcpy(&to, xprt->timeout, sizeof(to));
+               initval = DIV_ROUND_UP(connect_timeout, to.to_retries + 1);
+               /* Arbitrary lower limit */
+               if (initval <  XS_TCP_INIT_REEST_TO << 1)
+                       initval = XS_TCP_INIT_REEST_TO << 1;
+               to.to_initval = initval;
+               to.to_maxval = initval;
+               memcpy(&transport->tcp_timeout, &to,
+                               sizeof(transport->tcp_timeout));
+               xprt->timeout = &transport->tcp_timeout;
+               xprt->connect_timeout = connect_timeout;
+       }
+       set_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
+       spin_unlock_bh(&xprt->transport_lock);
+ }
  static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
  {
        struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
  
        if (!transport->inet) {
                struct sock *sk = sock->sk;
-               unsigned int keepidle = xprt->timeout->to_initval / HZ;
-               unsigned int keepcnt = xprt->timeout->to_retries + 1;
-               unsigned int opt_on = 1;
-               unsigned int timeo;
                unsigned int addr_pref = IPV6_PREFER_SRC_PUBLIC;
  
-               /* TCP Keepalive options */
-               kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
-                               (char *)&opt_on, sizeof(opt_on));
-               kernel_setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,
-                               (char *)&keepidle, sizeof(keepidle));
-               kernel_setsockopt(sock, SOL_TCP, TCP_KEEPINTVL,
-                               (char *)&keepidle, sizeof(keepidle));
-               kernel_setsockopt(sock, SOL_TCP, TCP_KEEPCNT,
-                               (char *)&keepcnt, sizeof(keepcnt));
                /* Avoid temporary address, they are bad for long-lived
                 * connections such as NFS mounts.
                 * RFC4941, section 3.6 suggests that:
                kernel_setsockopt(sock, SOL_IPV6, IPV6_ADDR_PREFERENCES,
                                (char *)&addr_pref, sizeof(addr_pref));
  
-               /* TCP user timeout (see RFC5482) */
-               timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
-                       (xprt->timeout->to_retries + 1);
-               kernel_setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT,
-                               (char *)&timeo, sizeof(timeo));
+               xs_tcp_set_socket_timeouts(xprt, sock);
  
                write_lock_bh(&sk->sk_callback_lock);
  
@@@ -2721,6 -2770,7 +2770,7 @@@ static struct rpc_xprt_ops xs_tcp_ops 
        .set_retrans_timeout    = xprt_set_retrans_timeout_def,
        .close                  = xs_tcp_shutdown,
        .destroy                = xs_destroy,
+       .set_connect_timeout    = xs_tcp_set_connect_timeout,
        .print_stats            = xs_tcp_print_stats,
        .enable_swap            = xs_enable_swap,
        .disable_swap           = xs_disable_swap,
@@@ -3007,6 -3057,8 +3057,8 @@@ static struct rpc_xprt *xs_setup_tcp(st
        xprt->timeout = &xs_tcp_default_timeout;
  
        xprt->max_reconnect_timeout = xprt->timeout->to_maxval;
+       xprt->connect_timeout = xprt->timeout->to_initval *
+               (xprt->timeout->to_retries + 1);
  
        INIT_WORK(&transport->recv_worker, xs_tcp_data_receive_workfn);
        INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket);
@@@ -3209,7 -3261,9 +3261,9 @@@ static int param_set_uint_minmax(const 
        if (!val)
                return -EINVAL;
        ret = kstrtouint(val, 0, &num);
-       if (ret == -EINVAL || num < min || num > max)
+       if (ret)
+               return ret;
+       if (num < min || num > max)
                return -EINVAL;
        *((unsigned int *)kp->arg) = num;
        return 0;