Commit 7e0338c0 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents df36b439 47fcb03f
...@@ -66,6 +66,10 @@ mandatory-locking.txt ...@@ -66,6 +66,10 @@ mandatory-locking.txt
- info on the Linux implementation of Sys V mandatory file locking. - info on the Linux implementation of Sys V mandatory file locking.
ncpfs.txt ncpfs.txt
- info on Novell Netware(tm) filesystem using NCP protocol. - 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 nfsroot.txt
- short guide on setting up a diskless box with NFS root filesystem. - short guide on setting up a diskless box with NFS root filesystem.
nilfs2.txt nilfs2.txt
......
...@@ -236,10 +236,12 @@ source "fs/nfsd/Kconfig" ...@@ -236,10 +236,12 @@ source "fs/nfsd/Kconfig"
config LOCKD config LOCKD
tristate tristate
depends on FILE_LOCKING
config LOCKD_V4 config LOCKD_V4
bool bool
depends on NFSD_V3 || NFS_V3 depends on NFSD_V3 || NFS_V3
depends on FILE_LOCKING
default y default y
config EXPORTFS config EXPORTFS
......
...@@ -326,6 +326,8 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call) ...@@ -326,6 +326,8 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call)
{ {
if (call->a_args.lock.oh.data != call->a_owner) if (call->a_args.lock.oh.data != call->a_owner)
kfree(call->a_args.lock.oh.data); 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) ...@@ -151,7 +151,7 @@ static struct file_lock *locks_alloc_lock(void)
return kmem_cache_alloc(filelock_cache, GFP_KERNEL); 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) {
if (fl->fl_ops->fl_release_private) if (fl->fl_ops->fl_release_private)
...@@ -165,6 +165,7 @@ static void locks_release_private(struct file_lock *fl) ...@@ -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. */ /* Free a lock which is not in use. */
static void locks_free_lock(struct file_lock *fl) static void locks_free_lock(struct file_lock *fl)
......
config NFS_FS config NFS_FS
tristate "NFS client support" tristate "NFS client support"
depends on INET depends on INET && FILE_LOCKING
select LOCKD select LOCKD
select SUNRPC select SUNRPC
select NFS_ACL_SUPPORT if NFS_V3_ACL select NFS_ACL_SUPPORT if NFS_V3_ACL
......
...@@ -464,16 +464,11 @@ static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp) ...@@ -464,16 +464,11 @@ static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
if (err) if (err)
return err; return err;
/* /*
* Just a quick sanity check; we could also try to check * XXX: It would be nice to also check whether this
* whether this pseudoflavor is supported, but at worst * pseudoflavor is supported, so we can discover the
* an unsupported pseudoflavor on the export would just * problem at export time instead of when a client fails
* be a pseudoflavor that won't match the flavor of any * to authenticate.
* authenticated request. The administrator will
* probably discover the problem when someone fails to
* authenticate.
*/ */
if (f->pseudoflavor < 0)
return -EINVAL;
err = get_int(mesg, &f->flags); err = get_int(mesg, &f->flags);
if (err) if (err)
return err; return err;
......
This diff is collapsed.
...@@ -272,6 +272,7 @@ void fill_post_wcc(struct svc_fh *fhp) ...@@ -272,6 +272,7 @@ void fill_post_wcc(struct svc_fh *fhp)
err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry, err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry,
&fhp->fh_post_attr); &fhp->fh_post_attr);
fhp->fh_post_change = fhp->fh_dentry->d_inode->i_version;
if (err) if (err)
fhp->fh_post_saved = 0; fhp->fh_post_saved = 0;
else else
......
...@@ -140,8 +140,10 @@ struct nfs4_cb_compound_hdr { ...@@ -140,8 +140,10 @@ struct nfs4_cb_compound_hdr {
int status; int status;
u32 ident; u32 ident;
u32 nops; u32 nops;
__be32 *nops_p;
u32 minorversion;
u32 taglen; u32 taglen;
char * tag; char *tag;
}; };
static struct { static struct {
...@@ -201,33 +203,39 @@ nfs_cb_stat_to_errno(int stat) ...@@ -201,33 +203,39 @@ nfs_cb_stat_to_errno(int stat)
* XDR encode * XDR encode
*/ */
static int static void
encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr) encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
{ {
__be32 * p; __be32 * p;
RESERVE_SPACE(16); RESERVE_SPACE(16);
WRITE32(0); /* tag length is always 0 */ WRITE32(0); /* tag length is always 0 */
WRITE32(NFS4_MINOR_VERSION); WRITE32(hdr->minorversion);
WRITE32(hdr->ident); WRITE32(hdr->ident);
hdr->nops_p = p;
WRITE32(hdr->nops); WRITE32(hdr->nops);
return 0;
} }
static int static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec) {
*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; __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(OP_CB_RECALL);
WRITE32(cb_rec->cbr_stateid.si_generation); WRITE32(dp->dl_stateid.si_generation);
WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t)); WRITEMEM(&dp->dl_stateid.si_opaque, sizeof(stateid_opaque_t));
WRITE32(cb_rec->cbr_trunc); WRITE32(0); /* truncate optimization not implemented */
WRITE32(len); WRITE32(len);
WRITEMEM(&cb_rec->cbr_fh.fh_base, len); WRITEMEM(&dp->dl_fh.fh_base, len);
return 0; hdr->nops++;
} }
static int static int
...@@ -241,17 +249,18 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p) ...@@ -241,17 +249,18 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
} }
static int 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 xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr = { struct nfs4_cb_compound_hdr hdr = {
.ident = args->cbr_ident, .ident = args->dl_ident,
.nops = 1,
}; };
xdr_init_encode(&xdr, &req->rq_snd_buf, p); xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_cb_compound_hdr(&xdr, &hdr); 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 = { ...@@ -358,18 +367,21 @@ static struct rpc_program cb_program = {
.pipe_dir_name = "/nfsd4_cb", .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. /* Reference counting, callback cleanup, etc., all look racy as heck.
* And why is cb_set an atomic? */ * 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 sockaddr_in addr;
struct nfs4_callback *cb = &clp->cl_callback; struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
struct rpc_timeout timeparms = { struct rpc_timeout timeparms = {
.to_initval = (NFSD_LEASE_TIME/4) * HZ, .to_initval = max_cb_time(),
.to_retries = 5, .to_retries = 0,
.to_maxval = (NFSD_LEASE_TIME/2) * HZ,
.to_exponential = 1,
}; };
struct rpc_create_args args = { struct rpc_create_args args = {
.protocol = IPPROTO_TCP, .protocol = IPPROTO_TCP,
...@@ -386,7 +398,7 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp) ...@@ -386,7 +398,7 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
struct rpc_clnt *client; struct rpc_clnt *client;
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
return ERR_PTR(-EINVAL); return -EINVAL;
/* Initialize address */ /* Initialize address */
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
...@@ -396,48 +408,77 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp) ...@@ -396,48 +408,77 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
/* Create RPC client */ /* Create RPC client */
client = rpc_create(&args); client = rpc_create(&args);
if (IS_ERR(client)) if (IS_ERR(client)) {
dprintk("NFSD: couldn't create callback client: %ld\n", dprintk("NFSD: couldn't create callback client: %ld\n",
PTR_ERR(client)); PTR_ERR(client));
return client; return PTR_ERR(client);
}
cb->cb_client = client;
return 0;
}
static void warn_no_callback_path(struct nfs4_client *clp, int reason)
{
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);
} }
static int do_probe_callback(void *data) void do_probe_callback(struct nfs4_client *clp)
{ {
struct nfs4_client *clp = data; struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
struct nfs4_callback *cb = &clp->cl_callback;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
.rpc_argp = clp, .rpc_argp = clp,
}; };
struct rpc_clnt *client; struct rpc_cred *cred;
int status; int status;
client = setup_callback_client(clp); cred = lookup_cb_cred(cb);
if (IS_ERR(client)) { if (IS_ERR(cred)) {
status = PTR_ERR(client); status = PTR_ERR(cred);
dprintk("NFSD: couldn't create callback client: %d\n", goto out;
status); }
goto out_err; 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);
} }
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);
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 @@ static int do_probe_callback(void *data) ...@@ -446,21 +487,65 @@ static int do_probe_callback(void *data)
void void
nfsd4_probe_callback(struct nfs4_client *clp) 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 */ /* the task holds a reference to the nfs4_client struct */
atomic_inc(&clp->cl_count); atomic_inc(&clp->cl_count);
t = kthread_run(do_probe_callback, clp, "nfs4_cb_probe"); do_probe_callback(clp);
}
if (IS_ERR(t)) static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
atomic_dec(&clp->cl_count); {
struct nfs4_delegation *dp = calldata;
struct nfs4_client *clp = dp->dl_client;
return; 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. * called with dp->dl_count inc'ed.
*/ */
...@@ -468,41 +553,19 @@ void ...@@ -468,41 +553,19 @@ void
nfsd4_cb_recall(struct nfs4_delegation *dp) nfsd4_cb_recall(struct nfs4_delegation *dp)
{ {
struct nfs4_client *clp = dp->dl_client; struct nfs4_client *clp = dp->dl_client;
struct rpc_clnt *clnt = clp->cl_callback.cb_client; struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
struct nfs4_cb_recall *cbr = &dp->dl_recall;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], .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;
int status = 0;
dp->dl_retries = 1;
cbr->cbr_trunc = 0; /* XXX need to implement truncate optimization */ status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
cbr->cbr_dp = dp; &nfsd4_cb_recall_ops, dp);
if (status) {
status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT); put_nfs4_client(clp);
while (retries--) { nfs4_put_delegation(dp);
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.
*/
put_nfs4_client(clp);
nfs4_put_delegation(dp);
return;
} }
...@@ -51,6 +51,78 @@ ...@@ -51,6 +51,78 @@
#define NFSDDBG_FACILITY NFSDDBG_PROC #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 static inline void
fh_dup2(struct svc_fh *dst, struct svc_fh *src) 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, ...@@ -225,6 +297,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
goto out; 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 /* Openowner is now set, so sequence id will get bumped. Now we need
* these checks before we do any creates: */ * these checks before we do any creates: */
status = nfserr_grace; status = nfserr_grace;
...@@ -395,6 +471,11 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -395,6 +471,11 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
return status; return status;
status = check_attr_support(rqstp, cstate, create->cr_bmval,
nfsd_attrmask);
if (status)
return status;
switch (create->cr_type) { switch (create->cr_type) {
case NF4LNK: case NF4LNK:
/* ugh! we have to null-terminate the linktext, or /* ugh! we have to null-terminate the linktext, or
...@@ -689,6 +770,12 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -689,6 +770,12 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
return status; return status;
status = nfs_ok; status = nfs_ok;
status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
nfsd_attrmask);
if (status)
goto out;
if (setattr->sa_acl != NULL) if (setattr->sa_acl != NULL)
status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh, status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
setattr->sa_acl); setattr->sa_acl);
...@@ -763,10 +850,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -763,10 +850,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
return status; return status;
if ((verify->ve_bmval[0] & ~nfsd_suppattrs0(cstate->minorversion)) status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL);
|| (verify->ve_bmval[1] & ~nfsd_suppattrs1(cstate->minorversion)) if (status)
|| (verify->ve_bmval[2] & ~nfsd_suppattrs2(cstate->minorversion))) return status;
return nfserr_attrnotsupp;
if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR) if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
|| (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
return nfserr_inval; return nfserr_inval;
...@@ -1226,24 +1313,9 @@ static const char *nfsd4_op_name(unsigned opnum) ...@@ -1226,24 +1313,9 @@ static const char *nfsd4_op_name(unsigned opnum)
return "unknown_operation"; return "unknown_operation";
} }
#define nfs4svc_decode_voidargs NULL
#define nfs4svc_release_void NULL
#define nfsd4_voidres nfsd4_voidargs #define nfsd4_voidres nfsd4_voidargs
#define nfs4svc_release_compound NULL
struct nfsd4_voidargs { int dummy; }; 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 * TODO: At the present time, the NFSv4 server does not do XID caching
* of requests. Implementing XID caching would not be a serious problem, * of requests. Implementing XID caching would not be a serious problem,
...@@ -1255,8 +1327,23 @@ struct nfsd4_voidargs { int dummy; }; ...@@ -1255,8 +1327,23 @@ struct nfsd4_voidargs { int dummy; };
* better XID's. * better XID's.
*/ */
static struct svc_procedure nfsd_procedures4[2] = { static struct svc_procedure nfsd_procedures4[2] = {
PROC(null, void, void, void, RC_NOCACHE, 1), [NFSPROC4_NULL] = {
PROC(compound, compound, compound, compound, RC_NOCACHE, NFSD_BUFSIZE/4) .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 = { struct svc_version nfsd_version4 = {
......
This diff is collapsed.
This diff is collapsed.
...@@ -29,15 +29,24 @@ ...@@ -29,15 +29,24 @@
*/ */
#define CACHESIZE 1024 #define CACHESIZE 1024
#define HASHSIZE 64 #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 struct list_head lru_head;
static int cache_disabled = 1; 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); static int nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
/* /*
* locking for the reply cache: * locking for the reply cache:
* A cache entry is "single use" if c_state == RC_INPROG * A cache entry is "single use" if c_state == RC_INPROG
* Otherwise, it when accessing _prev or _next, the lock must be held. * Otherwise, it when accessing _prev or _next, the lock must be held.
...@@ -62,8 +71,8 @@ int nfsd_reply_cache_init(void) ...@@ -62,8 +71,8 @@ int nfsd_reply_cache_init(void)
i--; i--;
} }
hash_list = kcalloc (HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL); cache_hash = kcalloc (HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL);
if (!hash_list) if (!cache_hash)
goto out_nomem; goto out_nomem;
cache_disabled = 0; cache_disabled = 0;
...@@ -88,8 +97,8 @@ void nfsd_reply_cache_shutdown(void) ...@@ -88,8 +97,8 @@ void nfsd_reply_cache_shutdown(void)
cache_disabled = 1; cache_disabled = 1;
kfree (hash_list); kfree (cache_hash);
hash_list = NULL; cache_hash = NULL;
} }
/* /*
...@@ -108,7 +117,7 @@ static void ...@@ -108,7 +117,7 @@ static void
hash_refile(struct svc_cacherep *rp) hash_refile(struct svc_cacherep *rp)
{ {
hlist_del_init(&rp->c_hash); 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) ...@@ -138,7 +147,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
spin_lock(&cache_lock); spin_lock(&cache_lock);
rtn = RC_DOIT; rtn = RC_DOIT;
rh = &hash_list[REQHASH(xid)]; rh = &cache_hash[request_hash(xid)];
hlist_for_each_entry(rp, hn, rh, c_hash) { hlist_for_each_entry(rp, hn, rh, c_hash) {
if (rp->c_state != RC_UNUSED && if (rp->c_state != RC_UNUSED &&
xid == rp->c_xid && proc == rp->c_proc && xid == rp->c_xid && proc == rp->c_proc &&
...@@ -165,8 +174,8 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type) ...@@ -165,8 +174,8 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
} }
} }
/* This should not happen */ /* All entries on the LRU are in-progress. This should not happen */
if (rp == NULL) { if (&rp->c_lru == &lru_head) {
static int complaints; static int complaints;
printk(KERN_WARNING "nfsd: all repcache entries locked!\n"); printk(KERN_WARNING "nfsd: all repcache entries locked!\n");
...@@ -264,7 +273,7 @@ nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp) ...@@ -264,7 +273,7 @@ nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
len = resv->iov_len - ((char*)statp - (char*)resv->iov_base); len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
len >>= 2; len >>= 2;
/* Don't cache excessive amounts of data and XDR failures */ /* Don't cache excessive amounts of data and XDR failures */
if (!statp || len > (256 >> 2)) { if (!statp || len > (256 >> 2)) {
rp->c_state = RC_UNUSED; rp->c_state = RC_UNUSED;
......
...@@ -207,10 +207,14 @@ static struct file_operations pool_stats_operations = { ...@@ -207,10 +207,14 @@ static struct file_operations pool_stats_operations = {
static ssize_t write_svc(struct file *file, char *buf, size_t size) static ssize_t write_svc(struct file *file, char *buf, size_t size)
{ {
struct nfsctl_svc *data; struct nfsctl_svc *data;
int err;
if (size < sizeof(*data)) if (size < sizeof(*data))
return -EINVAL; return -EINVAL;
data = (struct nfsctl_svc*) buf; 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) ...@@ -692,11 +696,12 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
if (newthreads < 0) if (newthreads < 0)
return -EINVAL; return -EINVAL;
rv = nfsd_svc(NFS_PORT, newthreads); rv = nfsd_svc(NFS_PORT, newthreads);
if (rv) if (rv < 0)
return rv; return rv;
} } else
sprintf(buf, "%d\n", nfsd_nrthreads()); rv = nfsd_nrthreads();
return strlen(buf);
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) ...@@ -793,7 +798,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
{ {
char *mesg = buf; char *mesg = buf;
char *vers, *minorp, sign; char *vers, *minorp, sign;
int len, num; int len, num, remaining;
unsigned minor; unsigned minor;
ssize_t tlen = 0; ssize_t tlen = 0;
char *sep; char *sep;
...@@ -840,32 +845,50 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -840,32 +845,50 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
} }
next: next:
vers += len + 1; vers += len + 1;
tlen += len;
} while ((len = qword_get(&mesg, vers, size)) > 0); } while ((len = qword_get(&mesg, vers, size)) > 0);
/* If all get turned off, turn them back on, as /* If all get turned off, turn them back on, as
* having no versions is BAD * having no versions is BAD
*/ */
nfsd_reset_versions(); nfsd_reset_versions();
} }
/* Now write current state into reply buffer */ /* Now write current state into reply buffer */
len = 0; len = 0;
sep = ""; sep = "";
remaining = SIMPLE_TRANSACTION_LIMIT;
for (num=2 ; num <= 4 ; num++) for (num=2 ; num <= 4 ; num++)
if (nfsd_vers(num, NFSD_AVAIL)) { 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)?'+':'-', nfsd_vers(num, NFSD_TEST)?'+':'-',
num); num);
sep = " "; sep = " ";
if (len > remaining)
break;
remaining -= len;
buf += len;
tlen += len;
} }
if (nfsd_vers(4, NFSD_AVAIL)) if (nfsd_vers(4, NFSD_AVAIL))
for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION; minor++) for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
len += sprintf(buf+len, " %c4.%u", minor++) {
len = snprintf(buf, remaining, " %c4.%u",
(nfsd_vers(4, NFSD_TEST) && (nfsd_vers(4, NFSD_TEST) &&
nfsd_minorversion(minor, NFSD_TEST)) ? nfsd_minorversion(minor, NFSD_TEST)) ?
'+' : '-', '+' : '-',
minor); 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) ...@@ -910,104 +933,143 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
return rv; 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)
int len = 0; return 0;
return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
}
if (nfsd_serv) /*
len = svc_xprt_names(nfsd_serv, buf, 0); * A single 'fd' number was written, in which case it must be for
return len; * a socket of a supported family/protocol, and we use it as an
} * nfsd listener.
/* Either a single 'fd' number is written, in which */
* case it must be for a socket of a supported family/protocol, static ssize_t __write_ports_addfd(char *buf)
* and we use it as an nfsd socket, or {
* A '-' followed by the 'name' of a socket in which case char *mesg = buf;
* we close the socket. int fd, err;
*/
if (isdigit(buf[0])) { err = get_int(&mesg, &fd);
char *mesg = buf; if (err != 0 || fd < 0)
int fd; return -EINVAL;
int err;
err = get_int(&mesg, &fd); err = nfsd_create_serv();
if (err) if (err != 0)
return -EINVAL; return err;
if (fd < 0)
return -EINVAL; err = lockd_up();
err = nfsd_create_serv(); if (err != 0)
if (!err) { goto out;
err = svc_addsock(nfsd_serv, fd, buf);
if (err >= 0) { err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
err = lockd_up(); if (err < 0)
if (err < 0) lockd_down();
svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
} out:
/* Decrease the count, but don't shutdown the /* Decrease the count, but don't shut down the service */
* the service nfsd_serv->sv_nrthreads--;
*/ return err;
nfsd_serv->sv_nrthreads--; }
}
return err < 0 ? err : 0; /*
} * A '-' followed by the 'name' of a socket means we close the socket.
if (buf[0] == '-' && isdigit(buf[1])) { */
char *toclose = kstrdup(buf+1, GFP_KERNEL); static ssize_t __write_ports_delfd(char *buf)
int len = 0; {
if (!toclose) char *toclose;
return -ENOMEM; int len = 0;
if (nfsd_serv)
len = svc_sock_names(buf, nfsd_serv, toclose); toclose = kstrdup(buf + 1, GFP_KERNEL);
if (len >= 0) if (toclose == NULL)
lockd_down(); return -ENOMEM;
kfree(toclose);
return len; if (nfsd_serv != NULL)
} len = svc_sock_names(nfsd_serv, buf,
/* SIMPLE_TRANSACTION_LIMIT, toclose);
* Add a transport listener by writing it's transport name if (len >= 0)
*/ lockd_down();
if (isalpha(buf[0])) {
int err; kfree(toclose);
char transport[16]; return len;
int port; }
if (sscanf(buf, "%15s %4d", transport, &port) == 2) {
if (port < 1 || port > 65535) /*
return -EINVAL; * A transport listener is added by writing it's transport name and
err = nfsd_create_serv(); * a port number.
if (!err) { */
err = svc_create_xprt(nfsd_serv, static ssize_t __write_ports_addxprt(char *buf)
transport, PF_INET, port, {
SVC_SOCK_ANONYMOUS); char transport[16];
if (err == -ENOENT) int port, err;
/* Give a reasonable perror msg for
* bad transport string */ if (sscanf(buf, "%15s %4u", transport, &port) != 2)
err = -EPROTONOSUPPORT; return -EINVAL;
}
return err < 0 ? err : 0; if (port < 1 || port > USHORT_MAX)
} return -EINVAL;
}
/* err = nfsd_create_serv();
* Remove a transport by writing it's transport name and port number if (err != 0)
*/ return err;
if (buf[0] == '-' && isalpha(buf[1])) {
struct svc_xprt *xprt; err = svc_create_xprt(nfsd_serv, transport,
int err = -EINVAL; PF_INET, port, SVC_SOCK_ANONYMOUS);
char transport[16]; if (err < 0) {
int port; /* Give a reasonable perror msg for bad transport string */
if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) { if (err == -ENOENT)
if (port < 1 || port > 65535) err = -EPROTONOSUPPORT;
return -EINVAL; return err;
if (nfsd_serv) {
xprt = svc_find_xprt(nfsd_serv, transport,
AF_UNSPEC, port);
if (xprt) {
svc_close_xprt(xprt);
svc_xprt_put(xprt);
err = 0;
} else
err = -ENOTCONN;
}
return err < 0 ? err : 0;
}
} }
return 0;
}
/*
* A transport listener is removed by writing a "-", it's transport
* name, and it's port number.
*/
static ssize_t __write_ports_delxprt(char *buf)
{
struct svc_xprt *xprt;
char transport[16];
int port;
if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
return -EINVAL;
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);
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; return -EINVAL;
} }
...@@ -1030,7 +1092,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) ...@@ -1030,7 +1092,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
* buf: C string containing an unsigned * buf: C string containing an unsigned
* integer value representing a bound * integer value representing a bound
* but unconnected socket that is to be * 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 * size: non-zero length of C string in @buf
* Output: * Output:
* On success: NFS service is started; * On success: NFS service is started;
...@@ -1138,7 +1202,9 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) ...@@ -1138,7 +1202,9 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
nfsd_max_blksize = bsize; nfsd_max_blksize = bsize;
mutex_unlock(&nfsd_mutex); 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 #ifdef CONFIG_NFSD_V4
...@@ -1162,8 +1228,9 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) ...@@ -1162,8 +1228,9 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
return -EINVAL; return -EINVAL;
nfs4_reset_lease(lease); 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) ...@@ -1219,8 +1286,9 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
status = nfs4_reset_recoverydir(recdir); 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 @@ ...@@ -27,9 +27,6 @@
#define NFSDDBG_FACILITY NFSDDBG_FH #define NFSDDBG_FACILITY NFSDDBG_FH
static int nfsd_nr_verified;
static int nfsd_nr_put;
/* /*
* our acceptability function. * our acceptability function.
* if NOSUBTREECHECK, accept anything * if NOSUBTREECHECK, accept anything
...@@ -251,7 +248,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) ...@@ -251,7 +248,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
fhp->fh_dentry = dentry; fhp->fh_dentry = dentry;
fhp->fh_export = exp; fhp->fh_export = exp;
nfsd_nr_verified++;
return 0; return 0;
out: out:
exp_put(exp); exp_put(exp);
...@@ -552,7 +548,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, ...@@ -552,7 +548,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
return nfserr_opnotsupp; return nfserr_opnotsupp;
} }
nfsd_nr_verified++;
return 0; return 0;
} }
...@@ -609,7 +604,6 @@ fh_put(struct svc_fh *fhp) ...@@ -609,7 +604,6 @@ fh_put(struct svc_fh *fhp)
fhp->fh_pre_saved = 0; fhp->fh_pre_saved = 0;
fhp->fh_post_saved = 0; fhp->fh_post_saved = 0;
#endif #endif
nfsd_nr_put++;
} }
if (exp) { if (exp) {
cache_put(&exp->h, &svc_export_cache); cache_put(&exp->h, &svc_export_cache);
......
...@@ -533,45 +533,179 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, ...@@ -533,45 +533,179 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
* NFSv2 Server procedures. * NFSv2 Server procedures.
* Only the results of non-idempotent operations are cached. * Only the results of non-idempotent operations are cached.
*/ */
#define nfsd_proc_none NULL
#define nfssvc_release_none NULL
struct nfsd_void { int dummy; }; 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 ST 1 /* status */
#define FH 8 /* filehandle */ #define FH 8 /* filehandle */
#define AT 18 /* attributes */ #define AT 18 /* attributes */
static struct svc_procedure nfsd_procedures2[18] = { static struct svc_procedure nfsd_procedures2[18] = {
PROC(null, void, void, none, RC_NOCACHE, ST), [NFSPROC_NULL] = {
PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT), .pc_func = (svc_procfunc) nfsd_proc_null,
PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF, ST+AT), .pc_decode = (kxdrproc_t) nfssvc_decode_void,
PROC(none, void, void, none, RC_NOCACHE, ST), .pc_encode = (kxdrproc_t) nfssvc_encode_void,
PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, ST+FH+AT), .pc_argsize = sizeof(struct nfsd_void),
PROC(readlink, readlinkargs, readlinkres, none, RC_NOCACHE, ST+1+NFS_MAXPATHLEN/4), .pc_ressize = sizeof(struct nfsd_void),
PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE_V2/4), .pc_cachetype = RC_NOCACHE,
PROC(none, void, void, none, RC_NOCACHE, ST), .pc_xdrressize = ST,
PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, ST+AT), },
PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT), [NFSPROC_GETATTR] = {
PROC(remove, diropargs, void, none, RC_REPLSTAT, ST), .pc_func = (svc_procfunc) nfsd_proc_getattr,
PROC(rename, renameargs, void, none, RC_REPLSTAT, ST), .pc_decode = (kxdrproc_t) nfssvc_decode_fhandle,
PROC(link, linkargs, void, none, RC_REPLSTAT, ST), .pc_encode = (kxdrproc_t) nfssvc_encode_attrstat,
PROC(symlink, symlinkargs, void, none, RC_REPLSTAT, ST), .pc_release = (kxdrproc_t) nfssvc_release_fhandle,
PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT), .pc_argsize = sizeof(struct nfsd_fhandle),
PROC(rmdir, diropargs, void, none, RC_REPLSTAT, ST), .pc_ressize = sizeof(struct nfsd_attrstat),
PROC(readdir, readdirargs, readdirres, none, RC_NOCACHE, 0), .pc_cachetype = RC_NOCACHE,
PROC(statfs, fhandle, statfsres, none, RC_NOCACHE, ST+5), .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,12 +390,14 @@ nfsd_svc(unsigned short port, int nrservs) ...@@ -390,12 +390,14 @@ nfsd_svc(unsigned short port, int nrservs)
mutex_lock(&nfsd_mutex); mutex_lock(&nfsd_mutex);
dprintk("nfsd: creating service\n"); dprintk("nfsd: creating service\n");
error = -EINVAL;
if (nrservs <= 0) if (nrservs <= 0)
nrservs = 0; nrservs = 0;
if (nrservs > NFSD_MAXSERVS) if (nrservs > NFSD_MAXSERVS)
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 */ /* Readahead param cache - will no-op if it already exists */
error = nfsd_racache_init(2*nrservs); error = nfsd_racache_init(2*nrservs);
if (error<0) if (error<0)
...@@ -413,6 +415,12 @@ nfsd_svc(unsigned short port, int nrservs) ...@@ -413,6 +415,12 @@ nfsd_svc(unsigned short port, int nrservs)
goto failure; goto failure;
error = svc_set_num_threads(nfsd_serv, NULL, nrservs); 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: failure:
svc_destroy(nfsd_serv); /* Release server */ svc_destroy(nfsd_serv); /* Release server */
out: out:
......
...@@ -966,6 +966,43 @@ static void kill_suid(struct dentry *dentry) ...@@ -966,6 +966,43 @@ static void kill_suid(struct dentry *dentry)
mutex_unlock(&dentry->d_inode->i_mutex); 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 static __be32
nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, 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, ...@@ -978,6 +1015,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
__be32 err = 0; __be32 err = 0;
int host_err; int host_err;
int stable = *stablep; int stable = *stablep;
int use_wgather;
#ifdef MSNFS #ifdef MSNFS
err = nfserr_perm; err = nfserr_perm;
...@@ -996,9 +1034,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -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 sync export option has been set, or
* - the client requested O_SYNC behavior (NFSv3 feature). * - the client requested O_SYNC behavior (NFSv3 feature).
* - The file system doesn't support fsync(). * - 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. * 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 */ if (!file->f_op->fsync) {/* COMMIT3 cannot work */
stable = 2; stable = 2;
...@@ -1007,7 +1046,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -1007,7 +1046,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
if (!EX_ISSYNC(exp)) if (!EX_ISSYNC(exp))
stable = 0; stable = 0;
if (stable && !EX_WGATHER(exp)) { if (stable && !use_wgather) {
spin_lock(&file->f_lock); spin_lock(&file->f_lock);
file->f_flags |= O_SYNC; file->f_flags |= O_SYNC;
spin_unlock(&file->f_lock); spin_unlock(&file->f_lock);
...@@ -1017,52 +1056,20 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -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); oldfs = get_fs(); set_fs(KERNEL_DS);
host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
set_fs(oldfs); set_fs(oldfs);
if (host_err >= 0) { if (host_err < 0)
*cnt = host_err; goto out_nfserr;
nfsdstats.io_write += host_err; *cnt = host_err;
fsnotify_modify(file->f_path.dentry); nfsdstats.io_write += host_err;
} fsnotify_modify(file->f_path.dentry);
/* clear setuid/setgid flag after write */ /* 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); kill_suid(dentry);
if (host_err >= 0 && stable) { if (stable && use_wgather)
static ino_t last_ino; host_err = wait_for_concurrent_writes(file);
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;
}
out_nfserr:
dprintk("nfsd: write complete host_err=%d\n", host_err); dprintk("nfsd: write complete host_err=%d\n", host_err);
if (host_err >= 0) if (host_err >= 0)
err = 0; err = 0;
......
...@@ -1107,6 +1107,7 @@ extern void locks_copy_lock(struct file_lock *, struct file_lock *); ...@@ -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_copy_lock(struct file_lock *, const struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_flock(struct file *); 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 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(struct file *, struct file_lock *, struct file_lock *);
extern int posix_lock_file_wait(struct file *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *);
......
...@@ -14,8 +14,7 @@ ...@@ -14,8 +14,7 @@
#include <linux/uio.h> #include <linux/uio.h>
/* /*
* Representation of a reply cache entry. The first two members *must* * Representation of a reply cache entry.
* be hash_next and hash_prev.
*/ */
struct svc_cacherep { struct svc_cacherep {
struct hlist_node c_hash; struct hlist_node c_hash;
......
...@@ -151,9 +151,15 @@ typedef struct svc_fh { ...@@ -151,9 +151,15 @@ typedef struct svc_fh {
__u64 fh_pre_size; /* size before operation */ __u64 fh_pre_size; /* size before operation */
struct timespec fh_pre_mtime; /* mtime before oper */ struct timespec fh_pre_mtime; /* mtime before oper */
struct timespec fh_pre_ctime; /* ctime 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 */ /* Post-op attributes saved in fh_unlock */
struct kstat fh_post_attr; /* full attrs after operation */ struct kstat fh_post_attr; /* full attrs after operation */
u64 fh_post_change; /* nfsv4 change; see above */
#endif /* CONFIG_NFSD_V3 */ #endif /* CONFIG_NFSD_V3 */
} svc_fh; } svc_fh;
...@@ -298,6 +304,7 @@ fill_pre_wcc(struct svc_fh *fhp) ...@@ -298,6 +304,7 @@ fill_pre_wcc(struct svc_fh *fhp)
fhp->fh_pre_mtime = inode->i_mtime; fhp->fh_pre_mtime = inode->i_mtime;
fhp->fh_pre_ctime = inode->i_ctime; fhp->fh_pre_ctime = inode->i_ctime;
fhp->fh_pre_size = inode->i_size; fhp->fh_pre_size = inode->i_size;
fhp->fh_pre_change = inode->i_version;
fhp->fh_pre_saved = 1; fhp->fh_pre_saved = 1;
} }
} }
......
...@@ -60,15 +60,6 @@ typedef struct { ...@@ -60,15 +60,6 @@ typedef struct {
#define si_stateownerid si_opaque.so_stateownerid #define si_stateownerid si_opaque.so_stateownerid
#define si_fileid si_opaque.so_fileid #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 nfs4_delegation {
struct list_head dl_perfile; struct list_head dl_perfile;
struct list_head dl_perclnt; struct list_head dl_perclnt;
...@@ -80,22 +71,25 @@ struct nfs4_delegation { ...@@ -80,22 +71,25 @@ struct nfs4_delegation {
struct file *dl_vfs_file; struct file *dl_vfs_file;
u32 dl_type; u32 dl_type;
time_t dl_time; 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 */ /* client delegation callback info */
struct nfs4_callback { struct nfs4_cb_conn {
/* SETCLIENTID info */ /* SETCLIENTID info */
u32 cb_addr; u32 cb_addr;
unsigned short cb_port; unsigned short cb_port;
u32 cb_prog; u32 cb_prog;
u32 cb_ident; u32 cb_minorversion;
u32 cb_ident; /* minorversion 0 only */
/* RPC client info */ /* RPC client info */
atomic_t cb_set; /* successful CB_NULL call */ atomic_t cb_set; /* successful CB_NULL call */
struct rpc_clnt * cb_client; struct rpc_clnt * cb_client;
struct rpc_cred * cb_cred;
}; };
/* Maximum number of slots per session. 128 is useful for long haul TCP */ /* Maximum number of slots per session. 128 is useful for long haul TCP */
...@@ -121,6 +115,17 @@ struct nfsd4_slot { ...@@ -121,6 +115,17 @@ struct nfsd4_slot {
struct nfsd4_cache_entry sl_cache_entry; 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 nfsd4_session {
struct kref se_ref; struct kref se_ref;
struct list_head se_hash; /* hash by sessionid */ struct list_head se_hash; /* hash by sessionid */
...@@ -128,11 +133,8 @@ struct nfsd4_session { ...@@ -128,11 +133,8 @@ struct nfsd4_session {
u32 se_flags; u32 se_flags;
struct nfs4_client *se_client; /* for expire_client */ struct nfs4_client *se_client; /* for expire_client */
struct nfs4_sessionid se_sessionid; struct nfs4_sessionid se_sessionid;
u32 se_fmaxreq_sz; struct nfsd4_channel_attrs se_fchannel;
u32 se_fmaxresp_sz; struct nfsd4_channel_attrs se_bchannel;
u32 se_fmaxresp_cached;
u32 se_fmaxops;
u32 se_fnumslots;
struct nfsd4_slot se_slots[]; /* forward channel slots */ struct nfsd4_slot se_slots[]; /* forward channel slots */
}; };
...@@ -184,7 +186,7 @@ struct nfs4_client { ...@@ -184,7 +186,7 @@ struct nfs4_client {
struct svc_cred cl_cred; /* setclientid principal */ struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */ clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* 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 */ atomic_t cl_count; /* ref count */
u32 cl_firststate; /* recovery dir creation */ u32 cl_firststate; /* recovery dir creation */
......
...@@ -64,10 +64,13 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs) ...@@ -64,10 +64,13 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
struct nfsd4_change_info { struct nfsd4_change_info {
u32 atomic; u32 atomic;
bool change_supported;
u32 before_ctime_sec; u32 before_ctime_sec;
u32 before_ctime_nsec; u32 before_ctime_nsec;
u64 before_change;
u32 after_ctime_sec; u32 after_ctime_sec;
u32 after_ctime_nsec; u32 after_ctime_nsec;
u64 after_change;
}; };
struct nfsd4_access { struct nfsd4_access {
...@@ -363,17 +366,6 @@ struct nfsd4_exchange_id { ...@@ -363,17 +366,6 @@ struct nfsd4_exchange_id {
int spa_how; 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 { struct nfsd4_create_session {
clientid_t clientid; clientid_t clientid;
struct nfs4_sessionid sessionid; struct nfs4_sessionid sessionid;
...@@ -503,10 +495,16 @@ set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp) ...@@ -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); BUG_ON(!fhp->fh_pre_saved || !fhp->fh_post_saved);
cinfo->atomic = 1; cinfo->atomic = 1;
cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec; cinfo->change_supported = IS_I_VERSION(fhp->fh_dentry->d_inode);
cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec; if (cinfo->change_supported) {
cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec; cinfo->before_change = fhp->fh_pre_change;
cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec; 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 *); int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *, void *);
......
...@@ -83,7 +83,7 @@ int svc_port_is_privileged(struct sockaddr *sin); ...@@ -83,7 +83,7 @@ int svc_port_is_privileged(struct sockaddr *sin);
int svc_print_xprts(char *buf, int maxlen); int svc_print_xprts(char *buf, int maxlen);
struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
const sa_family_t af, const unsigned short port); 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) 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) ...@@ -118,7 +118,7 @@ static inline unsigned short svc_addr_port(const struct sockaddr *sa)
return 0; 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) { switch (sa->sa_family) {
case AF_INET: case AF_INET:
...@@ -126,7 +126,8 @@ static inline size_t svc_addr_len(struct sockaddr *sa) ...@@ -126,7 +126,8 @@ static inline size_t svc_addr_len(struct sockaddr *sa)
case AF_INET6: case AF_INET6:
return sizeof(struct sockaddr_in6); return sizeof(struct sockaddr_in6);
} }
return -EAFNOSUPPORT;
return 0;
} }
static inline unsigned short svc_xprt_local_port(const struct svc_xprt *xprt) static inline unsigned short svc_xprt_local_port(const struct svc_xprt *xprt)
......
...@@ -38,8 +38,11 @@ int svc_recv(struct svc_rqst *, long); ...@@ -38,8 +38,11 @@ int svc_recv(struct svc_rqst *, long);
int svc_send(struct svc_rqst *); int svc_send(struct svc_rqst *);
void svc_drop(struct svc_rqst *); void svc_drop(struct svc_rqst *);
void svc_sock_update_bufs(struct svc_serv *serv); void svc_sock_update_bufs(struct svc_serv *serv);
int svc_sock_names(char *buf, struct svc_serv *serv, char *toclose); int svc_sock_names(struct svc_serv *serv, char *buf,
int svc_addsock(struct svc_serv *serv, int fd, char *name_return); 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_init_xprt_sock(void);
void svc_cleanup_xprt_sock(void); void svc_cleanup_xprt_sock(void);
struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot); 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) ...@@ -488,7 +488,7 @@ static void do_cache_clean(struct work_struct *work)
{ {
int delay = 5; int delay = 5;
if (cache_clean() == -1) if (cache_clean() == -1)
delay = 30*HZ; delay = round_jiffies_relative(30*HZ);
if (list_empty(&cache_list)) if (list_empty(&cache_list))
delay = 0; delay = 0;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <net/sock.h> #include <net/sock.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/svcsock.h>
#define RPCDBG_FACILITY RPCDBG_SVCXPRT #define RPCDBG_FACILITY RPCDBG_SVCXPRT
...@@ -1097,36 +1098,58 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, ...@@ -1097,36 +1098,58 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
} }
EXPORT_SYMBOL_GPL(svc_find_xprt); EXPORT_SYMBOL_GPL(svc_find_xprt);
/* static int svc_one_xprt_name(const struct svc_xprt *xprt,
* Format a buffer with a list of the active transports. A zero for char *pos, int remaining)
* the buflen parameter disables target buffer overflow checking. {
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; struct svc_xprt *xprt;
char xprt_str[64]; int len, totlen;
int totlen = 0; char *pos;
int len;
/* Sanity check args */ /* Sanity check args */
if (!serv) if (!serv)
return 0; return 0;
spin_lock_bh(&serv->sv_lock); spin_lock_bh(&serv->sv_lock);
pos = buf;
totlen = 0;
list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) { list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
len = snprintf(xprt_str, sizeof(xprt_str), len = svc_one_xprt_name(xprt, pos, buflen - totlen);
"%s %d\n", xprt->xpt_class->xcl_name, if (len < 0) {
svc_xprt_local_port(xprt)); *buf = '\0';
/* If the string was truncated, replace with error string */ totlen = len;
if (len >= sizeof(xprt_str)) }
strcpy(xprt_str, "name-too-long\n"); if (len <= 0)
/* Don't overflow buffer */
len = strlen(xprt_str);
if (buflen && (len + totlen >= buflen))
break; break;
strcpy(buf+totlen, xprt_str);
pos += len;
totlen += len; totlen += len;
} }
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
return totlen; return totlen;
} }
......
...@@ -240,42 +240,76 @@ static int svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) ...@@ -240,42 +240,76 @@ static int svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr)
/* /*
* Report socket names for nfsdfs * 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; int len;
switch(svsk->sk_sk->sk_family) { switch (sk->sk_family) {
case AF_INET: case PF_INET:
len = sprintf(buf, "ipv4 %s %pI4 %d\n", len = snprintf(buf, remaining, "ipv4 %s %pI4 %d\n",
svsk->sk_sk->sk_protocol == IPPROTO_UDP ? proto_name,
"udp" : "tcp", &inet_sk(sk)->rcv_saddr,
&inet_sk(svsk->sk_sk)->rcv_saddr, inet_sk(sk)->num);
inet_sk(svsk->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; break;
default: default:
len = sprintf(buf, "*unknown-%d*\n", len = snprintf(buf, remaining, "*unknown-%d*\n",
svsk->sk_sk->sk_family); sk->sk_family);
}
if (len >= remaining) {
*buf = '\0';
return -ENAMETOOLONG;
} }
return len; 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; struct svc_sock *svsk, *closesk = NULL;
int len = 0; int len = 0;
if (!serv) if (!serv)
return 0; return 0;
spin_lock_bh(&serv->sv_lock); spin_lock_bh(&serv->sv_lock);
list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list) { list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list) {
int onelen = one_sock_name(buf+len, svsk); int onelen = svc_one_sock_name(svsk, buf + len, buflen - len);
if (toclose && strcmp(toclose, buf+len) == 0) if (onelen < 0) {
len = onelen;
break;
}
if (toclose && strcmp(toclose, buf + len) == 0)
closesk = svsk; closesk = svsk;
else else
len += onelen; len += onelen;
} }
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
if (closesk) if (closesk)
/* Should unregister with portmap, but you cannot /* Should unregister with portmap, but you cannot
* unregister just one protocol... * unregister just one protocol...
...@@ -346,6 +380,7 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, ...@@ -346,6 +380,7 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd,
sock->sk->sk_sndbuf = snd * 2; sock->sk->sk_sndbuf = snd * 2;
sock->sk->sk_rcvbuf = rcv * 2; sock->sk->sk_rcvbuf = rcv * 2;
sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK; sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK|SOCK_RCVBUF_LOCK;
sock->sk->sk_write_space(sock->sk);
release_sock(sock->sk); release_sock(sock->sk);
#endif #endif
} }
...@@ -387,6 +422,15 @@ static void svc_write_space(struct sock *sk) ...@@ -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. * Copy the UDP datagram's destination address to the rqstp structure.
* The 'destination' address in this case is the address to which the * 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) ...@@ -427,13 +471,14 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
long all[SVC_PKTINFO_SPACE / sizeof(long)]; long all[SVC_PKTINFO_SPACE / sizeof(long)];
} buffer; } buffer;
struct cmsghdr *cmh = &buffer.hdr; struct cmsghdr *cmh = &buffer.hdr;
int err, len;
struct msghdr msg = { struct msghdr msg = {
.msg_name = svc_addr(rqstp), .msg_name = svc_addr(rqstp),
.msg_control = cmh, .msg_control = cmh,
.msg_controllen = sizeof(buffer), .msg_controllen = sizeof(buffer),
.msg_flags = MSG_DONTWAIT, .msg_flags = MSG_DONTWAIT,
}; };
size_t len;
int err;
if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags)) if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags))
/* udp sockets need large rcvbuf as all pending /* udp sockets need large rcvbuf as all pending
...@@ -465,8 +510,8 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) ...@@ -465,8 +510,8 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
return -EAGAIN; return -EAGAIN;
} }
len = svc_addr_len(svc_addr(rqstp)); len = svc_addr_len(svc_addr(rqstp));
if (len < 0) if (len == 0)
return len; return -EAFNOSUPPORT;
rqstp->rq_addrlen = len; rqstp->rq_addrlen = len;
if (skb->tstamp.tv64 == 0) { if (skb->tstamp.tv64 == 0) {
skb->tstamp = ktime_get_real(); skb->tstamp = ktime_get_real();
...@@ -980,25 +1025,16 @@ static void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp) ...@@ -980,25 +1025,16 @@ static void svc_tcp_prep_reply_hdr(struct svc_rqst *rqstp)
static int svc_tcp_has_wspace(struct svc_xprt *xprt) static int svc_tcp_has_wspace(struct svc_xprt *xprt)
{ {
struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
struct svc_serv *serv = svsk->sk_xprt.xpt_server; struct svc_serv *serv = svsk->sk_xprt.xpt_server;
int required; int required;
int wspace;
/* if (test_bit(XPT_LISTENER, &xprt->xpt_flags))
* Set the SOCK_NOSPACE flag before checking the available return 1;
* sock space. 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); set_bit(SOCK_NOSPACE, &svsk->sk_sock->flags);
required = atomic_read(&svsk->sk_xprt.xpt_reserved) + serv->sv_max_mesg; return 0;
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);
return 1;
} }
static struct svc_xprt *svc_tcp_create(struct svc_serv *serv, 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) ...@@ -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"); dprintk("setting up TCP socket for reading\n");
sk->sk_state_change = svc_tcp_state_change; sk->sk_state_change = svc_tcp_state_change;
sk->sk_data_ready = svc_tcp_data_ready; 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_reclen = 0;
svsk->sk_tcplen = 0; svsk->sk_tcplen = 0;
...@@ -1148,9 +1184,19 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, ...@@ -1148,9 +1184,19 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
return svsk; return svsk;
} }
int svc_addsock(struct svc_serv *serv, /**
int fd, * svc_addsock - add a listener socket to an RPC service
char *name_return) * @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; int err = 0;
struct socket *so = sockfd_lookup(fd, &err); struct socket *so = sockfd_lookup(fd, &err);
...@@ -1190,7 +1236,7 @@ int svc_addsock(struct svc_serv *serv, ...@@ -1190,7 +1236,7 @@ int svc_addsock(struct svc_serv *serv,
sockfd_put(so); sockfd_put(so);
return err; return err;
} }
return one_sock_name(name_return, svsk); return svc_one_sock_name(svsk, name_return, len);
} }
EXPORT_SYMBOL_GPL(svc_addsock); EXPORT_SYMBOL_GPL(svc_addsock);
......
...@@ -397,14 +397,14 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, ...@@ -397,14 +397,14 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
if (!ch) if (!ch)
return 0; 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); svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count);
if (ch_count > RPCSVC_MAXPAGES) if (ch_count > RPCSVC_MAXPAGES)
return -EINVAL; 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) if (!xprt->sc_frmr_pg_list_len)
sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp,
rpl_map, chl_map, ch_count, rpl_map, chl_map, ch_count,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment