Merge branch 'for-3.8' of git://linux-nfs.org/~bfields/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 20 Dec 2012 22:04:11 +0000 (14:04 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 20 Dec 2012 22:04:11 +0000 (14:04 -0800)
Pull nfsd update from Bruce Fields:
 "Included this time:

   - more nfsd containerization work from Stanislav Kinsbursky: we're
     not quite there yet, but should be by 3.9.

   - NFSv4.1 progress: implementation of basic backchannel security
     negotiation and the mandatory BACKCHANNEL_CTL operation.  See

       http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues

     for remaining TODO's

   - Fixes for some bugs that could be triggered by unusual compounds.
     Our xdr code wasn't designed with v4 compounds in mind, and it
     shows.  A more thorough rewrite is still a todo.

   - If you've ever seen "RPC: multiple fragments per record not
     supported" logged while using some sort of odd userland NFS client,
     that should now be fixed.

   - Further work from Jeff Layton on our mechanism for storing
     information about NFSv4 clients across reboots.

   - Further work from Bryan Schumaker on his fault-injection mechanism
     (which allows us to discard selective NFSv4 state, to excercise
     rarely-taken recovery code paths in the client.)

   - The usual mix of miscellaneous bugs and cleanup.

  Thanks to everyone who tested or contributed this cycle."

* 'for-3.8' of git://linux-nfs.org/~bfields/linux: (111 commits)
  nfsd4: don't leave freed stateid hashed
  nfsd4: free_stateid can use the current stateid
  nfsd4: cleanup: replace rq_resused count by rq_next_page pointer
  nfsd: warn on odd reply state in nfsd_vfs_read
  nfsd4: fix oops on unusual readlike compound
  nfsd4: disable zero-copy on non-final read ops
  svcrpc: fix some printks
  NFSD: Correct the size calculation in fault_inject_write
  NFSD: Pass correct buffer size to rpc_ntop
  nfsd: pass proper net to nfsd_destroy() from NFSd kthreads
  nfsd: simplify service shutdown
  nfsd: replace boolean nfsd_up flag by users counter
  nfsd: simplify NFSv4 state init and shutdown
  nfsd: introduce helpers for generic resources init and shutdown
  nfsd: make NFSd service structure allocated per net
  nfsd: make NFSd service boot time per-net
  nfsd: per-net NFSd up flag introduced
  nfsd: move per-net startup code to separated function
  nfsd: pass net to __write_ports() and down
  nfsd: pass net to nfsd_set_nrthreads()
  ...

1  2 
fs/exportfs/expfs.c
fs/fhandle.c
include/linux/exportfs.h
net/sunrpc/rpcb_clnt.c
net/sunrpc/svc.c
net/sunrpc/svcsock.c

diff --combined fs/exportfs/expfs.c
index 606bb074c501842a1cf11df97464002e6d098f30,f1f1c59c2966cc6aac49fa103cad3012bb495044..5df4bb4aab145dff9edc58408f2c1d923a5c8552
@@@ -322,10 -322,10 +322,10 @@@ static int export_encode_fh(struct inod
  
        if (parent && (len < 4)) {
                *max_len = 4;
-               return 255;
+               return FILEID_INVALID;
        } else if (len < 2) {
                *max_len = 2;
-               return 255;
+               return FILEID_INVALID;
        }
  
        len = 2;
        return type;
  }
  
 +int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
 +                           int *max_len, struct inode *parent)
 +{
 +      const struct export_operations *nop = inode->i_sb->s_export_op;
 +
 +      if (nop && nop->encode_fh)
 +              return nop->encode_fh(inode, fid->raw, max_len, parent);
 +
 +      return export_encode_fh(inode, fid, max_len, parent);
 +}
 +EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh);
 +
  int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len,
                int connectable)
  {
 -      const struct export_operations *nop = dentry->d_sb->s_export_op;
        int error;
        struct dentry *p = NULL;
        struct inode *inode = dentry->d_inode, *parent = NULL;
                 */
                parent = p->d_inode;
        }
 -      if (nop->encode_fh)
 -              error = nop->encode_fh(inode, fid->raw, max_len, parent);
 -      else
 -              error = export_encode_fh(inode, fid, max_len, parent);
 +
 +      error = exportfs_encode_inode_fh(inode, fid, max_len, parent);
        dput(p);
  
        return error;
diff --combined fs/fhandle.c
index cccdc874bb55df99eb10f89f33a7bcb1f92ddc99,26f12b95702a35f4ac5533936859a9ab449bb096..999ff5c3cab0edacd585447132180d5c35554e3c
@@@ -22,7 -22,7 +22,7 @@@ static long do_sys_name_to_handle(struc
        struct file_handle *handle = NULL;
  
        /*
 -       * We need t make sure wether the file system
 +       * We need to make sure whether the file system
         * support decoding of the file handle
         */
        if (!path->dentry->d_sb->s_export_op ||
@@@ -40,7 -40,7 +40,7 @@@
        if (!handle)
                return -ENOMEM;
  
 -      /* convert handle size to  multiple of sizeof(u32) */
 +      /* convert handle size to multiple of sizeof(u32) */
        handle_dwords = f_handle.handle_bytes >> 2;
  
        /* we ask for a non connected handle */
@@@ -52,7 -52,7 +52,7 @@@
        handle_bytes = handle_dwords * sizeof(u32);
        handle->handle_bytes = handle_bytes;
        if ((handle->handle_bytes > f_handle.handle_bytes) ||
-           (retval == 255) || (retval == -ENOSPC)) {
+           (retval == FILEID_INVALID) || (retval == -ENOSPC)) {
                /* As per old exportfs_encode_fh documentation
                 * we could return ENOSPC to indicate overflow
                 * But file system returned 255 always. So handle
diff --combined include/linux/exportfs.h
index c7e6b6392ab8e28a92d69b17ea17a88c72a935a3,0e14525463005cb1c18f0622862103449578e0df..5b9b5b317180ceff7ac1d3d31e639e66c2f9b80f
@@@ -83,6 -83,11 +83,11 @@@ enum fid_type 
         * 64 bit parent inode number.
         */
        FILEID_NILFS_WITH_PARENT = 0x62,
+       /*
+        * Filesystems must not use 0xff file ID.
+        */
+       FILEID_INVALID = 0xff,
  };
  
  struct fid {
@@@ -177,8 -182,6 +182,8 @@@ struct export_operations 
        int (*commit_metadata)(struct inode *inode);
  };
  
 +extern int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
 +                                  int *max_len, struct inode *parent);
  extern int exportfs_encode_fh(struct dentry *dentry, struct fid *fid,
        int *max_len, int connectable);
  extern struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
diff --combined net/sunrpc/rpcb_clnt.c
index 411f332de0b316b2ae33bda6e9385a498bd7fabd,109a67a37a8c35fde292c5948b98a25bb0e8abcf..795a0f4e920bf8b4e52e78458b210fea02df8132
@@@ -23,7 -23,6 +23,6 @@@
  #include <linux/errno.h>
  #include <linux/mutex.h>
  #include <linux/slab.h>
- #include <linux/nsproxy.h>
  #include <net/ipv6.h>
  
  #include <linux/sunrpc/clnt.h>
@@@ -884,10 -883,7 +883,10 @@@ static void encode_rpcb_string(struct x
        u32 len;
  
        len = strlen(string);
 -      BUG_ON(len > maxstrlen);
 +      WARN_ON_ONCE(len > maxstrlen);
 +      if (len > maxstrlen)
 +              /* truncate and hope for the best */
 +              len = maxstrlen;
        p = xdr_reserve_space(xdr, 4 + len);
        xdr_encode_opaque(p, string, len);
  }
diff --combined net/sunrpc/svc.c
index dfa4ba69ff4503748ed22bad836643540221af39,c6abf1a6ba956230bd65c97df4fb28d9c93f6edd..dbf12ac5ecb7f611fac1a06d5db643a7a5a39ac4
@@@ -20,7 -20,6 +20,6 @@@
  #include <linux/module.h>
  #include <linux/kthread.h>
  #include <linux/slab.h>
- #include <linux/nsproxy.h>
  
  #include <linux/sunrpc/types.h>
  #include <linux/sunrpc/xdr.h>
@@@ -324,9 -323,7 +323,9 @@@ svc_pool_map_set_cpumask(struct task_st
         * The caller checks for sv_nrpools > 1, which
         * implies that we've been initialized.
         */
 -      BUG_ON(m->count == 0);
 +      WARN_ON_ONCE(m->count == 0);
 +      if (m->count == 0)
 +              return;
  
        switch (m->mode) {
        case SVC_POOL_PERCPU:
@@@ -587,9 -584,7 +586,9 @@@ svc_init_buffer(struct svc_rqst *rqstp
                                       * We assume one is at most one page
                                       */
        arghi = 0;
 -      BUG_ON(pages > RPCSVC_MAXPAGES);
 +      WARN_ON_ONCE(pages > RPCSVC_MAXPAGES);
 +      if (pages > RPCSVC_MAXPAGES)
 +              pages = RPCSVC_MAXPAGES;
        while (pages) {
                struct page *p = alloc_pages_node(node, GFP_KERNEL, 0);
                if (!p)
@@@ -950,9 -945,7 +949,9 @@@ int svc_register(const struct svc_serv 
        unsigned int            i;
        int                     error = 0;
  
 -      BUG_ON(proto == 0 && port == 0);
 +      WARN_ON_ONCE(proto == 0 && port == 0);
 +      if (proto == 0 && port == 0)
 +              return -EINVAL;
  
        for (progp = serv->sv_program; progp; progp = progp->pg_next) {
                for (i = 0; i < progp->pg_nvers; i++) {
@@@ -1041,7 -1034,7 +1040,7 @@@ static void svc_unregister(const struc
  }
  
  /*
-  * Printk the given error with the address of the client that caused it.
+  * dprintk the given error with the address of the client that caused it.
   */
  static __printf(2, 3)
  void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
        vaf.fmt = fmt;
        vaf.va = &args;
  
-       net_warn_ratelimited("svc: %s: %pV",
-                            svc_print_addr(rqstp, buf, sizeof(buf)), &vaf);
+       dprintk("svc: %s: %pV", svc_print_addr(rqstp, buf, sizeof(buf)), &vaf);
  
        va_end(args);
  }
@@@ -1305,7 -1297,7 +1303,7 @@@ svc_process(struct svc_rqst *rqstp
         * Setup response xdr_buf.
         * Initially it has just one page
         */
-       rqstp->rq_resused = 1;
+       rqstp->rq_next_page = &rqstp->rq_respages[1];
        resv->iov_base = page_address(rqstp->rq_respages[0]);
        resv->iov_len = 0;
        rqstp->rq_res.pages = rqstp->rq_respages + 1;
diff --combined net/sunrpc/svcsock.c
index cc3020d1678905a9f6930cab28753dfbbe99713e,dcd5669c5154b0f7e5a711e6a117d6419c8c1f0a..0a148c9d2a5ce7b3915df8c10f5e5f0913b99c25
@@@ -84,11 -84,7 +84,11 @@@ static struct lock_class_key svc_slock_
  static void svc_reclassify_socket(struct socket *sock)
  {
        struct sock *sk = sock->sk;
 -      BUG_ON(sock_owned_by_user(sk));
 +
 +      WARN_ON_ONCE(sock_owned_by_user(sk));
 +      if (sock_owned_by_user(sk))
 +              return;
 +
        switch (sk->sk_family) {
        case AF_INET:
                sock_lock_init_class_and_name(sk, "slock-AF_INET-NFSD",
@@@ -605,6 -601,7 +605,7 @@@ static int svc_udp_recvfrom(struct svc_
                rqstp->rq_respages = rqstp->rq_pages + 1 +
                        DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE);
        }
+       rqstp->rq_next_page = rqstp->rq_respages+1;
  
        if (serv->sv_stats)
                serv->sv_stats->netudpcnt++;
@@@ -878,9 -875,9 +879,9 @@@ static unsigned int svc_tcp_restore_pag
  {
        unsigned int i, len, npages;
  
-       if (svsk->sk_tcplen <= sizeof(rpc_fraghdr))
+       if (svsk->sk_datalen == 0)
                return 0;
-       len = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+       len = svsk->sk_datalen;
        npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
        for (i = 0; i < npages; i++) {
                if (rqstp->rq_pages[i] != NULL)
@@@ -897,9 -894,9 +898,9 @@@ static void svc_tcp_save_pages(struct s
  {
        unsigned int i, len, npages;
  
-       if (svsk->sk_tcplen <= sizeof(rpc_fraghdr))
+       if (svsk->sk_datalen == 0)
                return;
-       len = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+       len = svsk->sk_datalen;
        npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
        for (i = 0; i < npages; i++) {
                svsk->sk_pages[i] = rqstp->rq_pages[i];
@@@ -911,9 -908,9 +912,9 @@@ static void svc_tcp_clear_pages(struct 
  {
        unsigned int i, len, npages;
  
-       if (svsk->sk_tcplen <= sizeof(rpc_fraghdr))
+       if (svsk->sk_datalen == 0)
                goto out;
-       len = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+       len = svsk->sk_datalen;
        npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
        for (i = 0; i < npages; i++) {
                BUG_ON(svsk->sk_pages[i] == NULL);
        }
  out:
        svsk->sk_tcplen = 0;
+       svsk->sk_datalen = 0;
  }
  
  /*
-  * Receive data.
+  * Receive fragment record header.
   * If we haven't gotten the record length yet, get the next four bytes.
-  * Otherwise try to gobble up as much as possible up to the complete
-  * record length.
   */
  static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp)
  {
                        return -EAGAIN;
                }
  
-               svsk->sk_reclen = ntohl(svsk->sk_reclen);
-               if (!(svsk->sk_reclen & RPC_LAST_STREAM_FRAGMENT)) {
-                       /* FIXME: technically, a record can be fragmented,
-                        *  and non-terminal fragments will not have the top
-                        *  bit set in the fragment length header.
-                        *  But apparently no known nfs clients send fragmented
-                        *  records. */
-                       net_notice_ratelimited("RPC: multiple fragments per record not supported\n");
-                       goto err_delete;
-               }
-               svsk->sk_reclen &= RPC_FRAGMENT_SIZE_MASK;
-               dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen);
-               if (svsk->sk_reclen > serv->sv_max_mesg) {
-                       net_notice_ratelimited("RPC: fragment too large: 0x%08lx\n",
-                                              (unsigned long)svsk->sk_reclen);
+               dprintk("svc: TCP record, %d bytes\n", svc_sock_reclen(svsk));
+               if (svc_sock_reclen(svsk) + svsk->sk_datalen >
+                                                       serv->sv_max_mesg) {
+                       net_notice_ratelimited("RPC: fragment too large: %d\n",
+                                       svc_sock_reclen(svsk));
                        goto err_delete;
                }
        }
  
-       if (svsk->sk_reclen < 8)
-               goto err_delete; /* client is nuts. */
-       len = svsk->sk_reclen;
-       return len;
+       return svc_sock_reclen(svsk);
  error:
        dprintk("RPC: TCP recv_record got %d\n", len);
        return len;
@@@ -1023,7 -1003,7 +1007,7 @@@ static int receive_cb_reply(struct svc_
        if (dst->iov_len < src->iov_len)
                return -EAGAIN; /* whatever; just giving up. */
        memcpy(dst->iov_base, src->iov_base, src->iov_len);
-       xprt_complete_rqst(req->rq_task, svsk->sk_reclen);
+       xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len);
        rqstp->rq_arg.len = 0;
        return 0;
  }
@@@ -1042,6 -1022,17 +1026,17 @@@ static int copy_pages_to_kvecs(struct k
        return i;
  }
  
+ static void svc_tcp_fragment_received(struct svc_sock *svsk)
+ {
+       /* If we have more data, signal svc_xprt_enqueue() to try again */
+       if (svc_recv_available(svsk) > sizeof(rpc_fraghdr))
+               set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+       dprintk("svc: TCP %s record (%d bytes)\n",
+               svc_sock_final_rec(svsk) ? "final" : "nonfinal",
+               svc_sock_reclen(svsk));
+       svsk->sk_tcplen = 0;
+       svsk->sk_reclen = 0;
+ }
  
  /*
   * Receive data from a TCP socket.
@@@ -1068,29 -1059,39 +1063,39 @@@ static int svc_tcp_recvfrom(struct svc_
                goto error;
  
        base = svc_tcp_restore_pages(svsk, rqstp);
-       want = svsk->sk_reclen - base;
+       want = svc_sock_reclen(svsk) - (svsk->sk_tcplen - sizeof(rpc_fraghdr));
  
        vec = rqstp->rq_vec;
  
        pnum = copy_pages_to_kvecs(&vec[0], &rqstp->rq_pages[0],
-                                               svsk->sk_reclen);
+                                               svsk->sk_datalen + want);
  
        rqstp->rq_respages = &rqstp->rq_pages[pnum];
+       rqstp->rq_next_page = rqstp->rq_respages + 1;
  
        /* Now receive data */
        len = svc_partial_recvfrom(rqstp, vec, pnum, want, base);
-       if (len >= 0)
+       if (len >= 0) {
                svsk->sk_tcplen += len;
-       if (len != want) {
+               svsk->sk_datalen += len;
+       }
+       if (len != want || !svc_sock_final_rec(svsk)) {
                svc_tcp_save_pages(svsk, rqstp);
                if (len < 0 && len != -EAGAIN)
-                       goto err_other;
-               dprintk("svc: incomplete TCP record (%d of %d)\n",
-                       svsk->sk_tcplen, svsk->sk_reclen);
+                       goto err_delete;
+               if (len == want)
+                       svc_tcp_fragment_received(svsk);
+               else
+                       dprintk("svc: incomplete TCP record (%d of %d)\n",
+                               (int)(svsk->sk_tcplen - sizeof(rpc_fraghdr)),
+                               svc_sock_reclen(svsk));
                goto err_noclose;
        }
  
-       rqstp->rq_arg.len = svsk->sk_reclen;
+       if (svc_sock_reclen(svsk) < 8)
+               goto err_delete; /* client is nuts. */
+       rqstp->rq_arg.len = svsk->sk_datalen;
        rqstp->rq_arg.page_base = 0;
        if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
                rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
                len = receive_cb_reply(svsk, rqstp);
  
        /* Reset TCP read info */
-       svsk->sk_reclen = 0;
-       svsk->sk_tcplen = 0;
-       /* If we have more data, signal svc_xprt_enqueue() to try again */
-       if (svc_recv_available(svsk) > sizeof(rpc_fraghdr))
-               set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+       svsk->sk_datalen = 0;
+       svc_tcp_fragment_received(svsk);
  
        if (len < 0)
                goto error;
        if (serv->sv_stats)
                serv->sv_stats->nettcpcnt++;
  
-       dprintk("svc: TCP complete record (%d bytes)\n", rqstp->rq_arg.len);
        return rqstp->rq_arg.len;
  
  error:
        if (len != -EAGAIN)
-               goto err_other;
+               goto err_delete;
        dprintk("RPC: TCP recvfrom got EAGAIN\n");
        return 0;
- err_other:
+ err_delete:
        printk(KERN_NOTICE "%s: recvfrom returned errno %d\n",
               svsk->sk_xprt.xpt_server->sv_name, -len);
        set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags);
@@@ -1305,6 -1302,7 +1306,7 @@@ static void svc_tcp_init(struct svc_soc
  
                svsk->sk_reclen = 0;
                svsk->sk_tcplen = 0;
+               svsk->sk_datalen = 0;
                memset(&svsk->sk_pages[0], 0, sizeof(svsk->sk_pages));
  
                tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF;