NFS: Use cached page as buffer for NFS symlink requests
Chuck Lever [Wed, 23 Aug 2006 00:06:23 +0000 (20:06 -0400)]
Now that we have a copy of the symlink path in the page cache, we can pass
a struct page down to the XDR routines instead of a string buffer.

Test plan:
Connectathon, all NFS versions.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>

fs/nfs/dir.c
fs/nfs/nfs2xdr.c
fs/nfs/nfs3proc.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
fs/nfs/proc.c
include/linux/nfs_xdr.h

index b483e5d..51328ae 100644 (file)
@@ -1464,10 +1464,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
        char *kaddr;
        struct iattr attr;
        unsigned int pathlen = strlen(symname);
-       struct qstr qsymname = {
-               .name   = symname,
-               .len    = pathlen,
-       };
        int error;
 
        dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
@@ -1493,10 +1489,8 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
                memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
        kunmap_atomic(kaddr, KM_USER0);
 
-       /* XXX: eventually this will pass in {page, pathlen},
-        *      instead of qsymname; need XDR changes for that */
        nfs_begin_data_update(dir);
-       error = NFS_PROTO(dir)->symlink(dir, dentry, &qsymname, &attr);
+       error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
        nfs_end_data_update(dir);
        if (error != 0) {
                dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
index 67391ee..b49501f 100644 (file)
@@ -51,7 +51,7 @@
 #define NFS_createargs_sz      (NFS_diropargs_sz+NFS_sattr_sz)
 #define NFS_renameargs_sz      (NFS_diropargs_sz+NFS_diropargs_sz)
 #define NFS_linkargs_sz                (NFS_fhandle_sz+NFS_diropargs_sz)
-#define NFS_symlinkargs_sz     (NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz)
+#define NFS_symlinkargs_sz     (NFS_diropargs_sz+1+NFS_sattr_sz)
 #define NFS_readdirargs_sz     (NFS_fhandle_sz+2)
 
 #define NFS_attrstat_sz                (1+NFS_fattr_sz)
@@ -351,11 +351,26 @@ nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
 static int
 nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
 {
+       struct xdr_buf *sndbuf = &req->rq_snd_buf;
+       size_t pad;
+
        p = xdr_encode_fhandle(p, args->fromfh);
        p = xdr_encode_array(p, args->fromname, args->fromlen);
-       p = xdr_encode_array(p, args->topath, args->tolen);
+       *p++ = htonl(args->pathlen);
+       sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
+
+       xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
+
+       /*
+        * xdr_encode_pages may have added a few bytes to ensure the
+        * pathname ends on a 4-byte boundary.  Start encoding the
+        * attributes after the pad bytes.
+        */
+       pad = sndbuf->tail->iov_len;
+       if (pad > 0)
+               p++;
        p = xdr_encode_sattr(p, args->sattr);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
        return 0;
 }
 
index d85ac42..f8688ea 100644 (file)
@@ -544,8 +544,8 @@ nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
 }
 
 static int
-nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
-                 struct iattr *sattr)
+nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
+                 unsigned int len, struct iattr *sattr)
 {
        struct nfs_fh fhandle;
        struct nfs_fattr fattr, dir_attr;
@@ -553,8 +553,8 @@ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
                .fromfh         = NFS_FH(dir),
                .fromname       = dentry->d_name.name,
                .fromlen        = dentry->d_name.len,
-               .topath         = path->name,
-               .tolen          = path->len,
+               .pages          = &page,
+               .pathlen        = len,
                .sattr          = sattr
        };
        struct nfs3_diropres    res = {
@@ -569,11 +569,11 @@ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
        };
        int                     status;
 
-       if (path->len > NFS3_MAXPATHLEN)
+       if (len > NFS3_MAXPATHLEN)
                return -ENAMETOOLONG;
 
-       dprintk("NFS call  symlink %s -> %s\n", dentry->d_name.name,
-                       path->name);
+       dprintk("NFS call  symlink %s\n", dentry->d_name.name);
+
        nfs_fattr_init(&dir_attr);
        nfs_fattr_init(&fattr);
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
index 0250269..16556fa 100644 (file)
@@ -56,7 +56,7 @@
 #define NFS3_writeargs_sz      (NFS3_fh_sz+5)
 #define NFS3_createargs_sz     (NFS3_diropargs_sz+NFS3_sattr_sz)
 #define NFS3_mkdirargs_sz      (NFS3_diropargs_sz+NFS3_sattr_sz)
-#define NFS3_symlinkargs_sz    (NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz)
+#define NFS3_symlinkargs_sz    (NFS3_diropargs_sz+1+NFS3_sattr_sz)
 #define NFS3_mknodargs_sz      (NFS3_diropargs_sz+2+NFS3_sattr_sz)
 #define NFS3_renameargs_sz     (NFS3_diropargs_sz+NFS3_diropargs_sz)
 #define NFS3_linkargs_sz               (NFS3_fh_sz+NFS3_diropargs_sz)
@@ -398,8 +398,11 @@ nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args
        p = xdr_encode_fhandle(p, args->fromfh);
        p = xdr_encode_array(p, args->fromname, args->fromlen);
        p = xdr_encode_sattr(p, args->sattr);
-       p = xdr_encode_array(p, args->topath, args->tolen);
+       *p++ = htonl(args->pathlen);
        req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+       /* Copy the page */
+       xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
        return 0;
 }
 
index 2d18eac..7f60beb 100644 (file)
@@ -2085,7 +2085,7 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n
 }
 
 static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
-               struct qstr *path, struct iattr *sattr)
+               struct page *page, unsigned int len, struct iattr *sattr)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs_fh fhandle;
@@ -2111,10 +2111,11 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
        };
        int                     status;
 
-       if (path->len > NFS4_MAXPATHLEN)
+       if (len > NFS4_MAXPATHLEN)
                return -ENAMETOOLONG;
 
-       arg.u.symlink = path;
+       arg.u.symlink.pages = &page;
+       arg.u.symlink.len = len;
        nfs_fattr_init(&fattr);
        nfs_fattr_init(&dir_fattr);
        
@@ -2128,13 +2129,14 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
 }
 
 static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
-               struct qstr *path, struct iattr *sattr)
+               struct page *page, unsigned int len, struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_symlink(dir, dentry, path, sattr),
+                               _nfs4_proc_symlink(dir, dentry, page,
+                                                       len, sattr),
                                &exception);
        } while (exception.retry);
        return err;
index 9992606..3dd413f 100644 (file)
@@ -128,7 +128,7 @@ static int nfs4_stat_to_errno(int);
 #define decode_link_maxsz      (op_decode_hdr_maxsz + 5)
 #define encode_symlink_maxsz   (op_encode_hdr_maxsz + \
                                1 + nfs4_name_maxsz + \
-                               nfs4_path_maxsz + \
+                               1 + \
                                nfs4_fattr_maxsz)
 #define decode_symlink_maxsz   (op_decode_hdr_maxsz + 8)
 #define encode_create_maxsz    (op_encode_hdr_maxsz + \
@@ -673,9 +673,9 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
 
        switch (create->ftype) {
        case NF4LNK:
-               RESERVE_SPACE(4 + create->u.symlink->len);
-               WRITE32(create->u.symlink->len);
-               WRITEMEM(create->u.symlink->name, create->u.symlink->len);
+               RESERVE_SPACE(4);
+               WRITE32(create->u.symlink.len);
+               xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len);
                break;
 
        case NF4BLK: case NF4CHR:
index 0b507bf..630e506 100644 (file)
@@ -425,8 +425,8 @@ nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
 }
 
 static int
-nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
-                struct iattr *sattr)
+nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
+                unsigned int len, struct iattr *sattr)
 {
        struct nfs_fh fhandle;
        struct nfs_fattr fattr;
@@ -434,8 +434,8 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
                .fromfh         = NFS_FH(dir),
                .fromname       = dentry->d_name.name,
                .fromlen        = dentry->d_name.len,
-               .topath         = path->name,
-               .tolen          = path->len,
+               .pages          = &page,
+               .pathlen        = len,
                .sattr          = sattr
        };
        struct rpc_message msg = {
@@ -444,11 +444,11 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct qstr *path,
        };
        int                     status;
 
-       if (path->len > NFS2_MAXPATHLEN)
+       if (len > NFS2_MAXPATHLEN)
                return -ENAMETOOLONG;
 
-       dprintk("NFS call  symlink %s -> %s\n", dentry->d_name.name,
-                       path->name);
+       dprintk("NFS call  symlink %s\n", dentry->d_name.name);
+
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        nfs_mark_for_revalidate(dir);
 
index ddf5d75..dc5397d 100644 (file)
@@ -358,8 +358,8 @@ struct nfs_symlinkargs {
        struct nfs_fh *         fromfh;
        const char *            fromname;
        unsigned int            fromlen;
-       const char *            topath;
-       unsigned int            tolen;
+       struct page **          pages;
+       unsigned int            pathlen;
        struct iattr *          sattr;
 };
 
@@ -434,8 +434,8 @@ struct nfs3_symlinkargs {
        struct nfs_fh *         fromfh;
        const char *            fromname;
        unsigned int            fromlen;
-       const char *            topath;
-       unsigned int            tolen;
+       struct page **          pages;
+       unsigned int            pathlen;
        struct iattr *          sattr;
 };
 
@@ -533,7 +533,10 @@ struct nfs4_accessres {
 struct nfs4_create_arg {
        u32                             ftype;
        union {
-               struct qstr *           symlink;    /* NF4LNK */
+               struct {
+                       struct page **  pages;
+                       unsigned int    len;
+               } symlink;   /* NF4LNK */
                struct {
                        u32             specdata1;
                        u32             specdata2;
@@ -793,8 +796,8 @@ struct nfs_rpc_ops {
        int     (*rename)  (struct inode *, struct qstr *,
                            struct inode *, struct qstr *);
        int     (*link)    (struct inode *, struct inode *, struct qstr *);
-       int     (*symlink) (struct inode *, struct dentry *, struct qstr *,
-                           struct iattr *);
+       int     (*symlink) (struct inode *, struct dentry *, struct page *,
+                           unsigned int, struct iattr *);
        int     (*mkdir)   (struct inode *, struct dentry *, struct iattr *);
        int     (*rmdir)   (struct inode *, struct qstr *);
        int     (*readdir) (struct dentry *, struct rpc_cred *,