Merge branch 'for-2.6.31' of git://fieldses.org/git/linux-nfsd
* 'for-2.6.31' of git://fieldses.org/git/linux-nfsd: (60 commits) SUNRPC: Fix the TCP server's send buffer accounting nfsd41: Backchannel: minorversion support for the back channel nfsd41: Backchannel: cleanup nfs4.0 callback encode routines nfsd41: Remove ip address collision detection case nfsd: optimise the starting of zero threads when none are running. nfsd: don't take nfsd_mutex twice when setting number of threads. nfsd41: sanity check client drc maxreqs nfsd41: move channel attributes from nfsd4_session to a nfsd4_channel_attr struct NFS: kill off complicated macro 'PROC' sunrpc: potential memory leak in function rdma_read_xdr nfsd: minor nfsd_vfs_write cleanup nfsd: Pull write-gathering code out of nfsd_vfs_write nfsd: track last inode only in use_wgather case sunrpc: align cache_clean work's timer nfsd: Use write gathering only with NFSv2 NFSv4: kill off complicated macro 'PROC' NFSv4: do exact check about attribute specified knfsd: remove unreported filehandle stats counters knfsd: fix reply cache memory corruption knfsd: reply cache cleanups ...
This commit is contained in:
Коммит
7e0338c0de
|
@ -66,6 +66,10 @@ mandatory-locking.txt
|
|||
- info on the Linux implementation of Sys V mandatory file locking.
|
||||
ncpfs.txt
|
||||
- info on Novell Netware(tm) filesystem using NCP protocol.
|
||||
nfs41-server.txt
|
||||
- info on the Linux server implementation of NFSv4 minor version 1.
|
||||
nfs-rdma.txt
|
||||
- how to install and setup the Linux NFS/RDMA client and server software.
|
||||
nfsroot.txt
|
||||
- short guide on setting up a diskless box with NFS root filesystem.
|
||||
nilfs2.txt
|
||||
|
|
|
@ -236,10 +236,12 @@ source "fs/nfsd/Kconfig"
|
|||
|
||||
config LOCKD
|
||||
tristate
|
||||
depends on FILE_LOCKING
|
||||
|
||||
config LOCKD_V4
|
||||
bool
|
||||
depends on NFSD_V3 || NFS_V3
|
||||
depends on FILE_LOCKING
|
||||
default y
|
||||
|
||||
config EXPORTFS
|
||||
|
|
|
@ -326,6 +326,8 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call)
|
|||
{
|
||||
if (call->a_args.lock.oh.data != call->a_owner)
|
||||
kfree(call->a_args.lock.oh.data);
|
||||
|
||||
locks_release_private(&call->a_args.lock.fl);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -151,7 +151,7 @@ static struct file_lock *locks_alloc_lock(void)
|
|||
return kmem_cache_alloc(filelock_cache, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void locks_release_private(struct file_lock *fl)
|
||||
void locks_release_private(struct file_lock *fl)
|
||||
{
|
||||
if (fl->fl_ops) {
|
||||
if (fl->fl_ops->fl_release_private)
|
||||
|
@ -165,6 +165,7 @@ static void locks_release_private(struct file_lock *fl)
|
|||
}
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(locks_release_private);
|
||||
|
||||
/* Free a lock which is not in use. */
|
||||
static void locks_free_lock(struct file_lock *fl)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
config NFS_FS
|
||||
tristate "NFS client support"
|
||||
depends on INET
|
||||
depends on INET && FILE_LOCKING
|
||||
select LOCKD
|
||||
select SUNRPC
|
||||
select NFS_ACL_SUPPORT if NFS_V3_ACL
|
||||
|
|
|
@ -464,16 +464,11 @@ static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
|
|||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* Just a quick sanity check; we could also try to check
|
||||
* whether this pseudoflavor is supported, but at worst
|
||||
* an unsupported pseudoflavor on the export would just
|
||||
* be a pseudoflavor that won't match the flavor of any
|
||||
* authenticated request. The administrator will
|
||||
* probably discover the problem when someone fails to
|
||||
* authenticate.
|
||||
* XXX: It would be nice to also check whether this
|
||||
* pseudoflavor is supported, so we can discover the
|
||||
* problem at export time instead of when a client fails
|
||||
* to authenticate.
|
||||
*/
|
||||
if (f->pseudoflavor < 0)
|
||||
return -EINVAL;
|
||||
err = get_int(mesg, &f->flags);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -652,8 +652,6 @@ nfsd3_proc_commit(struct svc_rqst * rqstp, struct nfsd3_commitargs *argp,
|
|||
* NFSv3 Server procedures.
|
||||
* Only the results of non-idempotent operations are cached.
|
||||
*/
|
||||
#define nfs3svc_decode_voidargs NULL
|
||||
#define nfs3svc_release_void NULL
|
||||
#define nfs3svc_decode_fhandleargs nfs3svc_decode_fhandle
|
||||
#define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat
|
||||
#define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat
|
||||
|
@ -686,28 +684,219 @@ struct nfsd3_voidargs { int dummy; };
|
|||
#define WC (7+pAT) /* WCC attributes */
|
||||
|
||||
static struct svc_procedure nfsd_procedures3[22] = {
|
||||
PROC(null, void, void, void, RC_NOCACHE, ST),
|
||||
PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT),
|
||||
PROC(setattr, sattr, wccstat, fhandle, RC_REPLBUFF, ST+WC),
|
||||
PROC(lookup, dirop, dirop, fhandle2, RC_NOCACHE, ST+FH+pAT+pAT),
|
||||
PROC(access, access, access, fhandle, RC_NOCACHE, ST+pAT+1),
|
||||
PROC(readlink, readlink, readlink, fhandle, RC_NOCACHE, ST+pAT+1+NFS3_MAXPATHLEN/4),
|
||||
PROC(read, read, read, fhandle, RC_NOCACHE, ST+pAT+4+NFSSVC_MAXBLKSIZE/4),
|
||||
PROC(write, write, write, fhandle, RC_REPLBUFF, ST+WC+4),
|
||||
PROC(create, create, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
|
||||
PROC(mkdir, mkdir, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
|
||||
PROC(symlink, symlink, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
|
||||
PROC(mknod, mknod, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
|
||||
PROC(remove, dirop, wccstat, fhandle, RC_REPLBUFF, ST+WC),
|
||||
PROC(rmdir, dirop, wccstat, fhandle, RC_REPLBUFF, ST+WC),
|
||||
PROC(rename, rename, rename, fhandle2, RC_REPLBUFF, ST+WC+WC),
|
||||
PROC(link, link, link, fhandle2, RC_REPLBUFF, ST+pAT+WC),
|
||||
PROC(readdir, readdir, readdir, fhandle, RC_NOCACHE, 0),
|
||||
PROC(readdirplus,readdirplus, readdir, fhandle, RC_NOCACHE, 0),
|
||||
PROC(fsstat, fhandle, fsstat, void, RC_NOCACHE, ST+pAT+2*6+1),
|
||||
PROC(fsinfo, fhandle, fsinfo, void, RC_NOCACHE, ST+pAT+12),
|
||||
PROC(pathconf, fhandle, pathconf, void, RC_NOCACHE, ST+pAT+6),
|
||||
PROC(commit, commit, commit, fhandle, RC_NOCACHE, ST+WC+2),
|
||||
[NFS3PROC_NULL] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_null,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd3_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFS3PROC_GETATTR] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_getattr,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_attrstatres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_attrstatres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT,
|
||||
},
|
||||
[NFS3PROC_SETATTR] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_setattr,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_sattrargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_wccstatres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_sattrargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_wccstatres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC,
|
||||
},
|
||||
[NFS3PROC_LOOKUP] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_lookup,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_diropargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_diropres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_diropres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+FH+pAT+pAT,
|
||||
},
|
||||
[NFS3PROC_ACCESS] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_access,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_accessargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_accessres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_accessargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_accessres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+1,
|
||||
},
|
||||
[NFS3PROC_READLINK] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_readlink,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_readlinkargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_readlinkres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readlinkargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readlinkres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4,
|
||||
},
|
||||
[NFS3PROC_READ] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_read,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_readargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_readres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4,
|
||||
},
|
||||
[NFS3PROC_WRITE] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_write,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_writeargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_writeres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_writeargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_writeres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC+4,
|
||||
},
|
||||
[NFS3PROC_CREATE] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_create,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_createargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_createargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
},
|
||||
[NFS3PROC_MKDIR] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_mkdir,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_mkdirargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_mkdirargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
},
|
||||
[NFS3PROC_SYMLINK] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_symlink,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_symlinkargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_symlinkargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
},
|
||||
[NFS3PROC_MKNOD] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_mknod,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_mknodargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_createres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_mknodargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_createres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+(1+FH+pAT)+WC,
|
||||
},
|
||||
[NFS3PROC_REMOVE] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_remove,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_diropargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_wccstatres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_wccstatres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC,
|
||||
},
|
||||
[NFS3PROC_RMDIR] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_rmdir,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_diropargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_wccstatres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_wccstatres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC,
|
||||
},
|
||||
[NFS3PROC_RENAME] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_rename,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_renameargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_renameres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_renameargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_renameres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+WC+WC,
|
||||
},
|
||||
[NFS3PROC_LINK] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_link,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_linkargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_linkres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle2,
|
||||
.pc_argsize = sizeof(struct nfsd3_linkargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_linkres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+pAT+WC,
|
||||
},
|
||||
[NFS3PROC_READDIR] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_readdir,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_readdirargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_readdirres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readdirargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readdirres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
},
|
||||
[NFS3PROC_READDIRPLUS] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_readdirplus,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_readdirplusargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_readdirres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_readdirplusargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_readdirres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
},
|
||||
[NFS3PROC_FSSTAT] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_fsstat,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_fsstatres,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_fsstatres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+2*6+1,
|
||||
},
|
||||
[NFS3PROC_FSINFO] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_fsinfo,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_fsinfores,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_fsinfores),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+12,
|
||||
},
|
||||
[NFS3PROC_PATHCONF] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_pathconf,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_fhandleargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_pathconfres,
|
||||
.pc_argsize = sizeof(struct nfsd3_fhandleargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_pathconfres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+pAT+6,
|
||||
},
|
||||
[NFS3PROC_COMMIT] = {
|
||||
.pc_func = (svc_procfunc) nfsd3_proc_commit,
|
||||
.pc_decode = (kxdrproc_t) nfs3svc_decode_commitargs,
|
||||
.pc_encode = (kxdrproc_t) nfs3svc_encode_commitres,
|
||||
.pc_release = (kxdrproc_t) nfs3svc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd3_commitargs),
|
||||
.pc_ressize = sizeof(struct nfsd3_commitres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+WC+2,
|
||||
},
|
||||
};
|
||||
|
||||
struct svc_version nfsd_version3 = {
|
||||
|
|
|
@ -272,6 +272,7 @@ void fill_post_wcc(struct svc_fh *fhp)
|
|||
|
||||
err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry,
|
||||
&fhp->fh_post_attr);
|
||||
fhp->fh_post_change = fhp->fh_dentry->d_inode->i_version;
|
||||
if (err)
|
||||
fhp->fh_post_saved = 0;
|
||||
else
|
||||
|
|
|
@ -140,8 +140,10 @@ struct nfs4_cb_compound_hdr {
|
|||
int status;
|
||||
u32 ident;
|
||||
u32 nops;
|
||||
__be32 *nops_p;
|
||||
u32 minorversion;
|
||||
u32 taglen;
|
||||
char * tag;
|
||||
char *tag;
|
||||
};
|
||||
|
||||
static struct {
|
||||
|
@ -201,33 +203,39 @@ nfs_cb_stat_to_errno(int stat)
|
|||
* XDR encode
|
||||
*/
|
||||
|
||||
static int
|
||||
static void
|
||||
encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
|
||||
{
|
||||
__be32 * p;
|
||||
|
||||
RESERVE_SPACE(16);
|
||||
WRITE32(0); /* tag length is always 0 */
|
||||
WRITE32(NFS4_MINOR_VERSION);
|
||||
WRITE32(hdr->minorversion);
|
||||
WRITE32(hdr->ident);
|
||||
hdr->nops_p = p;
|
||||
WRITE32(hdr->nops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
|
||||
static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
|
||||
{
|
||||
*hdr->nops_p = htonl(hdr->nops);
|
||||
}
|
||||
|
||||
static void
|
||||
encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
|
||||
struct nfs4_cb_compound_hdr *hdr)
|
||||
{
|
||||
__be32 *p;
|
||||
int len = cb_rec->cbr_fh.fh_size;
|
||||
int len = dp->dl_fh.fh_size;
|
||||
|
||||
RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
|
||||
RESERVE_SPACE(12+sizeof(dp->dl_stateid) + len);
|
||||
WRITE32(OP_CB_RECALL);
|
||||
WRITE32(cb_rec->cbr_stateid.si_generation);
|
||||
WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t));
|
||||
WRITE32(cb_rec->cbr_trunc);
|
||||
WRITE32(dp->dl_stateid.si_generation);
|
||||
WRITEMEM(&dp->dl_stateid.si_opaque, sizeof(stateid_opaque_t));
|
||||
WRITE32(0); /* truncate optimization not implemented */
|
||||
WRITE32(len);
|
||||
WRITEMEM(&cb_rec->cbr_fh.fh_base, len);
|
||||
return 0;
|
||||
WRITEMEM(&dp->dl_fh.fh_base, len);
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -241,17 +249,18 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
|
|||
}
|
||||
|
||||
static int
|
||||
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_cb_recall *args)
|
||||
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_delegation *args)
|
||||
{
|
||||
struct xdr_stream xdr;
|
||||
struct nfs4_cb_compound_hdr hdr = {
|
||||
.ident = args->cbr_ident,
|
||||
.nops = 1,
|
||||
.ident = args->dl_ident,
|
||||
};
|
||||
|
||||
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
|
||||
encode_cb_compound_hdr(&xdr, &hdr);
|
||||
return (encode_cb_recall(&xdr, args));
|
||||
encode_cb_recall(&xdr, args, &hdr);
|
||||
encode_cb_nops(&hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -358,18 +367,21 @@ static struct rpc_program cb_program = {
|
|||
.pipe_dir_name = "/nfsd4_cb",
|
||||
};
|
||||
|
||||
static int max_cb_time(void)
|
||||
{
|
||||
return max(NFSD_LEASE_TIME/10, (time_t)1) * HZ;
|
||||
}
|
||||
|
||||
/* Reference counting, callback cleanup, etc., all look racy as heck.
|
||||
* And why is cb_set an atomic? */
|
||||
|
||||
static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
|
||||
int setup_callback_client(struct nfs4_client *clp)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
struct nfs4_callback *cb = &clp->cl_callback;
|
||||
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
|
||||
struct rpc_timeout timeparms = {
|
||||
.to_initval = (NFSD_LEASE_TIME/4) * HZ,
|
||||
.to_retries = 5,
|
||||
.to_maxval = (NFSD_LEASE_TIME/2) * HZ,
|
||||
.to_exponential = 1,
|
||||
.to_initval = max_cb_time(),
|
||||
.to_retries = 0,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.protocol = IPPROTO_TCP,
|
||||
|
@ -386,7 +398,7 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
|
|||
struct rpc_clnt *client;
|
||||
|
||||
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
|
||||
/* Initialize address */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
@ -396,48 +408,77 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
|
|||
|
||||
/* Create RPC client */
|
||||
client = rpc_create(&args);
|
||||
if (IS_ERR(client))
|
||||
if (IS_ERR(client)) {
|
||||
dprintk("NFSD: couldn't create callback client: %ld\n",
|
||||
PTR_ERR(client));
|
||||
return client;
|
||||
return PTR_ERR(client);
|
||||
}
|
||||
cb->cb_client = client;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int do_probe_callback(void *data)
|
||||
static void warn_no_callback_path(struct nfs4_client *clp, int reason)
|
||||
{
|
||||
struct nfs4_client *clp = data;
|
||||
struct nfs4_callback *cb = &clp->cl_callback;
|
||||
dprintk("NFSD: warning: no callback path to client %.*s: error %d\n",
|
||||
(int)clp->cl_name.len, clp->cl_name.data, reason);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_client *clp = calldata;
|
||||
|
||||
if (task->tk_status)
|
||||
warn_no_callback_path(clp, task->tk_status);
|
||||
else
|
||||
atomic_set(&clp->cl_cb_conn.cb_set, 1);
|
||||
put_nfs4_client(clp);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfsd4_cb_probe_ops = {
|
||||
.rpc_call_done = nfsd4_cb_probe_done,
|
||||
};
|
||||
|
||||
static struct rpc_cred *lookup_cb_cred(struct nfs4_cb_conn *cb)
|
||||
{
|
||||
struct auth_cred acred = {
|
||||
.machine_cred = 1
|
||||
};
|
||||
|
||||
/*
|
||||
* Note in the gss case this doesn't actually have to wait for a
|
||||
* gss upcall (or any calls to the client); this just creates a
|
||||
* non-uptodate cred which the rpc state machine will fill in with
|
||||
* a refresh_upcall later.
|
||||
*/
|
||||
return rpcauth_lookup_credcache(cb->cb_client->cl_auth, &acred,
|
||||
RPCAUTH_LOOKUP_NEW);
|
||||
}
|
||||
|
||||
void do_probe_callback(struct nfs4_client *clp)
|
||||
{
|
||||
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
|
||||
.rpc_argp = clp,
|
||||
};
|
||||
struct rpc_clnt *client;
|
||||
struct rpc_cred *cred;
|
||||
int status;
|
||||
|
||||
client = setup_callback_client(clp);
|
||||
if (IS_ERR(client)) {
|
||||
status = PTR_ERR(client);
|
||||
dprintk("NFSD: couldn't create callback client: %d\n",
|
||||
status);
|
||||
goto out_err;
|
||||
cred = lookup_cb_cred(cb);
|
||||
if (IS_ERR(cred)) {
|
||||
status = PTR_ERR(cred);
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = rpc_call_sync(client, &msg, RPC_TASK_SOFT);
|
||||
|
||||
if (status)
|
||||
goto out_release_client;
|
||||
|
||||
cb->cb_client = client;
|
||||
atomic_set(&cb->cb_set, 1);
|
||||
cb->cb_cred = cred;
|
||||
msg.rpc_cred = cb->cb_cred;
|
||||
status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_SOFT,
|
||||
&nfsd4_cb_probe_ops, (void *)clp);
|
||||
out:
|
||||
if (status) {
|
||||
warn_no_callback_path(clp, status);
|
||||
put_nfs4_client(clp);
|
||||
return 0;
|
||||
out_release_client:
|
||||
rpc_shutdown_client(client);
|
||||
out_err:
|
||||
dprintk("NFSD: warning: no callback path to client %.*s: error %d\n",
|
||||
(int)clp->cl_name.len, clp->cl_name.data, status);
|
||||
put_nfs4_client(clp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -446,21 +487,65 @@ out_err:
|
|||
void
|
||||
nfsd4_probe_callback(struct nfs4_client *clp)
|
||||
{
|
||||
struct task_struct *t;
|
||||
int status;
|
||||
|
||||
BUG_ON(atomic_read(&clp->cl_callback.cb_set));
|
||||
BUG_ON(atomic_read(&clp->cl_cb_conn.cb_set));
|
||||
|
||||
status = setup_callback_client(clp);
|
||||
if (status) {
|
||||
warn_no_callback_path(clp, status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* the task holds a reference to the nfs4_client struct */
|
||||
atomic_inc(&clp->cl_count);
|
||||
|
||||
t = kthread_run(do_probe_callback, clp, "nfs4_cb_probe");
|
||||
|
||||
if (IS_ERR(t))
|
||||
atomic_dec(&clp->cl_count);
|
||||
|
||||
return;
|
||||
do_probe_callback(clp);
|
||||
}
|
||||
|
||||
static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
|
||||
switch (task->tk_status) {
|
||||
case -EIO:
|
||||
/* Network partition? */
|
||||
atomic_set(&clp->cl_cb_conn.cb_set, 0);
|
||||
warn_no_callback_path(clp, task->tk_status);
|
||||
case -EBADHANDLE:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
/* Race: client probably got cb_recall
|
||||
* before open reply granting delegation */
|
||||
break;
|
||||
default:
|
||||
/* success, or error we can't handle */
|
||||
return;
|
||||
}
|
||||
if (dp->dl_retries--) {
|
||||
rpc_delay(task, 2*HZ);
|
||||
task->tk_status = 0;
|
||||
rpc_restart_call(task);
|
||||
} else {
|
||||
atomic_set(&clp->cl_cb_conn.cb_set, 0);
|
||||
warn_no_callback_path(clp, task->tk_status);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfsd4_cb_recall_release(void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
|
||||
nfs4_put_delegation(dp);
|
||||
put_nfs4_client(clp);
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nfsd4_cb_recall_ops = {
|
||||
.rpc_call_done = nfsd4_cb_recall_done,
|
||||
.rpc_release = nfsd4_cb_recall_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* called with dp->dl_count inc'ed.
|
||||
*/
|
||||
|
@ -468,41 +553,19 @@ void
|
|||
nfsd4_cb_recall(struct nfs4_delegation *dp)
|
||||
{
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct rpc_clnt *clnt = clp->cl_callback.cb_client;
|
||||
struct nfs4_cb_recall *cbr = &dp->dl_recall;
|
||||
struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
|
||||
.rpc_argp = cbr,
|
||||
.rpc_argp = dp,
|
||||
.rpc_cred = clp->cl_cb_conn.cb_cred
|
||||
};
|
||||
int retries = 1;
|
||||
int status = 0;
|
||||
int status;
|
||||
|
||||
cbr->cbr_trunc = 0; /* XXX need to implement truncate optimization */
|
||||
cbr->cbr_dp = dp;
|
||||
|
||||
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
|
||||
while (retries--) {
|
||||
switch (status) {
|
||||
case -EIO:
|
||||
/* Network partition? */
|
||||
atomic_set(&clp->cl_callback.cb_set, 0);
|
||||
case -EBADHANDLE:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
/* Race: client probably got cb_recall
|
||||
* before open reply granting delegation */
|
||||
break;
|
||||
default:
|
||||
goto out_put_cred;
|
||||
}
|
||||
ssleep(2);
|
||||
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
|
||||
}
|
||||
out_put_cred:
|
||||
/*
|
||||
* Success or failure, now we're either waiting for lease expiration
|
||||
* or deleg_return.
|
||||
*/
|
||||
dp->dl_retries = 1;
|
||||
status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
|
||||
&nfsd4_cb_recall_ops, dp);
|
||||
if (status) {
|
||||
put_nfs4_client(clp);
|
||||
nfs4_put_delegation(dp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,78 @@
|
|||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
||||
|
||||
static u32 nfsd_attrmask[] = {
|
||||
NFSD_WRITEABLE_ATTRS_WORD0,
|
||||
NFSD_WRITEABLE_ATTRS_WORD1,
|
||||
NFSD_WRITEABLE_ATTRS_WORD2
|
||||
};
|
||||
|
||||
static u32 nfsd41_ex_attrmask[] = {
|
||||
NFSD_SUPPATTR_EXCLCREAT_WORD0,
|
||||
NFSD_SUPPATTR_EXCLCREAT_WORD1,
|
||||
NFSD_SUPPATTR_EXCLCREAT_WORD2
|
||||
};
|
||||
|
||||
static __be32
|
||||
check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
u32 *bmval, u32 *writable)
|
||||
{
|
||||
struct dentry *dentry = cstate->current_fh.fh_dentry;
|
||||
struct svc_export *exp = cstate->current_fh.fh_export;
|
||||
|
||||
/*
|
||||
* Check about attributes are supported by the NFSv4 server or not.
|
||||
* According to spec, unsupported attributes return ERR_ATTRNOTSUPP.
|
||||
*/
|
||||
if ((bmval[0] & ~nfsd_suppattrs0(cstate->minorversion)) ||
|
||||
(bmval[1] & ~nfsd_suppattrs1(cstate->minorversion)) ||
|
||||
(bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
|
||||
return nfserr_attrnotsupp;
|
||||
|
||||
/*
|
||||
* Check FATTR4_WORD0_ACL & FATTR4_WORD0_FS_LOCATIONS can be supported
|
||||
* in current environment or not.
|
||||
*/
|
||||
if (bmval[0] & FATTR4_WORD0_ACL) {
|
||||
if (!IS_POSIXACL(dentry->d_inode))
|
||||
return nfserr_attrnotsupp;
|
||||
}
|
||||
if (bmval[0] & FATTR4_WORD0_FS_LOCATIONS) {
|
||||
if (exp->ex_fslocs.locations == NULL)
|
||||
return nfserr_attrnotsupp;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to spec, read-only attributes return ERR_INVAL.
|
||||
*/
|
||||
if (writable) {
|
||||
if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
|
||||
(bmval[2] & ~writable[2]))
|
||||
return nfserr_inval;
|
||||
}
|
||||
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_check_open_attributes(struct svc_rqst *rqstp,
|
||||
struct nfsd4_compound_state *cstate, struct nfsd4_open *open)
|
||||
{
|
||||
__be32 status = nfs_ok;
|
||||
|
||||
if (open->op_create == NFS4_OPEN_CREATE) {
|
||||
if (open->op_createmode == NFS4_CREATE_UNCHECKED
|
||||
|| open->op_createmode == NFS4_CREATE_GUARDED)
|
||||
status = check_attr_support(rqstp, cstate,
|
||||
open->op_bmval, nfsd_attrmask);
|
||||
else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1)
|
||||
status = check_attr_support(rqstp, cstate,
|
||||
open->op_bmval, nfsd41_ex_attrmask);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fh_dup2(struct svc_fh *dst, struct svc_fh *src)
|
||||
{
|
||||
|
@ -225,6 +297,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
if (status)
|
||||
goto out;
|
||||
|
||||
status = nfsd4_check_open_attributes(rqstp, cstate, open);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* Openowner is now set, so sequence id will get bumped. Now we need
|
||||
* these checks before we do any creates: */
|
||||
status = nfserr_grace;
|
||||
|
@ -395,6 +471,11 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
if (status)
|
||||
return status;
|
||||
|
||||
status = check_attr_support(rqstp, cstate, create->cr_bmval,
|
||||
nfsd_attrmask);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
switch (create->cr_type) {
|
||||
case NF4LNK:
|
||||
/* ugh! we have to null-terminate the linktext, or
|
||||
|
@ -689,6 +770,12 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
if (status)
|
||||
return status;
|
||||
status = nfs_ok;
|
||||
|
||||
status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
|
||||
nfsd_attrmask);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
if (setattr->sa_acl != NULL)
|
||||
status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
|
||||
setattr->sa_acl);
|
||||
|
@ -763,10 +850,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
if (status)
|
||||
return status;
|
||||
|
||||
if ((verify->ve_bmval[0] & ~nfsd_suppattrs0(cstate->minorversion))
|
||||
|| (verify->ve_bmval[1] & ~nfsd_suppattrs1(cstate->minorversion))
|
||||
|| (verify->ve_bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
|
||||
return nfserr_attrnotsupp;
|
||||
status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
|
||||
|| (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
|
||||
return nfserr_inval;
|
||||
|
@ -1226,24 +1313,9 @@ static const char *nfsd4_op_name(unsigned opnum)
|
|||
return "unknown_operation";
|
||||
}
|
||||
|
||||
#define nfs4svc_decode_voidargs NULL
|
||||
#define nfs4svc_release_void NULL
|
||||
#define nfsd4_voidres nfsd4_voidargs
|
||||
#define nfs4svc_release_compound NULL
|
||||
struct nfsd4_voidargs { int dummy; };
|
||||
|
||||
#define PROC(name, argt, rest, relt, cache, respsize) \
|
||||
{ (svc_procfunc) nfsd4_proc_##name, \
|
||||
(kxdrproc_t) nfs4svc_decode_##argt##args, \
|
||||
(kxdrproc_t) nfs4svc_encode_##rest##res, \
|
||||
(kxdrproc_t) nfs4svc_release_##relt, \
|
||||
sizeof(struct nfsd4_##argt##args), \
|
||||
sizeof(struct nfsd4_##rest##res), \
|
||||
0, \
|
||||
cache, \
|
||||
respsize, \
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: At the present time, the NFSv4 server does not do XID caching
|
||||
* of requests. Implementing XID caching would not be a serious problem,
|
||||
|
@ -1255,8 +1327,23 @@ struct nfsd4_voidargs { int dummy; };
|
|||
* better XID's.
|
||||
*/
|
||||
static struct svc_procedure nfsd_procedures4[2] = {
|
||||
PROC(null, void, void, void, RC_NOCACHE, 1),
|
||||
PROC(compound, compound, compound, compound, RC_NOCACHE, NFSD_BUFSIZE/4)
|
||||
[NFSPROC4_NULL] = {
|
||||
.pc_func = (svc_procfunc) nfsd4_proc_null,
|
||||
.pc_encode = (kxdrproc_t) nfs4svc_encode_voidres,
|
||||
.pc_argsize = sizeof(struct nfsd4_voidargs),
|
||||
.pc_ressize = sizeof(struct nfsd4_voidres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = 1,
|
||||
},
|
||||
[NFSPROC4_COMPOUND] = {
|
||||
.pc_func = (svc_procfunc) nfsd4_proc_compound,
|
||||
.pc_decode = (kxdrproc_t) nfs4svc_decode_compoundargs,
|
||||
.pc_encode = (kxdrproc_t) nfs4svc_encode_compoundres,
|
||||
.pc_argsize = sizeof(struct nfsd4_compoundargs),
|
||||
.pc_ressize = sizeof(struct nfsd4_compoundres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = NFSD_BUFSIZE/4,
|
||||
},
|
||||
};
|
||||
|
||||
struct svc_version nfsd_version4 = {
|
||||
|
|
|
@ -182,7 +182,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
|
|||
{
|
||||
struct nfs4_delegation *dp;
|
||||
struct nfs4_file *fp = stp->st_file;
|
||||
struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback;
|
||||
struct nfs4_cb_conn *cb = &stp->st_stateowner->so_client->cl_cb_conn;
|
||||
|
||||
dprintk("NFSD alloc_init_deleg\n");
|
||||
if (fp->fi_had_conflict)
|
||||
|
@ -203,10 +203,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
|
|||
get_file(stp->st_vfs_file);
|
||||
dp->dl_vfs_file = stp->st_vfs_file;
|
||||
dp->dl_type = type;
|
||||
dp->dl_recall.cbr_dp = NULL;
|
||||
dp->dl_recall.cbr_ident = cb->cb_ident;
|
||||
dp->dl_recall.cbr_trunc = 0;
|
||||
dp->dl_stateid.si_boot = boot_time;
|
||||
dp->dl_ident = cb->cb_ident;
|
||||
dp->dl_stateid.si_boot = get_seconds();
|
||||
dp->dl_stateid.si_stateownerid = current_delegid++;
|
||||
dp->dl_stateid.si_fileid = 0;
|
||||
dp->dl_stateid.si_generation = 0;
|
||||
|
@ -427,6 +425,11 @@ static int set_forechannel_maxreqs(struct nfsd4_channel_attrs *fchan)
|
|||
{
|
||||
int status = 0, np = fchan->maxreqs * NFSD_PAGES_PER_SLOT;
|
||||
|
||||
if (fchan->maxreqs < 1)
|
||||
return nfserr_inval;
|
||||
else if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
|
||||
fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
|
||||
|
||||
spin_lock(&nfsd_serv->sv_lock);
|
||||
if (np + nfsd_serv->sv_drc_pages_used > nfsd_serv->sv_drc_max_pages)
|
||||
np = nfsd_serv->sv_drc_max_pages - nfsd_serv->sv_drc_pages_used;
|
||||
|
@ -446,7 +449,7 @@ static int set_forechannel_maxreqs(struct nfsd4_channel_attrs *fchan)
|
|||
* fchan holds the client values on input, and the server values on output
|
||||
*/
|
||||
static int init_forechannel_attrs(struct svc_rqst *rqstp,
|
||||
struct nfsd4_session *session,
|
||||
struct nfsd4_channel_attrs *session_fchan,
|
||||
struct nfsd4_channel_attrs *fchan)
|
||||
{
|
||||
int status = 0;
|
||||
|
@ -457,21 +460,21 @@ static int init_forechannel_attrs(struct svc_rqst *rqstp,
|
|||
/* Use the client's max request and max response size if possible */
|
||||
if (fchan->maxreq_sz > maxcount)
|
||||
fchan->maxreq_sz = maxcount;
|
||||
session->se_fmaxreq_sz = fchan->maxreq_sz;
|
||||
session_fchan->maxreq_sz = fchan->maxreq_sz;
|
||||
|
||||
if (fchan->maxresp_sz > maxcount)
|
||||
fchan->maxresp_sz = maxcount;
|
||||
session->se_fmaxresp_sz = fchan->maxresp_sz;
|
||||
session_fchan->maxresp_sz = fchan->maxresp_sz;
|
||||
|
||||
/* Set the max response cached size our default which is
|
||||
* a multiple of PAGE_SIZE and small */
|
||||
session->se_fmaxresp_cached = NFSD_PAGES_PER_SLOT * PAGE_SIZE;
|
||||
fchan->maxresp_cached = session->se_fmaxresp_cached;
|
||||
session_fchan->maxresp_cached = NFSD_PAGES_PER_SLOT * PAGE_SIZE;
|
||||
fchan->maxresp_cached = session_fchan->maxresp_cached;
|
||||
|
||||
/* Use the client's maxops if possible */
|
||||
if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
|
||||
fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
|
||||
session->se_fmaxops = fchan->maxops;
|
||||
session_fchan->maxops = fchan->maxops;
|
||||
|
||||
/* try to use the client requested number of slots */
|
||||
if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
|
||||
|
@ -483,7 +486,7 @@ static int init_forechannel_attrs(struct svc_rqst *rqstp,
|
|||
*/
|
||||
status = set_forechannel_maxreqs(fchan);
|
||||
|
||||
session->se_fnumslots = fchan->maxreqs;
|
||||
session_fchan->maxreqs = fchan->maxreqs;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -497,12 +500,14 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
|
|||
memset(&tmp, 0, sizeof(tmp));
|
||||
|
||||
/* FIXME: For now, we just accept the client back channel attributes. */
|
||||
status = init_forechannel_attrs(rqstp, &tmp, &cses->fore_channel);
|
||||
tmp.se_bchannel = cses->back_channel;
|
||||
status = init_forechannel_attrs(rqstp, &tmp.se_fchannel,
|
||||
&cses->fore_channel);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
/* allocate struct nfsd4_session and slot table in one piece */
|
||||
slotsize = tmp.se_fnumslots * sizeof(struct nfsd4_slot);
|
||||
slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot);
|
||||
new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
|
||||
if (!new)
|
||||
goto out;
|
||||
|
@ -576,7 +581,7 @@ free_session(struct kref *kref)
|
|||
int i;
|
||||
|
||||
ses = container_of(kref, struct nfsd4_session, se_ref);
|
||||
for (i = 0; i < ses->se_fnumslots; i++) {
|
||||
for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
|
||||
struct nfsd4_cache_entry *e = &ses->se_slots[i].sl_cache_entry;
|
||||
nfsd4_release_respages(e->ce_respages, e->ce_resused);
|
||||
}
|
||||
|
@ -632,16 +637,20 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
|
|||
static void
|
||||
shutdown_callback_client(struct nfs4_client *clp)
|
||||
{
|
||||
struct rpc_clnt *clnt = clp->cl_callback.cb_client;
|
||||
struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
|
||||
|
||||
if (clnt) {
|
||||
/*
|
||||
* Callback threads take a reference on the client, so there
|
||||
* should be no outstanding callbacks at this point.
|
||||
*/
|
||||
clp->cl_callback.cb_client = NULL;
|
||||
clp->cl_cb_conn.cb_client = NULL;
|
||||
rpc_shutdown_client(clnt);
|
||||
}
|
||||
if (clp->cl_cb_conn.cb_cred) {
|
||||
put_rpccred(clp->cl_cb_conn.cb_cred);
|
||||
clp->cl_cb_conn.cb_cred = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -714,7 +723,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir)
|
|||
return NULL;
|
||||
memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
|
||||
atomic_set(&clp->cl_count, 1);
|
||||
atomic_set(&clp->cl_callback.cb_set, 0);
|
||||
atomic_set(&clp->cl_cb_conn.cb_set, 0);
|
||||
INIT_LIST_HEAD(&clp->cl_idhash);
|
||||
INIT_LIST_HEAD(&clp->cl_strhash);
|
||||
INIT_LIST_HEAD(&clp->cl_openowners);
|
||||
|
@ -966,7 +975,7 @@ parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigne
|
|||
static void
|
||||
gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
|
||||
{
|
||||
struct nfs4_callback *cb = &clp->cl_callback;
|
||||
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
|
||||
|
||||
/* Currently, we only support tcp for the callback channel */
|
||||
if ((se->se_callback_netid_len != 3) || memcmp((char *)se->se_callback_netid_val, "tcp", 3))
|
||||
|
@ -975,6 +984,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
|
|||
if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val,
|
||||
&cb->cb_addr, &cb->cb_port)))
|
||||
goto out_err;
|
||||
cb->cb_minorversion = 0;
|
||||
cb->cb_prog = se->se_callback_prog;
|
||||
cb->cb_ident = se->se_callback_ident;
|
||||
return;
|
||||
|
@ -1128,7 +1138,7 @@ nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
|
|||
* is sent (lease renewal).
|
||||
*/
|
||||
if (seq && nfsd4_not_cached(resp)) {
|
||||
seq->maxslots = resp->cstate.session->se_fnumslots;
|
||||
seq->maxslots = resp->cstate.session->se_fchannel.maxreqs;
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
|
@ -1238,12 +1248,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
|
|||
expire_client(conf);
|
||||
goto out_new;
|
||||
}
|
||||
if (ip_addr != conf->cl_addr &&
|
||||
!(exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A)) {
|
||||
/* Client collision. 18.35.4 case 3 */
|
||||
status = nfserr_clid_inuse;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Set bit when the owner id and verifier map to an already
|
||||
* confirmed client id (18.35.3).
|
||||
|
@ -1257,13 +1261,13 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
|
|||
copy_verf(conf, &verf);
|
||||
new = conf;
|
||||
goto out_copy;
|
||||
} else {
|
||||
}
|
||||
|
||||
/* 18.35.4 case 7 */
|
||||
if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
|
||||
status = nfserr_noent;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
unconf = find_unconfirmed_client_by_str(dname, strhashval, true);
|
||||
if (unconf) {
|
||||
|
@ -1471,7 +1475,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
|
|||
goto out;
|
||||
|
||||
status = nfserr_badslot;
|
||||
if (seq->slotid >= session->se_fnumslots)
|
||||
if (seq->slotid >= session->se_fchannel.maxreqs)
|
||||
goto out;
|
||||
|
||||
slot = &session->se_slots[seq->slotid];
|
||||
|
@ -1686,9 +1690,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
|||
else {
|
||||
/* XXX: We just turn off callbacks until we can handle
|
||||
* change request correctly. */
|
||||
atomic_set(&conf->cl_callback.cb_set, 0);
|
||||
gen_confirm(conf);
|
||||
nfsd4_remove_clid_dir(unconf);
|
||||
atomic_set(&conf->cl_cb_conn.cb_set, 0);
|
||||
expire_client(unconf);
|
||||
status = nfs_ok;
|
||||
|
||||
|
@ -1882,7 +1884,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *
|
|||
stp->st_stateowner = sop;
|
||||
get_nfs4_file(fp);
|
||||
stp->st_file = fp;
|
||||
stp->st_stateid.si_boot = boot_time;
|
||||
stp->st_stateid.si_boot = get_seconds();
|
||||
stp->st_stateid.si_stateownerid = sop->so_id;
|
||||
stp->st_stateid.si_fileid = fp->fi_id;
|
||||
stp->st_stateid.si_generation = 0;
|
||||
|
@ -2058,19 +2060,6 @@ nfs4_file_downgrade(struct file *filp, unsigned int share_access)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Recall a delegation
|
||||
*/
|
||||
static int
|
||||
do_recall(void *__dp)
|
||||
{
|
||||
struct nfs4_delegation *dp = __dp;
|
||||
|
||||
dp->dl_file->fi_had_conflict = true;
|
||||
nfsd4_cb_recall(dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Spawn a thread to perform a recall on the delegation represented
|
||||
* by the lease (file_lock)
|
||||
|
@ -2082,8 +2071,7 @@ do_recall(void *__dp)
|
|||
static
|
||||
void nfsd_break_deleg_cb(struct file_lock *fl)
|
||||
{
|
||||
struct nfs4_delegation *dp= (struct nfs4_delegation *)fl->fl_owner;
|
||||
struct task_struct *t;
|
||||
struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner;
|
||||
|
||||
dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl);
|
||||
if (!dp)
|
||||
|
@ -2111,16 +2099,8 @@ void nfsd_break_deleg_cb(struct file_lock *fl)
|
|||
*/
|
||||
fl->fl_break_time = 0;
|
||||
|
||||
t = kthread_run(do_recall, dp, "%s", "nfs4_cb_recall");
|
||||
if (IS_ERR(t)) {
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
|
||||
printk(KERN_INFO "NFSD: Callback thread failed for "
|
||||
"for client (clientid %08x/%08x)\n",
|
||||
clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
|
||||
put_nfs4_client(dp->dl_client);
|
||||
nfs4_put_delegation(dp);
|
||||
}
|
||||
dp->dl_file->fi_had_conflict = true;
|
||||
nfsd4_cb_recall(dp);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2422,7 +2402,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
|
|||
{
|
||||
struct nfs4_delegation *dp;
|
||||
struct nfs4_stateowner *sop = stp->st_stateowner;
|
||||
struct nfs4_callback *cb = &sop->so_client->cl_callback;
|
||||
struct nfs4_cb_conn *cb = &sop->so_client->cl_cb_conn;
|
||||
struct file_lock fl, *flp = &fl;
|
||||
int status, flag = 0;
|
||||
|
||||
|
@ -2614,7 +2594,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
renew_client(clp);
|
||||
status = nfserr_cb_path_down;
|
||||
if (!list_empty(&clp->cl_delegations)
|
||||
&& !atomic_read(&clp->cl_callback.cb_set))
|
||||
&& !atomic_read(&clp->cl_cb_conn.cb_set))
|
||||
goto out;
|
||||
status = nfs_ok;
|
||||
out:
|
||||
|
@ -2738,12 +2718,42 @@ nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp)
|
|||
static int
|
||||
STALE_STATEID(stateid_t *stateid)
|
||||
{
|
||||
if (stateid->si_boot == boot_time)
|
||||
return 0;
|
||||
if (time_after((unsigned long)boot_time,
|
||||
(unsigned long)stateid->si_boot)) {
|
||||
dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
|
||||
stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid,
|
||||
stateid->si_generation);
|
||||
stateid->si_boot, stateid->si_stateownerid,
|
||||
stateid->si_fileid, stateid->si_generation);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
EXPIRED_STATEID(stateid_t *stateid)
|
||||
{
|
||||
if (time_before((unsigned long)boot_time,
|
||||
((unsigned long)stateid->si_boot)) &&
|
||||
time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) {
|
||||
dprintk("NFSD: expired stateid (%08x/%08x/%08x/%08x)!\n",
|
||||
stateid->si_boot, stateid->si_stateownerid,
|
||||
stateid->si_fileid, stateid->si_generation);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __be32
|
||||
stateid_error_map(stateid_t *stateid)
|
||||
{
|
||||
if (STALE_STATEID(stateid))
|
||||
return nfserr_stale_stateid;
|
||||
if (EXPIRED_STATEID(stateid))
|
||||
return nfserr_expired;
|
||||
|
||||
dprintk("NFSD: bad stateid (%08x/%08x/%08x/%08x)!\n",
|
||||
stateid->si_boot, stateid->si_stateownerid,
|
||||
stateid->si_fileid, stateid->si_generation);
|
||||
return nfserr_bad_stateid;
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
@ -2867,8 +2877,10 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
|
|||
status = nfserr_bad_stateid;
|
||||
if (is_delegation_stateid(stateid)) {
|
||||
dp = find_delegation_stateid(ino, stateid);
|
||||
if (!dp)
|
||||
if (!dp) {
|
||||
status = stateid_error_map(stateid);
|
||||
goto out;
|
||||
}
|
||||
status = check_stateid_generation(stateid, &dp->dl_stateid,
|
||||
flags);
|
||||
if (status)
|
||||
|
@ -2881,8 +2893,10 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
|
|||
*filpp = dp->dl_vfs_file;
|
||||
} else { /* open or lock stateid */
|
||||
stp = find_stateid(stateid, flags);
|
||||
if (!stp)
|
||||
if (!stp) {
|
||||
status = stateid_error_map(stateid);
|
||||
goto out;
|
||||
}
|
||||
if (nfs4_check_fh(current_fh, stp))
|
||||
goto out;
|
||||
if (!stp->st_stateowner->so_confirmed)
|
||||
|
@ -2956,7 +2970,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
|
|||
*/
|
||||
sop = search_close_lru(stateid->si_stateownerid, flags);
|
||||
if (sop == NULL)
|
||||
return nfserr_bad_stateid;
|
||||
return stateid_error_map(stateid);
|
||||
*sopp = sop;
|
||||
goto check_replay;
|
||||
}
|
||||
|
@ -3227,8 +3241,10 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
if (!is_delegation_stateid(stateid))
|
||||
goto out;
|
||||
dp = find_delegation_stateid(inode, stateid);
|
||||
if (!dp)
|
||||
if (!dp) {
|
||||
status = stateid_error_map(stateid);
|
||||
goto out;
|
||||
}
|
||||
status = check_stateid_generation(stateid, &dp->dl_stateid, flags);
|
||||
if (status)
|
||||
goto out;
|
||||
|
@ -3455,7 +3471,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
|
|||
stp->st_stateowner = sop;
|
||||
get_nfs4_file(fp);
|
||||
stp->st_file = fp;
|
||||
stp->st_stateid.si_boot = boot_time;
|
||||
stp->st_stateid.si_boot = get_seconds();
|
||||
stp->st_stateid.si_stateownerid = sop->so_id;
|
||||
stp->st_stateid.si_fileid = fp->fi_id;
|
||||
stp->st_stateid.si_generation = 0;
|
||||
|
@ -3987,6 +4003,7 @@ nfs4_state_init(void)
|
|||
INIT_LIST_HEAD(&conf_str_hashtbl[i]);
|
||||
INIT_LIST_HEAD(&unconf_str_hashtbl[i]);
|
||||
INIT_LIST_HEAD(&unconf_id_hashtbl[i]);
|
||||
INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
|
||||
}
|
||||
for (i = 0; i < SESSION_HASH_SIZE; i++)
|
||||
INIT_LIST_HEAD(&sessionid_hashtbl[i]);
|
||||
|
@ -4009,8 +4026,6 @@ nfs4_state_init(void)
|
|||
INIT_LIST_HEAD(&close_lru);
|
||||
INIT_LIST_HEAD(&client_lru);
|
||||
INIT_LIST_HEAD(&del_recall_lru);
|
||||
for (i = 0; i < CLIENT_HASH_SIZE; i++)
|
||||
INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
|
||||
reclaim_str_hashtbl_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -83,16 +83,6 @@ check_filename(char *str, int len, __be32 err)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* START OF "GENERIC" DECODE ROUTINES.
|
||||
* These may look a little ugly since they are imported from a "generic"
|
||||
* set of XDR encode/decode routines which are intended to be shared by
|
||||
* all of our NFSv4 implementations (OpenBSD, MacOS X...).
|
||||
*
|
||||
* If the pain of reading these is too great, it should be a straightforward
|
||||
* task to translate them into Linux-specific versions which are more
|
||||
* consistent with the style used in NFSv2/v3...
|
||||
*/
|
||||
#define DECODE_HEAD \
|
||||
__be32 *p; \
|
||||
__be32 status
|
||||
|
@ -254,20 +244,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
|
|||
DECODE_TAIL;
|
||||
}
|
||||
|
||||
static u32 nfsd_attrmask[] = {
|
||||
NFSD_WRITEABLE_ATTRS_WORD0,
|
||||
NFSD_WRITEABLE_ATTRS_WORD1,
|
||||
NFSD_WRITEABLE_ATTRS_WORD2
|
||||
};
|
||||
|
||||
static u32 nfsd41_ex_attrmask[] = {
|
||||
NFSD_SUPPATTR_EXCLCREAT_WORD0,
|
||||
NFSD_SUPPATTR_EXCLCREAT_WORD1,
|
||||
NFSD_SUPPATTR_EXCLCREAT_WORD2
|
||||
};
|
||||
|
||||
static __be32
|
||||
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
|
||||
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
|
||||
struct iattr *iattr, struct nfs4_acl **acl)
|
||||
{
|
||||
int expected_len, len = 0;
|
||||
|
@ -280,18 +258,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
|
|||
if ((status = nfsd4_decode_bitmap(argp, bmval)))
|
||||
return status;
|
||||
|
||||
/*
|
||||
* According to spec, unsupported attributes return ERR_ATTRNOTSUPP;
|
||||
* read-only attributes return ERR_INVAL.
|
||||
*/
|
||||
if ((bmval[0] & ~nfsd_suppattrs0(argp->minorversion)) ||
|
||||
(bmval[1] & ~nfsd_suppattrs1(argp->minorversion)) ||
|
||||
(bmval[2] & ~nfsd_suppattrs2(argp->minorversion)))
|
||||
return nfserr_attrnotsupp;
|
||||
if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
|
||||
(bmval[2] & ~writable[2]))
|
||||
return nfserr_inval;
|
||||
|
||||
READ_BUF(4);
|
||||
READ32(expected_len);
|
||||
|
||||
|
@ -424,8 +390,11 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
|
|||
goto xdr_error;
|
||||
}
|
||||
}
|
||||
BUG_ON(bmval[2]); /* no such writeable attr supported yet */
|
||||
if (len != expected_len)
|
||||
if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
|
||||
|| bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
|
||||
|| bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
|
||||
READ_BUF(expected_len - len);
|
||||
else if (len != expected_len)
|
||||
goto xdr_error;
|
||||
|
||||
DECODE_TAIL;
|
||||
|
@ -518,8 +487,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
|
|||
if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
|
||||
return status;
|
||||
|
||||
status = nfsd4_decode_fattr(argp, create->cr_bmval, nfsd_attrmask,
|
||||
&create->cr_iattr, &create->cr_acl);
|
||||
status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
|
||||
&create->cr_acl);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
|
@ -682,7 +651,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
|
|||
case NFS4_CREATE_UNCHECKED:
|
||||
case NFS4_CREATE_GUARDED:
|
||||
status = nfsd4_decode_fattr(argp, open->op_bmval,
|
||||
nfsd_attrmask, &open->op_iattr, &open->op_acl);
|
||||
&open->op_iattr, &open->op_acl);
|
||||
if (status)
|
||||
goto out;
|
||||
break;
|
||||
|
@ -696,8 +665,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
|
|||
READ_BUF(8);
|
||||
COPYMEM(open->op_verf.data, 8);
|
||||
status = nfsd4_decode_fattr(argp, open->op_bmval,
|
||||
nfsd41_ex_attrmask, &open->op_iattr,
|
||||
&open->op_acl);
|
||||
&open->op_iattr, &open->op_acl);
|
||||
if (status)
|
||||
goto out;
|
||||
break;
|
||||
|
@ -893,8 +861,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
|
|||
status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
|
||||
if (status)
|
||||
return status;
|
||||
return nfsd4_decode_fattr(argp, setattr->sa_bmval, nfsd_attrmask,
|
||||
&setattr->sa_iattr, &setattr->sa_acl);
|
||||
return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
|
||||
&setattr->sa_acl);
|
||||
}
|
||||
|
||||
static __be32
|
||||
|
@ -1328,64 +1296,64 @@ static nfsd4_dec nfsd4_dec_ops[] = {
|
|||
};
|
||||
|
||||
static nfsd4_dec nfsd41_dec_ops[] = {
|
||||
[OP_ACCESS] (nfsd4_dec)nfsd4_decode_access,
|
||||
[OP_CLOSE] (nfsd4_dec)nfsd4_decode_close,
|
||||
[OP_COMMIT] (nfsd4_dec)nfsd4_decode_commit,
|
||||
[OP_CREATE] (nfsd4_dec)nfsd4_decode_create,
|
||||
[OP_DELEGPURGE] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_DELEGRETURN] (nfsd4_dec)nfsd4_decode_delegreturn,
|
||||
[OP_GETATTR] (nfsd4_dec)nfsd4_decode_getattr,
|
||||
[OP_GETFH] (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_LINK] (nfsd4_dec)nfsd4_decode_link,
|
||||
[OP_LOCK] (nfsd4_dec)nfsd4_decode_lock,
|
||||
[OP_LOCKT] (nfsd4_dec)nfsd4_decode_lockt,
|
||||
[OP_LOCKU] (nfsd4_dec)nfsd4_decode_locku,
|
||||
[OP_LOOKUP] (nfsd4_dec)nfsd4_decode_lookup,
|
||||
[OP_LOOKUPP] (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_NVERIFY] (nfsd4_dec)nfsd4_decode_verify,
|
||||
[OP_OPEN] (nfsd4_dec)nfsd4_decode_open,
|
||||
[OP_OPENATTR] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_OPEN_CONFIRM] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_OPEN_DOWNGRADE] (nfsd4_dec)nfsd4_decode_open_downgrade,
|
||||
[OP_PUTFH] (nfsd4_dec)nfsd4_decode_putfh,
|
||||
[OP_PUTPUBFH] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_PUTROOTFH] (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_READ] (nfsd4_dec)nfsd4_decode_read,
|
||||
[OP_READDIR] (nfsd4_dec)nfsd4_decode_readdir,
|
||||
[OP_READLINK] (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_REMOVE] (nfsd4_dec)nfsd4_decode_remove,
|
||||
[OP_RENAME] (nfsd4_dec)nfsd4_decode_rename,
|
||||
[OP_RENEW] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_RESTOREFH] (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_SAVEFH] (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_SECINFO] (nfsd4_dec)nfsd4_decode_secinfo,
|
||||
[OP_SETATTR] (nfsd4_dec)nfsd4_decode_setattr,
|
||||
[OP_SETCLIENTID] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_SETCLIENTID_CONFIRM](nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_VERIFY] (nfsd4_dec)nfsd4_decode_verify,
|
||||
[OP_WRITE] (nfsd4_dec)nfsd4_decode_write,
|
||||
[OP_RELEASE_LOCKOWNER] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_ACCESS] = (nfsd4_dec)nfsd4_decode_access,
|
||||
[OP_CLOSE] = (nfsd4_dec)nfsd4_decode_close,
|
||||
[OP_COMMIT] = (nfsd4_dec)nfsd4_decode_commit,
|
||||
[OP_CREATE] = (nfsd4_dec)nfsd4_decode_create,
|
||||
[OP_DELEGPURGE] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_DELEGRETURN] = (nfsd4_dec)nfsd4_decode_delegreturn,
|
||||
[OP_GETATTR] = (nfsd4_dec)nfsd4_decode_getattr,
|
||||
[OP_GETFH] = (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_LINK] = (nfsd4_dec)nfsd4_decode_link,
|
||||
[OP_LOCK] = (nfsd4_dec)nfsd4_decode_lock,
|
||||
[OP_LOCKT] = (nfsd4_dec)nfsd4_decode_lockt,
|
||||
[OP_LOCKU] = (nfsd4_dec)nfsd4_decode_locku,
|
||||
[OP_LOOKUP] = (nfsd4_dec)nfsd4_decode_lookup,
|
||||
[OP_LOOKUPP] = (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_NVERIFY] = (nfsd4_dec)nfsd4_decode_verify,
|
||||
[OP_OPEN] = (nfsd4_dec)nfsd4_decode_open,
|
||||
[OP_OPENATTR] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_OPEN_CONFIRM] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_OPEN_DOWNGRADE] = (nfsd4_dec)nfsd4_decode_open_downgrade,
|
||||
[OP_PUTFH] = (nfsd4_dec)nfsd4_decode_putfh,
|
||||
[OP_PUTPUBFH] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_PUTROOTFH] = (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_READ] = (nfsd4_dec)nfsd4_decode_read,
|
||||
[OP_READDIR] = (nfsd4_dec)nfsd4_decode_readdir,
|
||||
[OP_READLINK] = (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_REMOVE] = (nfsd4_dec)nfsd4_decode_remove,
|
||||
[OP_RENAME] = (nfsd4_dec)nfsd4_decode_rename,
|
||||
[OP_RENEW] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_RESTOREFH] = (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_SAVEFH] = (nfsd4_dec)nfsd4_decode_noop,
|
||||
[OP_SECINFO] = (nfsd4_dec)nfsd4_decode_secinfo,
|
||||
[OP_SETATTR] = (nfsd4_dec)nfsd4_decode_setattr,
|
||||
[OP_SETCLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_VERIFY] = (nfsd4_dec)nfsd4_decode_verify,
|
||||
[OP_WRITE] = (nfsd4_dec)nfsd4_decode_write,
|
||||
[OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
|
||||
/* new operations for NFSv4.1 */
|
||||
[OP_BACKCHANNEL_CTL] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_BIND_CONN_TO_SESSION](nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_EXCHANGE_ID] (nfsd4_dec)nfsd4_decode_exchange_id,
|
||||
[OP_CREATE_SESSION] (nfsd4_dec)nfsd4_decode_create_session,
|
||||
[OP_DESTROY_SESSION] (nfsd4_dec)nfsd4_decode_destroy_session,
|
||||
[OP_FREE_STATEID] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_GET_DIR_DELEGATION] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_GETDEVICEINFO] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_GETDEVICELIST] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_LAYOUTCOMMIT] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_LAYOUTGET] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_LAYOUTRETURN] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_SECINFO_NO_NAME] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_SEQUENCE] (nfsd4_dec)nfsd4_decode_sequence,
|
||||
[OP_SET_SSV] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_TEST_STATEID] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_WANT_DELEGATION] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_DESTROY_CLIENTID] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_RECLAIM_COMPLETE] (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
|
||||
[OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
|
||||
[OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
|
||||
[OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence,
|
||||
[OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
[OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_notsupp,
|
||||
};
|
||||
|
||||
struct nfsd4_minorversion_ops {
|
||||
|
@ -1489,21 +1457,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
|
|||
|
||||
DECODE_TAIL;
|
||||
}
|
||||
/*
|
||||
* END OF "GENERIC" DECODE ROUTINES.
|
||||
*/
|
||||
|
||||
/*
|
||||
* START OF "GENERIC" ENCODE ROUTINES.
|
||||
* These may look a little ugly since they are imported from a "generic"
|
||||
* set of XDR encode/decode routines which are intended to be shared by
|
||||
* all of our NFSv4 implementations (OpenBSD, MacOS X...).
|
||||
*
|
||||
* If the pain of reading these is too great, it should be a straightforward
|
||||
* task to translate them into Linux-specific versions which are more
|
||||
* consistent with the style used in NFSv2/v3...
|
||||
*/
|
||||
#define ENCODE_HEAD __be32 *p
|
||||
|
||||
#define WRITE32(n) *p++ = htonl(n)
|
||||
#define WRITE64(n) do { \
|
||||
|
@ -1515,13 +1468,41 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
|
|||
memcpy(p, ptr, nbytes); \
|
||||
p += XDR_QUADLEN(nbytes); \
|
||||
}} while (0)
|
||||
#define WRITECINFO(c) do { \
|
||||
*p++ = htonl(c.atomic); \
|
||||
*p++ = htonl(c.before_ctime_sec); \
|
||||
*p++ = htonl(c.before_ctime_nsec); \
|
||||
*p++ = htonl(c.after_ctime_sec); \
|
||||
*p++ = htonl(c.after_ctime_nsec); \
|
||||
} while (0)
|
||||
|
||||
static void write32(__be32 **p, u32 n)
|
||||
{
|
||||
*(*p)++ = n;
|
||||
}
|
||||
|
||||
static void write64(__be32 **p, u64 n)
|
||||
{
|
||||
write32(p, (u32)(n >> 32));
|
||||
write32(p, (u32)n);
|
||||
}
|
||||
|
||||
static void write_change(__be32 **p, struct kstat *stat, struct inode *inode)
|
||||
{
|
||||
if (IS_I_VERSION(inode)) {
|
||||
write64(p, inode->i_version);
|
||||
} else {
|
||||
write32(p, stat->ctime.tv_sec);
|
||||
write32(p, stat->ctime.tv_nsec);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
|
||||
{
|
||||
write32(p, c->atomic);
|
||||
if (c->change_supported) {
|
||||
write64(p, c->before_change);
|
||||
write64(p, c->after_change);
|
||||
} else {
|
||||
write32(p, c->before_ctime_sec);
|
||||
write32(p, c->before_ctime_nsec);
|
||||
write32(p, c->after_ctime_sec);
|
||||
write32(p, c->after_ctime_nsec);
|
||||
}
|
||||
}
|
||||
|
||||
#define RESERVE_SPACE(nbytes) do { \
|
||||
p = resp->p; \
|
||||
|
@ -1874,16 +1855,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
|||
WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
|
||||
}
|
||||
if (bmval0 & FATTR4_WORD0_CHANGE) {
|
||||
/*
|
||||
* Note: This _must_ be consistent with the scheme for writing
|
||||
* change_info, so any changes made here must be reflected there
|
||||
* as well. (See xdr4.h:set_change_info() and the WRITECINFO()
|
||||
* macro above.)
|
||||
*/
|
||||
if ((buflen -= 8) < 0)
|
||||
goto out_resource;
|
||||
WRITE32(stat.ctime.tv_sec);
|
||||
WRITE32(stat.ctime.tv_nsec);
|
||||
write_change(&p, &stat, dentry->d_inode);
|
||||
}
|
||||
if (bmval0 & FATTR4_WORD0_SIZE) {
|
||||
if ((buflen -= 8) < 0)
|
||||
|
@ -2348,7 +2322,7 @@ fail:
|
|||
static void
|
||||
nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
RESERVE_SPACE(sizeof(stateid_t));
|
||||
WRITE32(sid->si_generation);
|
||||
|
@ -2359,7 +2333,7 @@ nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
|
|||
static __be32
|
||||
nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(8);
|
||||
|
@ -2386,7 +2360,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
|
|||
static __be32
|
||||
nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(8);
|
||||
|
@ -2399,11 +2373,11 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
|
|||
static __be32
|
||||
nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(32);
|
||||
WRITECINFO(create->cr_cinfo);
|
||||
write_cinfo(&p, &create->cr_cinfo);
|
||||
WRITE32(2);
|
||||
WRITE32(create->cr_bmval[0]);
|
||||
WRITE32(create->cr_bmval[1]);
|
||||
|
@ -2435,7 +2409,7 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
|
|||
{
|
||||
struct svc_fh *fhp = *fhpp;
|
||||
unsigned int len;
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
len = fhp->fh_handle.fh_size;
|
||||
|
@ -2454,7 +2428,7 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
|
|||
static void
|
||||
nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
|
||||
WRITE64(ld->ld_start);
|
||||
|
@ -2510,11 +2484,11 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
|
|||
static __be32
|
||||
nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(20);
|
||||
WRITECINFO(link->li_cinfo);
|
||||
write_cinfo(&p, &link->li_cinfo);
|
||||
ADJUST_ARGS();
|
||||
}
|
||||
return nfserr;
|
||||
|
@ -2524,7 +2498,7 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li
|
|||
static __be32
|
||||
nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
ENCODE_SEQID_OP_HEAD;
|
||||
|
||||
if (nfserr)
|
||||
|
@ -2532,7 +2506,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
|
|||
|
||||
nfsd4_encode_stateid(resp, &open->op_stateid);
|
||||
RESERVE_SPACE(40);
|
||||
WRITECINFO(open->op_cinfo);
|
||||
write_cinfo(&p, &open->op_cinfo);
|
||||
WRITE32(open->op_rflags);
|
||||
WRITE32(2);
|
||||
WRITE32(open->op_bmval[0]);
|
||||
|
@ -2619,7 +2593,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
|
|||
int v, pn;
|
||||
unsigned long maxcount;
|
||||
long len;
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (nfserr)
|
||||
return nfserr;
|
||||
|
@ -2681,7 +2655,7 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
|
|||
{
|
||||
int maxcount;
|
||||
char *page;
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (nfserr)
|
||||
return nfserr;
|
||||
|
@ -2730,7 +2704,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
|
|||
int maxcount;
|
||||
loff_t offset;
|
||||
__be32 *page, *savep, *tailbase;
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (nfserr)
|
||||
return nfserr;
|
||||
|
@ -2806,11 +2780,11 @@ err_no_verf:
|
|||
static __be32
|
||||
nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(20);
|
||||
WRITECINFO(remove->rm_cinfo);
|
||||
write_cinfo(&p, &remove->rm_cinfo);
|
||||
ADJUST_ARGS();
|
||||
}
|
||||
return nfserr;
|
||||
|
@ -2819,12 +2793,12 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
|
|||
static __be32
|
||||
nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(40);
|
||||
WRITECINFO(rename->rn_sinfo);
|
||||
WRITECINFO(rename->rn_tinfo);
|
||||
write_cinfo(&p, &rename->rn_sinfo);
|
||||
write_cinfo(&p, &rename->rn_tinfo);
|
||||
ADJUST_ARGS();
|
||||
}
|
||||
return nfserr;
|
||||
|
@ -2839,7 +2813,7 @@ nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
|
|||
u32 nflavs;
|
||||
struct exp_flavor_info *flavs;
|
||||
struct exp_flavor_info def_flavs[2];
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (nfserr)
|
||||
goto out;
|
||||
|
@ -2904,7 +2878,7 @@ out:
|
|||
static __be32
|
||||
nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
RESERVE_SPACE(12);
|
||||
if (nfserr) {
|
||||
|
@ -2924,7 +2898,7 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
|
|||
static __be32
|
||||
nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(8 + sizeof(nfs4_verifier));
|
||||
|
@ -2944,7 +2918,7 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct n
|
|||
static __be32
|
||||
nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (!nfserr) {
|
||||
RESERVE_SPACE(16);
|
||||
|
@ -2960,7 +2934,7 @@ static __be32
|
|||
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
|
||||
struct nfsd4_exchange_id *exid)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
char *major_id;
|
||||
char *server_scope;
|
||||
int major_id_sz;
|
||||
|
@ -3015,7 +2989,7 @@ static __be32
|
|||
nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
|
||||
struct nfsd4_create_session *sess)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (nfserr)
|
||||
return nfserr;
|
||||
|
@ -3071,7 +3045,7 @@ __be32
|
|||
nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
|
||||
struct nfsd4_sequence *seq)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
if (nfserr)
|
||||
return nfserr;
|
||||
|
@ -3209,7 +3183,7 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
|
|||
dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
|
||||
length, xb->page_len, tlen, pad);
|
||||
|
||||
if (length <= session->se_fmaxresp_cached)
|
||||
if (length <= session->se_fchannel.maxresp_cached)
|
||||
return status;
|
||||
else
|
||||
return nfserr_rep_too_big_to_cache;
|
||||
|
@ -3219,7 +3193,7 @@ void
|
|||
nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
|
||||
{
|
||||
__be32 *statp;
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
|
||||
RESERVE_SPACE(8);
|
||||
WRITE32(op->opnum);
|
||||
|
@ -3253,7 +3227,7 @@ status:
|
|||
void
|
||||
nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
|
||||
{
|
||||
ENCODE_HEAD;
|
||||
__be32 *p;
|
||||
struct nfs4_replay *rp = op->replay;
|
||||
|
||||
BUG_ON(!rp);
|
||||
|
@ -3268,10 +3242,6 @@ nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
|
|||
ADJUST_ARGS();
|
||||
}
|
||||
|
||||
/*
|
||||
* END OF "GENERIC" ENCODE ROUTINES.
|
||||
*/
|
||||
|
||||
int
|
||||
nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
||||
{
|
||||
|
|
|
@ -29,12 +29,21 @@
|
|||
*/
|
||||
#define CACHESIZE 1024
|
||||
#define HASHSIZE 64
|
||||
#define REQHASH(xid) (((((__force __u32)xid) >> 24) ^ ((__force __u32)xid)) & (HASHSIZE-1))
|
||||
|
||||
static struct hlist_head * hash_list;
|
||||
static struct hlist_head * cache_hash;
|
||||
static struct list_head lru_head;
|
||||
static int cache_disabled = 1;
|
||||
|
||||
/*
|
||||
* Calculate the hash index from an XID.
|
||||
*/
|
||||
static inline u32 request_hash(u32 xid)
|
||||
{
|
||||
u32 h = xid;
|
||||
h ^= (xid >> 24);
|
||||
return h & (HASHSIZE-1);
|
||||
}
|
||||
|
||||
static int nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
|
||||
|
||||
/*
|
||||
|
@ -62,8 +71,8 @@ int nfsd_reply_cache_init(void)
|
|||
i--;
|
||||
}
|
||||
|
||||
hash_list = kcalloc (HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL);
|
||||
if (!hash_list)
|
||||
cache_hash = kcalloc (HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL);
|
||||
if (!cache_hash)
|
||||
goto out_nomem;
|
||||
|
||||
cache_disabled = 0;
|
||||
|
@ -88,8 +97,8 @@ void nfsd_reply_cache_shutdown(void)
|
|||
|
||||
cache_disabled = 1;
|
||||
|
||||
kfree (hash_list);
|
||||
hash_list = NULL;
|
||||
kfree (cache_hash);
|
||||
cache_hash = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -108,7 +117,7 @@ static void
|
|||
hash_refile(struct svc_cacherep *rp)
|
||||
{
|
||||
hlist_del_init(&rp->c_hash);
|
||||
hlist_add_head(&rp->c_hash, hash_list + REQHASH(rp->c_xid));
|
||||
hlist_add_head(&rp->c_hash, cache_hash + request_hash(rp->c_xid));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -138,7 +147,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
|
|||
spin_lock(&cache_lock);
|
||||
rtn = RC_DOIT;
|
||||
|
||||
rh = &hash_list[REQHASH(xid)];
|
||||
rh = &cache_hash[request_hash(xid)];
|
||||
hlist_for_each_entry(rp, hn, rh, c_hash) {
|
||||
if (rp->c_state != RC_UNUSED &&
|
||||
xid == rp->c_xid && proc == rp->c_proc &&
|
||||
|
@ -165,8 +174,8 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
|
|||
}
|
||||
}
|
||||
|
||||
/* This should not happen */
|
||||
if (rp == NULL) {
|
||||
/* All entries on the LRU are in-progress. This should not happen */
|
||||
if (&rp->c_lru == &lru_head) {
|
||||
static int complaints;
|
||||
|
||||
printk(KERN_WARNING "nfsd: all repcache entries locked!\n");
|
||||
|
|
256
fs/nfsd/nfsctl.c
256
fs/nfsd/nfsctl.c
|
@ -207,10 +207,14 @@ static struct file_operations pool_stats_operations = {
|
|||
static ssize_t write_svc(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
struct nfsctl_svc *data;
|
||||
int err;
|
||||
if (size < sizeof(*data))
|
||||
return -EINVAL;
|
||||
data = (struct nfsctl_svc*) buf;
|
||||
return nfsd_svc(data->svc_port, data->svc_nthreads);
|
||||
err = nfsd_svc(data->svc_port, data->svc_nthreads);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -692,11 +696,12 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
|
|||
if (newthreads < 0)
|
||||
return -EINVAL;
|
||||
rv = nfsd_svc(NFS_PORT, newthreads);
|
||||
if (rv)
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
}
|
||||
sprintf(buf, "%d\n", nfsd_nrthreads());
|
||||
return strlen(buf);
|
||||
} else
|
||||
rv = nfsd_nrthreads();
|
||||
|
||||
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -793,7 +798,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
|||
{
|
||||
char *mesg = buf;
|
||||
char *vers, *minorp, sign;
|
||||
int len, num;
|
||||
int len, num, remaining;
|
||||
unsigned minor;
|
||||
ssize_t tlen = 0;
|
||||
char *sep;
|
||||
|
@ -840,32 +845,50 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
|||
}
|
||||
next:
|
||||
vers += len + 1;
|
||||
tlen += len;
|
||||
} while ((len = qword_get(&mesg, vers, size)) > 0);
|
||||
/* If all get turned off, turn them back on, as
|
||||
* having no versions is BAD
|
||||
*/
|
||||
nfsd_reset_versions();
|
||||
}
|
||||
|
||||
/* Now write current state into reply buffer */
|
||||
len = 0;
|
||||
sep = "";
|
||||
remaining = SIMPLE_TRANSACTION_LIMIT;
|
||||
for (num=2 ; num <= 4 ; num++)
|
||||
if (nfsd_vers(num, NFSD_AVAIL)) {
|
||||
len += sprintf(buf+len, "%s%c%d", sep,
|
||||
len = snprintf(buf, remaining, "%s%c%d", sep,
|
||||
nfsd_vers(num, NFSD_TEST)?'+':'-',
|
||||
num);
|
||||
sep = " ";
|
||||
|
||||
if (len > remaining)
|
||||
break;
|
||||
remaining -= len;
|
||||
buf += len;
|
||||
tlen += len;
|
||||
}
|
||||
if (nfsd_vers(4, NFSD_AVAIL))
|
||||
for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION; minor++)
|
||||
len += sprintf(buf+len, " %c4.%u",
|
||||
for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
|
||||
minor++) {
|
||||
len = snprintf(buf, remaining, " %c4.%u",
|
||||
(nfsd_vers(4, NFSD_TEST) &&
|
||||
nfsd_minorversion(minor, NFSD_TEST)) ?
|
||||
'+' : '-',
|
||||
minor);
|
||||
len += sprintf(buf+len, "\n");
|
||||
return len;
|
||||
|
||||
if (len > remaining)
|
||||
break;
|
||||
remaining -= len;
|
||||
buf += len;
|
||||
tlen += len;
|
||||
}
|
||||
|
||||
len = snprintf(buf, remaining, "\n");
|
||||
if (len > remaining)
|
||||
return -EINVAL;
|
||||
return tlen + len;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -910,104 +933,143 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
|
|||
return rv;
|
||||
}
|
||||
|
||||
static ssize_t __write_ports(struct file *file, char *buf, size_t size)
|
||||
/*
|
||||
* Zero-length write. Return a list of NFSD's current listener
|
||||
* transports.
|
||||
*/
|
||||
static ssize_t __write_ports_names(char *buf)
|
||||
{
|
||||
if (size == 0) {
|
||||
if (nfsd_serv == NULL)
|
||||
return 0;
|
||||
return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* A single 'fd' number was written, in which case it must be for
|
||||
* a socket of a supported family/protocol, and we use it as an
|
||||
* nfsd listener.
|
||||
*/
|
||||
static ssize_t __write_ports_addfd(char *buf)
|
||||
{
|
||||
char *mesg = buf;
|
||||
int fd, err;
|
||||
|
||||
err = get_int(&mesg, &fd);
|
||||
if (err != 0 || fd < 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = nfsd_create_serv();
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = lockd_up();
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
|
||||
if (err < 0)
|
||||
lockd_down();
|
||||
|
||||
out:
|
||||
/* Decrease the count, but don't shut down the service */
|
||||
nfsd_serv->sv_nrthreads--;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* A '-' followed by the 'name' of a socket means we close the socket.
|
||||
*/
|
||||
static ssize_t __write_ports_delfd(char *buf)
|
||||
{
|
||||
char *toclose;
|
||||
int len = 0;
|
||||
|
||||
if (nfsd_serv)
|
||||
len = svc_xprt_names(nfsd_serv, buf, 0);
|
||||
return len;
|
||||
}
|
||||
/* Either a single 'fd' number is written, in which
|
||||
* case it must be for a socket of a supported family/protocol,
|
||||
* and we use it as an nfsd socket, or
|
||||
* A '-' followed by the 'name' of a socket in which case
|
||||
* we close the socket.
|
||||
*/
|
||||
if (isdigit(buf[0])) {
|
||||
char *mesg = buf;
|
||||
int fd;
|
||||
int err;
|
||||
err = get_int(&mesg, &fd);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
if (fd < 0)
|
||||
return -EINVAL;
|
||||
err = nfsd_create_serv();
|
||||
if (!err) {
|
||||
err = svc_addsock(nfsd_serv, fd, buf);
|
||||
if (err >= 0) {
|
||||
err = lockd_up();
|
||||
if (err < 0)
|
||||
svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
|
||||
}
|
||||
/* Decrease the count, but don't shutdown the
|
||||
* the service
|
||||
*/
|
||||
nfsd_serv->sv_nrthreads--;
|
||||
}
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
if (buf[0] == '-' && isdigit(buf[1])) {
|
||||
char *toclose = kstrdup(buf+1, GFP_KERNEL);
|
||||
int len = 0;
|
||||
if (!toclose)
|
||||
toclose = kstrdup(buf + 1, GFP_KERNEL);
|
||||
if (toclose == NULL)
|
||||
return -ENOMEM;
|
||||
if (nfsd_serv)
|
||||
len = svc_sock_names(buf, nfsd_serv, toclose);
|
||||
|
||||
if (nfsd_serv != NULL)
|
||||
len = svc_sock_names(nfsd_serv, buf,
|
||||
SIMPLE_TRANSACTION_LIMIT, toclose);
|
||||
if (len >= 0)
|
||||
lockd_down();
|
||||
|
||||
kfree(toclose);
|
||||
return len;
|
||||
}
|
||||
/*
|
||||
* Add a transport listener by writing it's transport name
|
||||
}
|
||||
|
||||
/*
|
||||
* A transport listener is added by writing it's transport name and
|
||||
* a port number.
|
||||
*/
|
||||
if (isalpha(buf[0])) {
|
||||
int err;
|
||||
static ssize_t __write_ports_addxprt(char *buf)
|
||||
{
|
||||
char transport[16];
|
||||
int port;
|
||||
if (sscanf(buf, "%15s %4d", transport, &port) == 2) {
|
||||
if (port < 1 || port > 65535)
|
||||
int port, err;
|
||||
|
||||
if (sscanf(buf, "%15s %4u", transport, &port) != 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (port < 1 || port > USHORT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
err = nfsd_create_serv();
|
||||
if (!err) {
|
||||
err = svc_create_xprt(nfsd_serv,
|
||||
transport, PF_INET, port,
|
||||
SVC_SOCK_ANONYMOUS);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = svc_create_xprt(nfsd_serv, transport,
|
||||
PF_INET, port, SVC_SOCK_ANONYMOUS);
|
||||
if (err < 0) {
|
||||
/* Give a reasonable perror msg for bad transport string */
|
||||
if (err == -ENOENT)
|
||||
/* Give a reasonable perror msg for
|
||||
* bad transport string */
|
||||
err = -EPROTONOSUPPORT;
|
||||
return err;
|
||||
}
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Remove a transport by writing it's transport name and port number
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A transport listener is removed by writing a "-", it's transport
|
||||
* name, and it's port number.
|
||||
*/
|
||||
if (buf[0] == '-' && isalpha(buf[1])) {
|
||||
static ssize_t __write_ports_delxprt(char *buf)
|
||||
{
|
||||
struct svc_xprt *xprt;
|
||||
int err = -EINVAL;
|
||||
char transport[16];
|
||||
int port;
|
||||
if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) {
|
||||
if (port < 1 || port > 65535)
|
||||
|
||||
if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
|
||||
return -EINVAL;
|
||||
if (nfsd_serv) {
|
||||
xprt = svc_find_xprt(nfsd_serv, transport,
|
||||
AF_UNSPEC, port);
|
||||
if (xprt) {
|
||||
|
||||
if (port < 1 || port > USHORT_MAX || nfsd_serv == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port);
|
||||
if (xprt == NULL)
|
||||
return -ENOTCONN;
|
||||
|
||||
svc_close_xprt(xprt);
|
||||
svc_xprt_put(xprt);
|
||||
err = 0;
|
||||
} else
|
||||
err = -ENOTCONN;
|
||||
}
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t __write_ports(struct file *file, char *buf, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return __write_ports_names(buf);
|
||||
|
||||
if (isdigit(buf[0]))
|
||||
return __write_ports_addfd(buf);
|
||||
|
||||
if (buf[0] == '-' && isdigit(buf[1]))
|
||||
return __write_ports_delfd(buf);
|
||||
|
||||
if (isalpha(buf[0]))
|
||||
return __write_ports_addxprt(buf);
|
||||
|
||||
if (buf[0] == '-' && isalpha(buf[1]))
|
||||
return __write_ports_delxprt(buf);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1030,7 +1092,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
|
|||
* buf: C string containing an unsigned
|
||||
* integer value representing a bound
|
||||
* but unconnected socket that is to be
|
||||
* used as an NFSD listener
|
||||
* used as an NFSD listener; listen(3)
|
||||
* must be called for a SOCK_STREAM
|
||||
* socket, otherwise it is ignored
|
||||
* size: non-zero length of C string in @buf
|
||||
* Output:
|
||||
* On success: NFS service is started;
|
||||
|
@ -1138,7 +1202,9 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
|
|||
nfsd_max_blksize = bsize;
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
}
|
||||
return sprintf(buf, "%d\n", nfsd_max_blksize);
|
||||
|
||||
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
|
||||
nfsd_max_blksize);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFSD_V4
|
||||
|
@ -1162,8 +1228,9 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
|
|||
return -EINVAL;
|
||||
nfs4_reset_lease(lease);
|
||||
}
|
||||
sprintf(buf, "%ld\n", nfs4_lease_time());
|
||||
return strlen(buf);
|
||||
|
||||
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n",
|
||||
nfs4_lease_time());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1219,8 +1286,9 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
|
|||
|
||||
status = nfs4_reset_recoverydir(recdir);
|
||||
}
|
||||
sprintf(buf, "%s\n", nfs4_recoverydir());
|
||||
return strlen(buf);
|
||||
|
||||
return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n",
|
||||
nfs4_recoverydir());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,9 +27,6 @@
|
|||
#define NFSDDBG_FACILITY NFSDDBG_FH
|
||||
|
||||
|
||||
static int nfsd_nr_verified;
|
||||
static int nfsd_nr_put;
|
||||
|
||||
/*
|
||||
* our acceptability function.
|
||||
* if NOSUBTREECHECK, accept anything
|
||||
|
@ -251,7 +248,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
|
|||
|
||||
fhp->fh_dentry = dentry;
|
||||
fhp->fh_export = exp;
|
||||
nfsd_nr_verified++;
|
||||
return 0;
|
||||
out:
|
||||
exp_put(exp);
|
||||
|
@ -552,7 +548,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
|
|||
return nfserr_opnotsupp;
|
||||
}
|
||||
|
||||
nfsd_nr_verified++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -609,7 +604,6 @@ fh_put(struct svc_fh *fhp)
|
|||
fhp->fh_pre_saved = 0;
|
||||
fhp->fh_post_saved = 0;
|
||||
#endif
|
||||
nfsd_nr_put++;
|
||||
}
|
||||
if (exp) {
|
||||
cache_put(&exp->h, &svc_export_cache);
|
||||
|
|
|
@ -533,45 +533,179 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
|
|||
* NFSv2 Server procedures.
|
||||
* Only the results of non-idempotent operations are cached.
|
||||
*/
|
||||
#define nfsd_proc_none NULL
|
||||
#define nfssvc_release_none NULL
|
||||
struct nfsd_void { int dummy; };
|
||||
|
||||
#define PROC(name, argt, rest, relt, cache, respsize) \
|
||||
{ (svc_procfunc) nfsd_proc_##name, \
|
||||
(kxdrproc_t) nfssvc_decode_##argt, \
|
||||
(kxdrproc_t) nfssvc_encode_##rest, \
|
||||
(kxdrproc_t) nfssvc_release_##relt, \
|
||||
sizeof(struct nfsd_##argt), \
|
||||
sizeof(struct nfsd_##rest), \
|
||||
0, \
|
||||
cache, \
|
||||
respsize, \
|
||||
}
|
||||
|
||||
#define ST 1 /* status */
|
||||
#define FH 8 /* filehandle */
|
||||
#define AT 18 /* attributes */
|
||||
|
||||
static struct svc_procedure nfsd_procedures2[18] = {
|
||||
PROC(null, void, void, none, RC_NOCACHE, ST),
|
||||
PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT),
|
||||
PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF, ST+AT),
|
||||
PROC(none, void, void, none, RC_NOCACHE, ST),
|
||||
PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, ST+FH+AT),
|
||||
PROC(readlink, readlinkargs, readlinkres, none, RC_NOCACHE, ST+1+NFS_MAXPATHLEN/4),
|
||||
PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4),
|
||||
PROC(none, void, void, none, RC_NOCACHE, ST),
|
||||
PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, ST+AT),
|
||||
PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT),
|
||||
PROC(remove, diropargs, void, none, RC_REPLSTAT, ST),
|
||||
PROC(rename, renameargs, void, none, RC_REPLSTAT, ST),
|
||||
PROC(link, linkargs, void, none, RC_REPLSTAT, ST),
|
||||
PROC(symlink, symlinkargs, void, none, RC_REPLSTAT, ST),
|
||||
PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT),
|
||||
PROC(rmdir, diropargs, void, none, RC_REPLSTAT, ST),
|
||||
PROC(readdir, readdirargs, readdirres, none, RC_NOCACHE, 0),
|
||||
PROC(statfs, fhandle, statfsres, none, RC_NOCACHE, ST+5),
|
||||
[NFSPROC_NULL] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_null,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_void,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_void),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_GETATTR] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_getattr,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT,
|
||||
},
|
||||
[NFSPROC_SETATTR] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_setattr,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_sattrargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_sattrargs),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+AT,
|
||||
},
|
||||
[NFSPROC_ROOT] = {
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_void,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_void),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_LOOKUP] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_lookup,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd_diropres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+FH+AT,
|
||||
},
|
||||
[NFSPROC_READLINK] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_readlink,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_readlinkargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_readlinkres,
|
||||
.pc_argsize = sizeof(struct nfsd_readlinkargs),
|
||||
.pc_ressize = sizeof(struct nfsd_readlinkres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+1+NFS_MAXPATHLEN/4,
|
||||
},
|
||||
[NFSPROC_READ] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_read,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_readargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_readres,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_readargs),
|
||||
.pc_ressize = sizeof(struct nfsd_readres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4,
|
||||
},
|
||||
[NFSPROC_WRITECACHE] = {
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_void,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_void),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_WRITE] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_write,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_writeargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_writeargs),
|
||||
.pc_ressize = sizeof(struct nfsd_attrstat),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+AT,
|
||||
},
|
||||
[NFSPROC_CREATE] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_create,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_createargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_createargs),
|
||||
.pc_ressize = sizeof(struct nfsd_diropres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+FH+AT,
|
||||
},
|
||||
[NFSPROC_REMOVE] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_remove,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_RENAME] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_rename,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_renameargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_renameargs),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_LINK] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_link,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_linkargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_linkargs),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_SYMLINK] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_symlink,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_symlinkargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_symlinkargs),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_MKDIR] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_mkdir,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_createargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_diropres,
|
||||
.pc_release = (kxdrproc_t) nfssvc_release_fhandle,
|
||||
.pc_argsize = sizeof(struct nfsd_createargs),
|
||||
.pc_ressize = sizeof(struct nfsd_diropres),
|
||||
.pc_cachetype = RC_REPLBUFF,
|
||||
.pc_xdrressize = ST+FH+AT,
|
||||
},
|
||||
[NFSPROC_RMDIR] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_rmdir,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_diropargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_void,
|
||||
.pc_argsize = sizeof(struct nfsd_diropargs),
|
||||
.pc_ressize = sizeof(struct nfsd_void),
|
||||
.pc_cachetype = RC_REPLSTAT,
|
||||
.pc_xdrressize = ST,
|
||||
},
|
||||
[NFSPROC_READDIR] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_readdir,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_readdirargs,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_readdirres,
|
||||
.pc_argsize = sizeof(struct nfsd_readdirargs),
|
||||
.pc_ressize = sizeof(struct nfsd_readdirres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
},
|
||||
[NFSPROC_STATFS] = {
|
||||
.pc_func = (svc_procfunc) nfsd_proc_statfs,
|
||||
.pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
|
||||
.pc_encode = (kxdrproc_t) nfssvc_encode_statfsres,
|
||||
.pc_argsize = sizeof(struct nfsd_fhandle),
|
||||
.pc_ressize = sizeof(struct nfsd_statfsres),
|
||||
.pc_cachetype = RC_NOCACHE,
|
||||
.pc_xdrressize = ST+5,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -390,11 +390,13 @@ nfsd_svc(unsigned short port, int nrservs)
|
|||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
dprintk("nfsd: creating service\n");
|
||||
error = -EINVAL;
|
||||
if (nrservs <= 0)
|
||||
nrservs = 0;
|
||||
if (nrservs > NFSD_MAXSERVS)
|
||||
nrservs = NFSD_MAXSERVS;
|
||||
error = 0;
|
||||
if (nrservs == 0 && nfsd_serv == NULL)
|
||||
goto out;
|
||||
|
||||
/* Readahead param cache - will no-op if it already exists */
|
||||
error = nfsd_racache_init(2*nrservs);
|
||||
|
@ -413,6 +415,12 @@ nfsd_svc(unsigned short port, int nrservs)
|
|||
goto failure;
|
||||
|
||||
error = svc_set_num_threads(nfsd_serv, NULL, nrservs);
|
||||
if (error == 0)
|
||||
/* We are holding a reference to nfsd_serv which
|
||||
* we don't want to count in the return value,
|
||||
* so subtract 1
|
||||
*/
|
||||
error = nfsd_serv->sv_nrthreads - 1;
|
||||
failure:
|
||||
svc_destroy(nfsd_serv); /* Release server */
|
||||
out:
|
||||
|
|
|
@ -966,6 +966,43 @@ static void kill_suid(struct dentry *dentry)
|
|||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gathered writes: If another process is currently writing to the file,
|
||||
* there's a high chance this is another nfsd (triggered by a bulk write
|
||||
* from a client's biod). Rather than syncing the file with each write
|
||||
* request, we sleep for 10 msec.
|
||||
*
|
||||
* I don't know if this roughly approximates C. Juszak's idea of
|
||||
* gathered writes, but it's a nice and simple solution (IMHO), and it
|
||||
* seems to work:-)
|
||||
*
|
||||
* Note: we do this only in the NFSv2 case, since v3 and higher have a
|
||||
* better tool (separate unstable writes and commits) for solving this
|
||||
* problem.
|
||||
*/
|
||||
static int wait_for_concurrent_writes(struct file *file)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
static ino_t last_ino;
|
||||
static dev_t last_dev;
|
||||
int err = 0;
|
||||
|
||||
if (atomic_read(&inode->i_writecount) > 1
|
||||
|| (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) {
|
||||
dprintk("nfsd: write defer %d\n", task_pid_nr(current));
|
||||
msleep(10);
|
||||
dprintk("nfsd: write resume %d\n", task_pid_nr(current));
|
||||
}
|
||||
|
||||
if (inode->i_state & I_DIRTY) {
|
||||
dprintk("nfsd: write sync %d\n", task_pid_nr(current));
|
||||
err = nfsd_sync(file);
|
||||
}
|
||||
last_ino = inode->i_ino;
|
||||
last_dev = inode->i_sb->s_dev;
|
||||
return err;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
||||
loff_t offset, struct kvec *vec, int vlen,
|
||||
|
@ -978,6 +1015,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
|||
__be32 err = 0;
|
||||
int host_err;
|
||||
int stable = *stablep;
|
||||
int use_wgather;
|
||||
|
||||
#ifdef MSNFS
|
||||
err = nfserr_perm;
|
||||
|
@ -996,9 +1034,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
|||
* - the sync export option has been set, or
|
||||
* - the client requested O_SYNC behavior (NFSv3 feature).
|
||||
* - The file system doesn't support fsync().
|
||||
* When gathered writes have been configured for this volume,
|
||||
* When NFSv2 gathered writes have been configured for this volume,
|
||||
* flushing the data to disk is handled separately below.
|
||||
*/
|
||||
use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp);
|
||||
|
||||
if (!file->f_op->fsync) {/* COMMIT3 cannot work */
|
||||
stable = 2;
|
||||
|
@ -1007,7 +1046,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
|||
|
||||
if (!EX_ISSYNC(exp))
|
||||
stable = 0;
|
||||
if (stable && !EX_WGATHER(exp)) {
|
||||
if (stable && !use_wgather) {
|
||||
spin_lock(&file->f_lock);
|
||||
file->f_flags |= O_SYNC;
|
||||
spin_unlock(&file->f_lock);
|
||||
|
@ -1017,52 +1056,20 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
|
|||
oldfs = get_fs(); set_fs(KERNEL_DS);
|
||||
host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
|
||||
set_fs(oldfs);
|
||||
if (host_err >= 0) {
|
||||
if (host_err < 0)
|
||||
goto out_nfserr;
|
||||
*cnt = host_err;
|
||||
nfsdstats.io_write += host_err;
|
||||
fsnotify_modify(file->f_path.dentry);
|
||||
}
|
||||
|
||||
/* clear setuid/setgid flag after write */
|
||||
if (host_err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID)))
|
||||
if (inode->i_mode & (S_ISUID | S_ISGID))
|
||||
kill_suid(dentry);
|
||||
|
||||
if (host_err >= 0 && stable) {
|
||||
static ino_t last_ino;
|
||||
static dev_t last_dev;
|
||||
|
||||
/*
|
||||
* Gathered writes: If another process is currently
|
||||
* writing to the file, there's a high chance
|
||||
* this is another nfsd (triggered by a bulk write
|
||||
* from a client's biod). Rather than syncing the
|
||||
* file with each write request, we sleep for 10 msec.
|
||||
*
|
||||
* I don't know if this roughly approximates
|
||||
* C. Juszak's idea of gathered writes, but it's a
|
||||
* nice and simple solution (IMHO), and it seems to
|
||||
* work:-)
|
||||
*/
|
||||
if (EX_WGATHER(exp)) {
|
||||
if (atomic_read(&inode->i_writecount) > 1
|
||||
|| (last_ino == inode->i_ino && last_dev == inode->i_sb->s_dev)) {
|
||||
dprintk("nfsd: write defer %d\n", task_pid_nr(current));
|
||||
msleep(10);
|
||||
dprintk("nfsd: write resume %d\n", task_pid_nr(current));
|
||||
}
|
||||
|
||||
if (inode->i_state & I_DIRTY) {
|
||||
dprintk("nfsd: write sync %d\n", task_pid_nr(current));
|
||||
host_err=nfsd_sync(file);
|
||||
}
|
||||
#if 0
|
||||
wake_up(&inode->i_wait);
|
||||
#endif
|
||||
}
|
||||
last_ino = inode->i_ino;
|
||||
last_dev = inode->i_sb->s_dev;
|
||||
}
|
||||
if (stable && use_wgather)
|
||||
host_err = wait_for_concurrent_writes(file);
|
||||
|
||||
out_nfserr:
|
||||
dprintk("nfsd: write complete host_err=%d\n", host_err);
|
||||
if (host_err >= 0)
|
||||
err = 0;
|
||||
|
|
|
@ -1107,6 +1107,7 @@ extern void locks_copy_lock(struct file_lock *, struct file_lock *);
|
|||
extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
|
||||
extern void locks_remove_posix(struct file *, fl_owner_t);
|
||||
extern void locks_remove_flock(struct file *);
|
||||
extern void locks_release_private(struct file_lock *);
|
||||
extern void posix_test_lock(struct file *, struct file_lock *);
|
||||
extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
|
||||
extern int posix_lock_file_wait(struct file *, struct file_lock *);
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
#include <linux/uio.h>
|
||||
|
||||
/*
|
||||
* Representation of a reply cache entry. The first two members *must*
|
||||
* be hash_next and hash_prev.
|
||||
* Representation of a reply cache entry.
|
||||
*/
|
||||
struct svc_cacherep {
|
||||
struct hlist_node c_hash;
|
||||
|
|
|
@ -151,9 +151,15 @@ typedef struct svc_fh {
|
|||
__u64 fh_pre_size; /* size before operation */
|
||||
struct timespec fh_pre_mtime; /* mtime before oper */
|
||||
struct timespec fh_pre_ctime; /* ctime before oper */
|
||||
/*
|
||||
* pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
|
||||
* to find out if it is valid.
|
||||
*/
|
||||
u64 fh_pre_change;
|
||||
|
||||
/* Post-op attributes saved in fh_unlock */
|
||||
struct kstat fh_post_attr; /* full attrs after operation */
|
||||
u64 fh_post_change; /* nfsv4 change; see above */
|
||||
#endif /* CONFIG_NFSD_V3 */
|
||||
|
||||
} svc_fh;
|
||||
|
@ -298,6 +304,7 @@ fill_pre_wcc(struct svc_fh *fhp)
|
|||
fhp->fh_pre_mtime = inode->i_mtime;
|
||||
fhp->fh_pre_ctime = inode->i_ctime;
|
||||
fhp->fh_pre_size = inode->i_size;
|
||||
fhp->fh_pre_change = inode->i_version;
|
||||
fhp->fh_pre_saved = 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,15 +60,6 @@ typedef struct {
|
|||
#define si_stateownerid si_opaque.so_stateownerid
|
||||
#define si_fileid si_opaque.so_fileid
|
||||
|
||||
|
||||
struct nfs4_cb_recall {
|
||||
u32 cbr_ident;
|
||||
int cbr_trunc;
|
||||
stateid_t cbr_stateid;
|
||||
struct knfsd_fh cbr_fh;
|
||||
struct nfs4_delegation *cbr_dp;
|
||||
};
|
||||
|
||||
struct nfs4_delegation {
|
||||
struct list_head dl_perfile;
|
||||
struct list_head dl_perclnt;
|
||||
|
@ -80,22 +71,25 @@ struct nfs4_delegation {
|
|||
struct file *dl_vfs_file;
|
||||
u32 dl_type;
|
||||
time_t dl_time;
|
||||
struct nfs4_cb_recall dl_recall;
|
||||
/* For recall: */
|
||||
u32 dl_ident;
|
||||
stateid_t dl_stateid;
|
||||
struct knfsd_fh dl_fh;
|
||||
int dl_retries;
|
||||
};
|
||||
|
||||
#define dl_stateid dl_recall.cbr_stateid
|
||||
#define dl_fh dl_recall.cbr_fh
|
||||
|
||||
/* client delegation callback info */
|
||||
struct nfs4_callback {
|
||||
struct nfs4_cb_conn {
|
||||
/* SETCLIENTID info */
|
||||
u32 cb_addr;
|
||||
unsigned short cb_port;
|
||||
u32 cb_prog;
|
||||
u32 cb_ident;
|
||||
u32 cb_minorversion;
|
||||
u32 cb_ident; /* minorversion 0 only */
|
||||
/* RPC client info */
|
||||
atomic_t cb_set; /* successful CB_NULL call */
|
||||
struct rpc_clnt * cb_client;
|
||||
struct rpc_cred * cb_cred;
|
||||
};
|
||||
|
||||
/* Maximum number of slots per session. 128 is useful for long haul TCP */
|
||||
|
@ -121,6 +115,17 @@ struct nfsd4_slot {
|
|||
struct nfsd4_cache_entry sl_cache_entry;
|
||||
};
|
||||
|
||||
struct nfsd4_channel_attrs {
|
||||
u32 headerpadsz;
|
||||
u32 maxreq_sz;
|
||||
u32 maxresp_sz;
|
||||
u32 maxresp_cached;
|
||||
u32 maxops;
|
||||
u32 maxreqs;
|
||||
u32 nr_rdma_attrs;
|
||||
u32 rdma_attrs;
|
||||
};
|
||||
|
||||
struct nfsd4_session {
|
||||
struct kref se_ref;
|
||||
struct list_head se_hash; /* hash by sessionid */
|
||||
|
@ -128,11 +133,8 @@ struct nfsd4_session {
|
|||
u32 se_flags;
|
||||
struct nfs4_client *se_client; /* for expire_client */
|
||||
struct nfs4_sessionid se_sessionid;
|
||||
u32 se_fmaxreq_sz;
|
||||
u32 se_fmaxresp_sz;
|
||||
u32 se_fmaxresp_cached;
|
||||
u32 se_fmaxops;
|
||||
u32 se_fnumslots;
|
||||
struct nfsd4_channel_attrs se_fchannel;
|
||||
struct nfsd4_channel_attrs se_bchannel;
|
||||
struct nfsd4_slot se_slots[]; /* forward channel slots */
|
||||
};
|
||||
|
||||
|
@ -184,7 +186,7 @@ struct nfs4_client {
|
|||
struct svc_cred cl_cred; /* setclientid principal */
|
||||
clientid_t cl_clientid; /* generated by server */
|
||||
nfs4_verifier cl_confirm; /* generated by server */
|
||||
struct nfs4_callback cl_callback; /* callback info */
|
||||
struct nfs4_cb_conn cl_cb_conn; /* callback info */
|
||||
atomic_t cl_count; /* ref count */
|
||||
u32 cl_firststate; /* recovery dir creation */
|
||||
|
||||
|
|
|
@ -64,10 +64,13 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
|
|||
|
||||
struct nfsd4_change_info {
|
||||
u32 atomic;
|
||||
bool change_supported;
|
||||
u32 before_ctime_sec;
|
||||
u32 before_ctime_nsec;
|
||||
u64 before_change;
|
||||
u32 after_ctime_sec;
|
||||
u32 after_ctime_nsec;
|
||||
u64 after_change;
|
||||
};
|
||||
|
||||
struct nfsd4_access {
|
||||
|
@ -363,17 +366,6 @@ struct nfsd4_exchange_id {
|
|||
int spa_how;
|
||||
};
|
||||
|
||||
struct nfsd4_channel_attrs {
|
||||
u32 headerpadsz;
|
||||
u32 maxreq_sz;
|
||||
u32 maxresp_sz;
|
||||
u32 maxresp_cached;
|
||||
u32 maxops;
|
||||
u32 maxreqs;
|
||||
u32 nr_rdma_attrs;
|
||||
u32 rdma_attrs;
|
||||
};
|
||||
|
||||
struct nfsd4_create_session {
|
||||
clientid_t clientid;
|
||||
struct nfs4_sessionid sessionid;
|
||||
|
@ -503,10 +495,16 @@ set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp)
|
|||
{
|
||||
BUG_ON(!fhp->fh_pre_saved || !fhp->fh_post_saved);
|
||||
cinfo->atomic = 1;
|
||||
cinfo->change_supported = IS_I_VERSION(fhp->fh_dentry->d_inode);
|
||||
if (cinfo->change_supported) {
|
||||
cinfo->before_change = fhp->fh_pre_change;
|
||||
cinfo->after_change = fhp->fh_post_change;
|
||||
} else {
|
||||
cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec;
|
||||
cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec;
|
||||
cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec;
|
||||
cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec;
|
||||
}
|
||||
}
|
||||
|
||||
int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *, void *);
|
||||
|
|
|
@ -83,7 +83,7 @@ int svc_port_is_privileged(struct sockaddr *sin);
|
|||
int svc_print_xprts(char *buf, int maxlen);
|
||||
struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
|
||||
const sa_family_t af, const unsigned short port);
|
||||
int svc_xprt_names(struct svc_serv *serv, char *buf, int buflen);
|
||||
int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen);
|
||||
|
||||
static inline void svc_xprt_get(struct svc_xprt *xprt)
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ static inline unsigned short svc_addr_port(const struct sockaddr *sa)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t svc_addr_len(struct sockaddr *sa)
|
||||
static inline size_t svc_addr_len(const struct sockaddr *sa)
|
||||
{
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
|
@ -126,7 +126,8 @@ static inline size_t svc_addr_len(struct sockaddr *sa)
|
|||
case AF_INET6:
|
||||
return sizeof(struct sockaddr_in6);
|
||||
}
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned short svc_xprt_local_port(const struct svc_xprt *xprt)
|
||||
|
|
|
@ -38,8 +38,11 @@ int svc_recv(struct svc_rqst *, long);
|
|||
int svc_send(struct svc_rqst *);
|
||||
void svc_drop(struct svc_rqst *);
|
||||
void svc_sock_update_bufs(struct svc_serv *serv);
|
||||
int svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
|
||||
int svc_addsock(struct svc_serv *serv, int fd, char *name_return);
|
||||
int svc_sock_names(struct svc_serv *serv, char *buf,
|
||||
const size_t buflen,
|
||||
const char *toclose);
|
||||
int svc_addsock(struct svc_serv *serv, const int fd,
|
||||
char *name_return, const size_t len);
|
||||
void svc_init_xprt_sock(void);
|
||||
void svc_cleanup_xprt_sock(void);
|
||||
struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot);
|
||||
|
|
|
@ -488,7 +488,7 @@ static void do_cache_clean(struct work_struct *work)
|
|||
{
|
||||
int delay = 5;
|
||||
if (cache_clean() == -1)
|
||||
delay = 30*HZ;
|
||||
delay = round_jiffies_relative(30*HZ);
|
||||
|
||||
if (list_empty(&cache_list))
|
||||
delay = 0;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <net/sock.h>
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/sunrpc/svc_xprt.h>
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
|
||||
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
|
||||
|
||||
|
@ -1097,36 +1098,58 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(svc_find_xprt);
|
||||
|
||||
/*
|
||||
* Format a buffer with a list of the active transports. A zero for
|
||||
* the buflen parameter disables target buffer overflow checking.
|
||||
static int svc_one_xprt_name(const struct svc_xprt *xprt,
|
||||
char *pos, int remaining)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = snprintf(pos, remaining, "%s %u\n",
|
||||
xprt->xpt_class->xcl_name,
|
||||
svc_xprt_local_port(xprt));
|
||||
if (len >= remaining)
|
||||
return -ENAMETOOLONG;
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_xprt_names - format a buffer with a list of transport names
|
||||
* @serv: pointer to an RPC service
|
||||
* @buf: pointer to a buffer to be filled in
|
||||
* @buflen: length of buffer to be filled in
|
||||
*
|
||||
* Fills in @buf with a string containing a list of transport names,
|
||||
* each name terminated with '\n'.
|
||||
*
|
||||
* Returns positive length of the filled-in string on success; otherwise
|
||||
* a negative errno value is returned if an error occurs.
|
||||
*/
|
||||
int svc_xprt_names(struct svc_serv *serv, char *buf, int buflen)
|
||||
int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen)
|
||||
{
|
||||
struct svc_xprt *xprt;
|
||||
char xprt_str[64];
|
||||
int totlen = 0;
|
||||
int len;
|
||||
int len, totlen;
|
||||
char *pos;
|
||||
|
||||
/* Sanity check args */
|
||||
if (!serv)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
|
||||
pos = buf;
|
||||
totlen = 0;
|
||||
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
|
||||
len = snprintf(xprt_str, sizeof(xprt_str),
|
||||
"%s %d\n", xprt->xpt_class->xcl_name,
|
||||
svc_xprt_local_port(xprt));
|
||||
/* If the string was truncated, replace with error string */
|
||||
if (len >= sizeof(xprt_str))
|
||||
strcpy(xprt_str, "name-too-long\n");
|
||||
/* Don't overflow buffer */
|
||||
len = strlen(xprt_str);
|
||||
if (buflen && (len + totlen >= buflen))
|
||||
len = svc_one_xprt_name(xprt, pos, buflen - totlen);
|
||||
if (len < 0) {
|
||||
*buf = '\0';
|
||||
totlen = len;
|
||||
}
|
||||
if (len <= 0)
|
||||
break;
|
||||
strcpy(buf+totlen, xprt_str);
|
||||
|
||||
pos += len;
|
||||
totlen += len;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
return totlen;
|
||||
}
|
||||
|
|
|
@ -240,42 +240,76 @@ out:
|
|||
/*
|
||||
* Report socket names for nfsdfs
|
||||
*/
|
||||
static int one_sock_name(char *buf, struct svc_sock *svsk)
|
||||
static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
|
||||
{
|
||||
const struct sock *sk = svsk->sk_sk;
|
||||
const char *proto_name = sk->sk_protocol == IPPROTO_UDP ?
|
||||
"udp" : "tcp";
|
||||
int len;
|
||||
|
||||
switch(svsk->sk_sk->sk_family) {
|
||||
case AF_INET:
|
||||
len = sprintf(buf, "ipv4 %s %pI4 %d\n",
|
||||
svsk->sk_sk->sk_protocol == IPPROTO_UDP ?
|
||||
"udp" : "tcp",
|
||||
&inet_sk(svsk->sk_sk)->rcv_saddr,
|
||||
inet_sk(svsk->sk_sk)->num);
|
||||
switch (sk->sk_family) {
|
||||
case PF_INET:
|
||||
len = snprintf(buf, remaining, "ipv4 %s %pI4 %d\n",
|
||||
proto_name,
|
||||
&inet_sk(sk)->rcv_saddr,
|
||||
inet_sk(sk)->num);
|
||||
break;
|
||||
case PF_INET6:
|
||||
len = snprintf(buf, remaining, "ipv6 %s %pI6 %d\n",
|
||||
proto_name,
|
||||
&inet6_sk(sk)->rcv_saddr,
|
||||
inet_sk(sk)->num);
|
||||
break;
|
||||
default:
|
||||
len = sprintf(buf, "*unknown-%d*\n",
|
||||
svsk->sk_sk->sk_family);
|
||||
len = snprintf(buf, remaining, "*unknown-%d*\n",
|
||||
sk->sk_family);
|
||||
}
|
||||
|
||||
if (len >= remaining) {
|
||||
*buf = '\0';
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
|
||||
/**
|
||||
* svc_sock_names - construct a list of listener names in a string
|
||||
* @serv: pointer to RPC service
|
||||
* @buf: pointer to a buffer to fill in with socket names
|
||||
* @buflen: size of the buffer to be filled
|
||||
* @toclose: pointer to '\0'-terminated C string containing the name
|
||||
* of a listener to be closed
|
||||
*
|
||||
* Fills in @buf with a '\n'-separated list of names of listener
|
||||
* sockets. If @toclose is not NULL, the socket named by @toclose
|
||||
* is closed, and is not included in the output list.
|
||||
*
|
||||
* Returns positive length of the socket name string, or a negative
|
||||
* errno value on error.
|
||||
*/
|
||||
int svc_sock_names(struct svc_serv *serv, char *buf, const size_t buflen,
|
||||
const char *toclose)
|
||||
{
|
||||
struct svc_sock *svsk, *closesk = NULL;
|
||||
int len = 0;
|
||||
|
||||
if (!serv)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list) {
|
||||
int onelen = one_sock_name(buf+len, svsk);
|
||||
if (toclose && strcmp(toclose, buf+len) == 0)
|
||||
int onelen = svc_one_sock_name(svsk, buf + len, buflen - len);
|
||||
if (onelen < 0) {
|
||||
len = onelen;
|
||||
break;
|
||||
}
|
||||
if (toclose && strcmp(toclose, buf + len) == 0)
|
||||
closesk = svsk;
|
||||
else
|
||||
len += onelen;
|
||||
}
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
|
||||
if (closesk)
|
||||
/* Should unregister with portmap, but you cannot
|
||||
* unregister just one protocol...
|
||||
|
@ -346,6 +380,7 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd,
|
|||
sock->sk->sk_sndbuf = snd * 2;
|
||||
sock->sk->sk_rcvbuf = rcv * 2;
|
||||
sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK;
|
||||
sock->sk->sk_write_space(sock->sk);
|
||||
release_sock(sock->sk);
|
||||
#endif
|
||||
}
|
||||
|
@ -387,6 +422,15 @@ static void svc_write_space(struct sock *sk)
|
|||
}
|
||||
}
|
||||
|
||||
static void svc_tcp_write_space(struct sock *sk)
|
||||
{
|
||||
struct socket *sock = sk->sk_socket;
|
||||
|
||||
if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && sock)
|
||||
clear_bit(SOCK_NOSPACE, &sock->flags);
|
||||
svc_write_space(sk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the UDP datagram's destination address to the rqstp structure.
|
||||
* The 'destination' address in this case is the address to which the
|
||||
|
@ -427,13 +471,14 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
|
|||
long all[SVC_PKTINFO_SPACE / sizeof(long)];
|
||||
} buffer;
|
||||
struct cmsghdr *cmh = &buffer.hdr;
|
||||
int err, len;
|
||||
struct msghdr msg = {
|
||||
.msg_name = svc_addr(rqstp),
|
||||
.msg_control = cmh,
|
||||
.msg_controllen = sizeof(buffer),
|
||||
.msg_flags = MSG_DONTWAIT,
|
||||
};
|
||||
size_t len;
|
||||
int err;
|
||||
|
||||
if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags))
|
||||
/* udp sockets need large rcvbuf as all pending
|
||||
|
@ -465,8 +510,8 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
|
|||
return -EAGAIN;
|
||||
}
|
||||
len = svc_addr_len(svc_addr(rqstp));
|
||||
if (len < 0)
|
||||
return len;
|
||||
if (len == 0)
|
||||
return -EAFNOSUPPORT;
|
||||
rqstp->rq_addrlen = len;
|
||||
if (skb->tstamp.tv64 == 0) {
|
||||
skb->tstamp = ktime_get_real();
|
||||
|
@ -982,23 +1027,14 @@ static int svc_tcp_has_wspace(struct svc_xprt *xprt)
|
|||
struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
|
||||
struct svc_serv *serv = svsk->sk_xprt.xpt_server;
|
||||
int required;
|
||||
int wspace;
|
||||
|
||||
/*
|
||||
* Set the SOCK_NOSPACE flag before checking the available
|
||||
* sock space.
|
||||
*/
|
||||
set_bit(SOCK_NOSPACE, &svsk->sk_sock->flags);
|
||||
required = atomic_read(&svsk->sk_xprt.xpt_reserved) + serv->sv_max_mesg;
|
||||
wspace = sk_stream_wspace(svsk->sk_sk);
|
||||
|
||||
if (wspace < sk_stream_min_wspace(svsk->sk_sk))
|
||||
return 0;
|
||||
if (required * 2 > wspace)
|
||||
return 0;
|
||||
|
||||
clear_bit(SOCK_NOSPACE, &svsk->sk_sock->flags);
|
||||
if (test_bit(XPT_LISTENER, &xprt->xpt_flags))
|
||||
return 1;
|
||||
required = atomic_read(&xprt->xpt_reserved) + serv->sv_max_mesg;
|
||||
if (sk_stream_wspace(svsk->sk_sk) >= required)
|
||||
return 1;
|
||||
set_bit(SOCK_NOSPACE, &svsk->sk_sock->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct svc_xprt *svc_tcp_create(struct svc_serv *serv,
|
||||
|
@ -1054,7 +1090,7 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv)
|
|||
dprintk("setting up TCP socket for reading\n");
|
||||
sk->sk_state_change = svc_tcp_state_change;
|
||||
sk->sk_data_ready = svc_tcp_data_ready;
|
||||
sk->sk_write_space = svc_write_space;
|
||||
sk->sk_write_space = svc_tcp_write_space;
|
||||
|
||||
svsk->sk_reclen = 0;
|
||||
svsk->sk_tcplen = 0;
|
||||
|
@ -1148,9 +1184,19 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
|
|||
return svsk;
|
||||
}
|
||||
|
||||
int svc_addsock(struct svc_serv *serv,
|
||||
int fd,
|
||||
char *name_return)
|
||||
/**
|
||||
* svc_addsock - add a listener socket to an RPC service
|
||||
* @serv: pointer to RPC service to which to add a new listener
|
||||
* @fd: file descriptor of the new listener
|
||||
* @name_return: pointer to buffer to fill in with name of listener
|
||||
* @len: size of the buffer
|
||||
*
|
||||
* Fills in socket name and returns positive length of name if successful.
|
||||
* Name is terminated with '\n'. On error, returns a negative errno
|
||||
* value.
|
||||
*/
|
||||
int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
|
||||
const size_t len)
|
||||
{
|
||||
int err = 0;
|
||||
struct socket *so = sockfd_lookup(fd, &err);
|
||||
|
@ -1190,7 +1236,7 @@ int svc_addsock(struct svc_serv *serv,
|
|||
sockfd_put(so);
|
||||
return err;
|
||||
}
|
||||
return one_sock_name(name_return, svsk);
|
||||
return svc_one_sock_name(svsk, name_return, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_addsock);
|
||||
|
||||
|
|
|
@ -397,14 +397,14 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
|
|||
if (!ch)
|
||||
return 0;
|
||||
|
||||
/* Allocate temporary reply and chunk maps */
|
||||
rpl_map = svc_rdma_get_req_map();
|
||||
chl_map = svc_rdma_get_req_map();
|
||||
|
||||
svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count);
|
||||
if (ch_count > RPCSVC_MAXPAGES)
|
||||
return -EINVAL;
|
||||
|
||||
/* Allocate temporary reply and chunk maps */
|
||||
rpl_map = svc_rdma_get_req_map();
|
||||
chl_map = svc_rdma_get_req_map();
|
||||
|
||||
if (!xprt->sc_frmr_pg_list_len)
|
||||
sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp,
|
||||
rpl_map, chl_map, ch_count,
|
||||
|
|
Загрузка…
Ссылка в новой задаче