mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
Merge branch 'for-2.6.37' of git://linux-nfs.org/~bfields/linux
* 'for-2.6.37' of git://linux-nfs.org/~bfields/linux: (99 commits) svcrpc: svc_tcp_sendto XPT_DEAD check is redundant svcrpc: no need for XPT_DEAD check in svc_xprt_enqueue svcrpc: assume svc_delete_xprt() called only once svcrpc: never clear XPT_BUSY on dead xprt nfsd4: fix connection allocation in sequence() nfsd4: only require krb5 principal for NFSv4.0 callbacks nfsd4: move minorversion to client nfsd4: delay session removal till free_client nfsd4: separate callback change and callback probe nfsd4: callback program number is per-session nfsd4: track backchannel connections nfsd4: confirm only on succesful create_session nfsd4: make backchannel sequence number per-session nfsd4: use client pointer to backchannel session nfsd4: move callback setup into session init code nfsd4: don't cache seq_misordered replies SUNRPC: Properly initialize sock_xprt.srcaddr in all cases SUNRPC: Use conventional switch statement when reclassifying sockets sunrpc/xprtrdma: clean up workqueue usage sunrpc: Turn list_for_each-s into the ..._entry-s ... Fix up trivial conflicts (two different deprecation notices added in separate branches) in Documentation/feature-removal-schedule.txt
This commit is contained in:
commit
4390110fef
61 changed files with 1535 additions and 1937 deletions
|
|
@ -29,6 +29,18 @@ config NFSD
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config NFSD_DEPRECATED
|
||||
bool "Include support for deprecated syscall interface to NFSD"
|
||||
depends on NFSD
|
||||
default y
|
||||
help
|
||||
The syscall interface to nfsd was obsoleted in 2.6.0 by a new
|
||||
filesystem based interface. The old interface is due for removal
|
||||
in 2.6.40. If you wish to remove the interface before then
|
||||
say N.
|
||||
|
||||
In unsure, say Y.
|
||||
|
||||
config NFSD_V2_ACL
|
||||
bool
|
||||
depends on NFSD
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@
|
|||
typedef struct auth_domain svc_client;
|
||||
typedef struct svc_export svc_export;
|
||||
|
||||
static void exp_do_unexport(svc_export *unexp);
|
||||
static int exp_verify_string(char *cp, int max);
|
||||
|
||||
/*
|
||||
* We have two caches.
|
||||
* One maps client+vfsmnt+dentry to export options - the export map
|
||||
|
|
@ -802,6 +799,7 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
|
|||
return ek;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
|
||||
struct svc_export *exp)
|
||||
{
|
||||
|
|
@ -852,6 +850,7 @@ exp_get_fsid_key(svc_client *clp, int fsid)
|
|||
|
||||
return exp_find_key(clp, FSID_NUM, fsidv, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
|
||||
struct cache_req *reqp)
|
||||
|
|
@ -893,6 +892,7 @@ static struct svc_export *exp_parent(svc_client *clp, struct path *path)
|
|||
return exp;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
/*
|
||||
* Hashtable locking. Write locks are placed only by user processes
|
||||
* wanting to modify export information.
|
||||
|
|
@ -925,6 +925,19 @@ exp_writeunlock(void)
|
|||
{
|
||||
up_write(&hash_sem);
|
||||
}
|
||||
#else
|
||||
|
||||
/* hash_sem not needed once deprecated interface is removed */
|
||||
void exp_readlock(void) {}
|
||||
static inline void exp_writelock(void){}
|
||||
void exp_readunlock(void) {}
|
||||
static inline void exp_writeunlock(void){}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
static void exp_do_unexport(svc_export *unexp);
|
||||
static int exp_verify_string(char *cp, int max);
|
||||
|
||||
static void exp_fsid_unhash(struct svc_export *exp)
|
||||
{
|
||||
|
|
@ -935,10 +948,9 @@ static void exp_fsid_unhash(struct svc_export *exp)
|
|||
|
||||
ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid);
|
||||
if (!IS_ERR(ek)) {
|
||||
ek->h.expiry_time = get_seconds()-1;
|
||||
sunrpc_invalidate(&ek->h, &svc_expkey_cache);
|
||||
cache_put(&ek->h, &svc_expkey_cache);
|
||||
}
|
||||
svc_expkey_cache.nextcheck = get_seconds();
|
||||
}
|
||||
|
||||
static int exp_fsid_hash(svc_client *clp, struct svc_export *exp)
|
||||
|
|
@ -973,10 +985,9 @@ static void exp_unhash(struct svc_export *exp)
|
|||
|
||||
ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino);
|
||||
if (!IS_ERR(ek)) {
|
||||
ek->h.expiry_time = get_seconds()-1;
|
||||
sunrpc_invalidate(&ek->h, &svc_expkey_cache);
|
||||
cache_put(&ek->h, &svc_expkey_cache);
|
||||
}
|
||||
svc_expkey_cache.nextcheck = get_seconds();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1097,8 +1108,7 @@ out:
|
|||
static void
|
||||
exp_do_unexport(svc_export *unexp)
|
||||
{
|
||||
unexp->h.expiry_time = get_seconds()-1;
|
||||
svc_export_cache.nextcheck = get_seconds();
|
||||
sunrpc_invalidate(&unexp->h, &svc_export_cache);
|
||||
exp_unhash(unexp);
|
||||
exp_fsid_unhash(unexp);
|
||||
}
|
||||
|
|
@ -1150,6 +1160,7 @@ out_unlock:
|
|||
exp_writeunlock();
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_NFSD_DEPRECATED */
|
||||
|
||||
/*
|
||||
* Obtain the root fh on behalf of a client.
|
||||
|
|
@ -1459,25 +1470,43 @@ static void show_secinfo_flags(struct seq_file *m, int flags)
|
|||
show_expflags(m, flags, NFSEXP_SECINFO_FLAGS);
|
||||
}
|
||||
|
||||
static bool secinfo_flags_equal(int f, int g)
|
||||
{
|
||||
f &= NFSEXP_SECINFO_FLAGS;
|
||||
g &= NFSEXP_SECINFO_FLAGS;
|
||||
return f == g;
|
||||
}
|
||||
|
||||
static int show_secinfo_run(struct seq_file *m, struct exp_flavor_info **fp, struct exp_flavor_info *end)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = (*fp)->flags;
|
||||
seq_printf(m, ",sec=%d", (*fp)->pseudoflavor);
|
||||
(*fp)++;
|
||||
while (*fp != end && secinfo_flags_equal(flags, (*fp)->flags)) {
|
||||
seq_printf(m, ":%d", (*fp)->pseudoflavor);
|
||||
(*fp)++;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void show_secinfo(struct seq_file *m, struct svc_export *exp)
|
||||
{
|
||||
struct exp_flavor_info *f;
|
||||
struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
|
||||
int lastflags = 0, first = 0;
|
||||
int flags;
|
||||
|
||||
if (exp->ex_nflavors == 0)
|
||||
return;
|
||||
for (f = exp->ex_flavors; f < end; f++) {
|
||||
if (first || f->flags != lastflags) {
|
||||
if (!first)
|
||||
show_secinfo_flags(m, lastflags);
|
||||
seq_printf(m, ",sec=%d", f->pseudoflavor);
|
||||
lastflags = f->flags;
|
||||
} else {
|
||||
seq_printf(m, ":%d", f->pseudoflavor);
|
||||
}
|
||||
f = exp->ex_flavors;
|
||||
flags = show_secinfo_run(m, &f, end);
|
||||
if (!secinfo_flags_equal(flags, exp->ex_flags))
|
||||
show_secinfo_flags(m, flags);
|
||||
while (f != end) {
|
||||
flags = show_secinfo_run(m, &f, end);
|
||||
show_secinfo_flags(m, flags);
|
||||
}
|
||||
show_secinfo_flags(m, lastflags);
|
||||
}
|
||||
|
||||
static void exp_flags(struct seq_file *m, int flag, int fsid,
|
||||
|
|
@ -1532,6 +1561,7 @@ const struct seq_operations nfs_exports_op = {
|
|||
.show = e_show,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
/*
|
||||
* Add or modify a client.
|
||||
* Change requests may involve the list of host addresses. The list of
|
||||
|
|
@ -1563,7 +1593,7 @@ exp_addclient(struct nfsctl_client *ncp)
|
|||
/* Insert client into hashtable. */
|
||||
for (i = 0; i < ncp->cl_naddr; i++) {
|
||||
ipv6_addr_set_v4mapped(ncp->cl_addrlist[i].s_addr, &addr6);
|
||||
auth_unix_add_addr(&addr6, dom);
|
||||
auth_unix_add_addr(&init_net, &addr6, dom);
|
||||
}
|
||||
auth_unix_forget_old(dom);
|
||||
auth_domain_put(dom);
|
||||
|
|
@ -1621,6 +1651,7 @@ exp_verify_string(char *cp, int max)
|
|||
printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFSD_DEPRECATED */
|
||||
|
||||
/*
|
||||
* Initialize the exports module.
|
||||
|
|
|
|||
|
|
@ -247,10 +247,11 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
|
|||
}
|
||||
|
||||
static void
|
||||
encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
|
||||
encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
|
||||
struct nfs4_cb_compound_hdr *hdr)
|
||||
{
|
||||
__be32 *p;
|
||||
struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
|
||||
|
||||
if (hdr->minorversion == 0)
|
||||
return;
|
||||
|
|
@ -258,8 +259,8 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
|
|||
RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);
|
||||
|
||||
WRITE32(OP_CB_SEQUENCE);
|
||||
WRITEMEM(args->cbs_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
|
||||
WRITE32(args->cbs_clp->cl_cb_seq_nr);
|
||||
WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN);
|
||||
WRITE32(ses->se_cb_seq_nr);
|
||||
WRITE32(0); /* slotid, always 0 */
|
||||
WRITE32(0); /* highest slotid always 0 */
|
||||
WRITE32(0); /* cachethis always 0 */
|
||||
|
|
@ -279,18 +280,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_rpc_args *rpc_args)
|
||||
struct nfsd4_callback *cb)
|
||||
{
|
||||
struct xdr_stream xdr;
|
||||
struct nfs4_delegation *args = rpc_args->args_op;
|
||||
struct nfs4_delegation *args = cb->cb_op;
|
||||
struct nfs4_cb_compound_hdr hdr = {
|
||||
.ident = args->dl_ident,
|
||||
.minorversion = rpc_args->args_seq.cbs_minorversion,
|
||||
.ident = cb->cb_clp->cl_cb_ident,
|
||||
.minorversion = cb->cb_minorversion,
|
||||
};
|
||||
|
||||
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
|
||||
encode_cb_compound_hdr(&xdr, &hdr);
|
||||
encode_cb_sequence(&xdr, &rpc_args->args_seq, &hdr);
|
||||
encode_cb_sequence(&xdr, cb, &hdr);
|
||||
encode_cb_recall(&xdr, args, &hdr);
|
||||
encode_cb_nops(&hdr);
|
||||
return 0;
|
||||
|
|
@ -338,15 +339,16 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
|
|||
* with a single slot.
|
||||
*/
|
||||
static int
|
||||
decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
|
||||
decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
|
||||
struct rpc_rqst *rqstp)
|
||||
{
|
||||
struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
|
||||
struct nfs4_sessionid id;
|
||||
int status;
|
||||
u32 dummy;
|
||||
__be32 *p;
|
||||
|
||||
if (res->cbs_minorversion == 0)
|
||||
if (cb->cb_minorversion == 0)
|
||||
return 0;
|
||||
|
||||
status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
|
||||
|
|
@ -362,13 +364,12 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
|
|||
READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
|
||||
memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
|
||||
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
|
||||
if (memcmp(id.data, res->cbs_clp->cl_sessionid.data,
|
||||
NFS4_MAX_SESSIONID_LEN)) {
|
||||
if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
|
||||
dprintk("%s Invalid session id\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
READ32(dummy);
|
||||
if (dummy != res->cbs_clp->cl_cb_seq_nr) {
|
||||
if (dummy != ses->se_cb_seq_nr) {
|
||||
dprintk("%s Invalid sequence number\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -392,7 +393,7 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
|
|||
|
||||
static int
|
||||
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
|
||||
struct nfsd4_cb_sequence *seq)
|
||||
struct nfsd4_callback *cb)
|
||||
{
|
||||
struct xdr_stream xdr;
|
||||
struct nfs4_cb_compound_hdr hdr;
|
||||
|
|
@ -402,8 +403,8 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
|
|||
status = decode_cb_compound_hdr(&xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
if (seq) {
|
||||
status = decode_cb_sequence(&xdr, seq, rqstp);
|
||||
if (cb) {
|
||||
status = decode_cb_sequence(&xdr, cb, rqstp);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -472,30 +473,34 @@ static int max_cb_time(void)
|
|||
/* Reference counting, callback cleanup, etc., all look racy as heck.
|
||||
* And why is cl_cb_set an atomic? */
|
||||
|
||||
int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
|
||||
int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
|
||||
{
|
||||
struct rpc_timeout timeparms = {
|
||||
.to_initval = max_cb_time(),
|
||||
.to_retries = 0,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.protocol = XPRT_TRANSPORT_TCP,
|
||||
.address = (struct sockaddr *) &cb->cb_addr,
|
||||
.addrsize = cb->cb_addrlen,
|
||||
.net = &init_net,
|
||||
.address = (struct sockaddr *) &conn->cb_addr,
|
||||
.addrsize = conn->cb_addrlen,
|
||||
.timeout = &timeparms,
|
||||
.program = &cb_program,
|
||||
.prognumber = cb->cb_prog,
|
||||
.version = 0,
|
||||
.authflavor = clp->cl_flavor,
|
||||
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
|
||||
.client_name = clp->cl_principal,
|
||||
};
|
||||
struct rpc_clnt *client;
|
||||
|
||||
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
|
||||
return -EINVAL;
|
||||
if (cb->cb_minorversion) {
|
||||
args.bc_xprt = cb->cb_xprt;
|
||||
if (clp->cl_minorversion == 0) {
|
||||
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
|
||||
return -EINVAL;
|
||||
args.client_name = clp->cl_principal;
|
||||
args.prognumber = conn->cb_prog,
|
||||
args.protocol = XPRT_TRANSPORT_TCP;
|
||||
clp->cl_cb_ident = conn->cb_ident;
|
||||
} else {
|
||||
args.bc_xprt = conn->cb_xprt;
|
||||
args.prognumber = clp->cl_cb_session->se_cb_prog;
|
||||
args.protocol = XPRT_TRANSPORT_BC_TCP;
|
||||
}
|
||||
/* Create RPC client */
|
||||
|
|
@ -505,7 +510,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
|
|||
PTR_ERR(client));
|
||||
return PTR_ERR(client);
|
||||
}
|
||||
nfsd4_set_callback_client(clp, client);
|
||||
clp->cl_cb_client = client;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
|
@ -518,7 +523,7 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason)
|
|||
|
||||
static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_client *clp = calldata;
|
||||
struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
|
||||
|
||||
if (task->tk_status)
|
||||
warn_no_callback_path(clp, task->tk_status);
|
||||
|
|
@ -527,6 +532,8 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
|
|||
}
|
||||
|
||||
static const struct rpc_call_ops nfsd4_cb_probe_ops = {
|
||||
/* XXX: release method to ensure we set the cb channel down if
|
||||
* necessary on early failure? */
|
||||
.rpc_call_done = nfsd4_cb_probe_done,
|
||||
};
|
||||
|
||||
|
|
@ -542,38 +549,42 @@ int set_callback_cred(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct workqueue_struct *callback_wq;
|
||||
|
||||
void do_probe_callback(struct nfs4_client *clp)
|
||||
static void do_probe_callback(struct nfs4_client *clp)
|
||||
{
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
|
||||
.rpc_argp = clp,
|
||||
.rpc_cred = callback_cred
|
||||
};
|
||||
int status;
|
||||
struct nfsd4_callback *cb = &clp->cl_cb_null;
|
||||
|
||||
status = rpc_call_async(clp->cl_cb_client, &msg,
|
||||
RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
|
||||
&nfsd4_cb_probe_ops, (void *)clp);
|
||||
if (status)
|
||||
warn_no_callback_path(clp, status);
|
||||
cb->cb_op = NULL;
|
||||
cb->cb_clp = clp;
|
||||
|
||||
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
|
||||
cb->cb_msg.rpc_argp = NULL;
|
||||
cb->cb_msg.rpc_resp = NULL;
|
||||
cb->cb_msg.rpc_cred = callback_cred;
|
||||
|
||||
cb->cb_ops = &nfsd4_cb_probe_ops;
|
||||
|
||||
queue_work(callback_wq, &cb->cb_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
|
||||
* Poke the callback thread to process any updates to the callback
|
||||
* parameters, and send a null probe.
|
||||
*/
|
||||
void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
|
||||
void nfsd4_probe_callback(struct nfs4_client *clp)
|
||||
{
|
||||
int status;
|
||||
set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
|
||||
do_probe_callback(clp);
|
||||
}
|
||||
|
||||
void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
|
||||
{
|
||||
BUG_ON(atomic_read(&clp->cl_cb_set));
|
||||
|
||||
status = setup_callback_client(clp, cb);
|
||||
if (status) {
|
||||
warn_no_callback_path(clp, status);
|
||||
return;
|
||||
}
|
||||
do_probe_callback(clp);
|
||||
spin_lock(&clp->cl_lock);
|
||||
memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -584,8 +595,7 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
|
|||
static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
|
||||
struct rpc_task *task)
|
||||
{
|
||||
struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
|
||||
u32 *ptr = (u32 *)clp->cl_sessionid.data;
|
||||
u32 *ptr = (u32 *)clp->cl_cb_session->se_sessionid.data;
|
||||
int status = 0;
|
||||
|
||||
dprintk("%s: %u:%u:%u:%u\n", __func__,
|
||||
|
|
@ -597,14 +607,6 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
|
|||
status = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll need the clp during XDR encoding and decoding,
|
||||
* and the sequence during decoding to verify the reply
|
||||
*/
|
||||
args->args_seq.cbs_clp = clp;
|
||||
task->tk_msg.rpc_resp = &args->args_seq;
|
||||
|
||||
out:
|
||||
dprintk("%s status=%d\n", __func__, status);
|
||||
return status;
|
||||
|
|
@ -616,13 +618,13 @@ out:
|
|||
*/
|
||||
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
|
||||
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
|
||||
u32 minorversion = clp->cl_minorversion;
|
||||
int status = 0;
|
||||
|
||||
args->args_seq.cbs_minorversion = minorversion;
|
||||
cb->cb_minorversion = minorversion;
|
||||
if (minorversion) {
|
||||
status = nfsd41_cb_setup_sequence(clp, task);
|
||||
if (status) {
|
||||
|
|
@ -639,19 +641,20 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
|
|||
|
||||
static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
|
||||
dprintk("%s: minorversion=%d\n", __func__,
|
||||
clp->cl_cb_conn.cb_minorversion);
|
||||
clp->cl_minorversion);
|
||||
|
||||
if (clp->cl_cb_conn.cb_minorversion) {
|
||||
if (clp->cl_minorversion) {
|
||||
/* No need for lock, access serialized in nfsd4_cb_prepare */
|
||||
++clp->cl_cb_seq_nr;
|
||||
++clp->cl_cb_session->se_cb_seq_nr;
|
||||
clear_bit(0, &clp->cl_cb_slot_busy);
|
||||
rpc_wake_up_next(&clp->cl_cb_waitq);
|
||||
dprintk("%s: freed slot, new seqid=%d\n", __func__,
|
||||
clp->cl_cb_seq_nr);
|
||||
clp->cl_cb_session->se_cb_seq_nr);
|
||||
|
||||
/* We're done looking into the sequence information */
|
||||
task->tk_msg.rpc_resp = NULL;
|
||||
|
|
@ -661,7 +664,8 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
|
|||
|
||||
static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
|
||||
|
||||
|
|
@ -706,7 +710,8 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
|
|||
|
||||
static void nfsd4_cb_recall_release(void *calldata)
|
||||
{
|
||||
struct nfs4_delegation *dp = calldata;
|
||||
struct nfsd4_callback *cb = calldata;
|
||||
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
|
||||
|
||||
nfs4_put_delegation(dp);
|
||||
}
|
||||
|
|
@ -717,8 +722,6 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = {
|
|||
.rpc_release = nfsd4_cb_recall_release,
|
||||
};
|
||||
|
||||
static struct workqueue_struct *callback_wq;
|
||||
|
||||
int nfsd4_create_callback_queue(void)
|
||||
{
|
||||
callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
|
||||
|
|
@ -733,57 +736,88 @@ void nfsd4_destroy_callback_queue(void)
|
|||
}
|
||||
|
||||
/* must be called under the state lock */
|
||||
void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
|
||||
void nfsd4_shutdown_callback(struct nfs4_client *clp)
|
||||
{
|
||||
struct rpc_clnt *old = clp->cl_cb_client;
|
||||
|
||||
clp->cl_cb_client = new;
|
||||
set_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags);
|
||||
/*
|
||||
* After this, any work that saw the old value of cl_cb_client will
|
||||
* be gone:
|
||||
* Note this won't actually result in a null callback;
|
||||
* instead, nfsd4_do_callback_rpc() will detect the killed
|
||||
* client, destroy the rpc client, and stop:
|
||||
*/
|
||||
do_probe_callback(clp);
|
||||
flush_workqueue(callback_wq);
|
||||
/* So we can safely shut it down: */
|
||||
if (old)
|
||||
rpc_shutdown_client(old);
|
||||
}
|
||||
|
||||
/*
|
||||
* called with dp->dl_count inc'ed.
|
||||
*/
|
||||
static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
|
||||
void nfsd4_release_cb(struct nfsd4_callback *cb)
|
||||
{
|
||||
struct nfs4_client *clp = dp->dl_client;
|
||||
struct rpc_clnt *clnt = clp->cl_cb_client;
|
||||
struct nfs4_rpc_args *args = &dp->dl_recall.cb_args;
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
|
||||
.rpc_cred = callback_cred
|
||||
};
|
||||
if (cb->cb_ops->rpc_release)
|
||||
cb->cb_ops->rpc_release(cb);
|
||||
}
|
||||
|
||||
if (clnt == NULL) {
|
||||
nfs4_put_delegation(dp);
|
||||
return; /* Client is shutting down; give up. */
|
||||
void nfsd4_process_cb_update(struct nfsd4_callback *cb)
|
||||
{
|
||||
struct nfs4_cb_conn conn;
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* This is either an update, or the client dying; in either case,
|
||||
* kill the old client:
|
||||
*/
|
||||
if (clp->cl_cb_client) {
|
||||
rpc_shutdown_client(clp->cl_cb_client);
|
||||
clp->cl_cb_client = NULL;
|
||||
}
|
||||
if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags))
|
||||
return;
|
||||
spin_lock(&clp->cl_lock);
|
||||
/*
|
||||
* Only serialized callback code is allowed to clear these
|
||||
* flags; main nfsd code can only set them:
|
||||
*/
|
||||
BUG_ON(!clp->cl_cb_flags);
|
||||
clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
|
||||
memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn));
|
||||
spin_unlock(&clp->cl_lock);
|
||||
|
||||
args->args_op = dp;
|
||||
msg.rpc_argp = args;
|
||||
dp->dl_retries = 1;
|
||||
rpc_call_async(clnt, &msg, RPC_TASK_SOFT, &nfsd4_cb_recall_ops, dp);
|
||||
err = setup_callback_client(clp, &conn);
|
||||
if (err)
|
||||
warn_no_callback_path(clp, err);
|
||||
}
|
||||
|
||||
void nfsd4_do_callback_rpc(struct work_struct *w)
|
||||
{
|
||||
/* XXX: for now, just send off delegation recall. */
|
||||
/* In future, generalize to handle any sort of callback. */
|
||||
struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work);
|
||||
struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall);
|
||||
struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
struct rpc_clnt *clnt;
|
||||
|
||||
_nfsd4_cb_recall(dp);
|
||||
if (clp->cl_cb_flags)
|
||||
nfsd4_process_cb_update(cb);
|
||||
|
||||
clnt = clp->cl_cb_client;
|
||||
if (!clnt) {
|
||||
/* Callback channel broken, or client killed; give up: */
|
||||
nfsd4_release_cb(cb);
|
||||
return;
|
||||
}
|
||||
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
|
||||
cb->cb_ops, cb);
|
||||
}
|
||||
|
||||
|
||||
void nfsd4_cb_recall(struct nfs4_delegation *dp)
|
||||
{
|
||||
struct nfsd4_callback *cb = &dp->dl_recall;
|
||||
|
||||
dp->dl_retries = 1;
|
||||
cb->cb_op = dp;
|
||||
cb->cb_clp = dp->dl_client;
|
||||
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
|
||||
cb->cb_msg.rpc_argp = cb;
|
||||
cb->cb_msg.rpc_resp = cb;
|
||||
cb->cb_msg.rpc_cred = callback_cred;
|
||||
|
||||
cb->cb_ops = &nfsd4_cb_recall_ops;
|
||||
dp->dl_retries = 1;
|
||||
|
||||
queue_work(callback_wq, &dp->dl_recall.cb_work);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -482,109 +482,26 @@ nfsd_idmap_shutdown(void)
|
|||
cache_unregister(&nametoid_cache);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deferred request handling
|
||||
*/
|
||||
|
||||
struct idmap_defer_req {
|
||||
struct cache_req req;
|
||||
struct cache_deferred_req deferred_req;
|
||||
wait_queue_head_t waitq;
|
||||
atomic_t count;
|
||||
};
|
||||
|
||||
static inline void
|
||||
put_mdr(struct idmap_defer_req *mdr)
|
||||
{
|
||||
if (atomic_dec_and_test(&mdr->count))
|
||||
kfree(mdr);
|
||||
}
|
||||
|
||||
static inline void
|
||||
get_mdr(struct idmap_defer_req *mdr)
|
||||
{
|
||||
atomic_inc(&mdr->count);
|
||||
}
|
||||
|
||||
static void
|
||||
idmap_revisit(struct cache_deferred_req *dreq, int toomany)
|
||||
{
|
||||
struct idmap_defer_req *mdr =
|
||||
container_of(dreq, struct idmap_defer_req, deferred_req);
|
||||
|
||||
wake_up(&mdr->waitq);
|
||||
put_mdr(mdr);
|
||||
}
|
||||
|
||||
static struct cache_deferred_req *
|
||||
idmap_defer(struct cache_req *req)
|
||||
{
|
||||
struct idmap_defer_req *mdr =
|
||||
container_of(req, struct idmap_defer_req, req);
|
||||
|
||||
mdr->deferred_req.revisit = idmap_revisit;
|
||||
get_mdr(mdr);
|
||||
return (&mdr->deferred_req);
|
||||
}
|
||||
|
||||
static inline int
|
||||
do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key,
|
||||
struct cache_detail *detail, struct ent **item,
|
||||
struct idmap_defer_req *mdr)
|
||||
{
|
||||
*item = lookup_fn(key);
|
||||
if (!*item)
|
||||
return -ENOMEM;
|
||||
return cache_check(detail, &(*item)->h, &mdr->req);
|
||||
}
|
||||
|
||||
static inline int
|
||||
do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *),
|
||||
struct ent *key, struct cache_detail *detail,
|
||||
struct ent **item)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
|
||||
*item = lookup_fn(key);
|
||||
if (!*item)
|
||||
goto out_err;
|
||||
ret = -ETIMEDOUT;
|
||||
if (!test_bit(CACHE_VALID, &(*item)->h.flags)
|
||||
|| (*item)->h.expiry_time < get_seconds()
|
||||
|| detail->flush_time > (*item)->h.last_refresh)
|
||||
goto out_put;
|
||||
ret = -ENOENT;
|
||||
if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
|
||||
goto out_put;
|
||||
return 0;
|
||||
out_put:
|
||||
cache_put(&(*item)->h, detail);
|
||||
out_err:
|
||||
*item = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
idmap_lookup(struct svc_rqst *rqstp,
|
||||
struct ent *(*lookup_fn)(struct ent *), struct ent *key,
|
||||
struct cache_detail *detail, struct ent **item)
|
||||
{
|
||||
struct idmap_defer_req *mdr;
|
||||
int ret;
|
||||
|
||||
mdr = kzalloc(sizeof(*mdr), GFP_KERNEL);
|
||||
if (!mdr)
|
||||
*item = lookup_fn(key);
|
||||
if (!*item)
|
||||
return -ENOMEM;
|
||||
atomic_set(&mdr->count, 1);
|
||||
init_waitqueue_head(&mdr->waitq);
|
||||
mdr->req.defer = idmap_defer;
|
||||
ret = do_idmap_lookup(lookup_fn, key, detail, item, mdr);
|
||||
if (ret == -EAGAIN) {
|
||||
wait_event_interruptible_timeout(mdr->waitq,
|
||||
test_bit(CACHE_VALID, &(*item)->h.flags), 1 * HZ);
|
||||
ret = do_idmap_lookup_nowait(lookup_fn, key, detail, item);
|
||||
retry:
|
||||
ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
struct ent *prev_item = *item;
|
||||
*item = lookup_fn(key);
|
||||
if (*item != prev_item)
|
||||
goto retry;
|
||||
cache_put(&(*item)->h, detail);
|
||||
}
|
||||
put_mdr(mdr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1031,8 +1031,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
|
|||
resp->cstate.session = NULL;
|
||||
fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
|
||||
fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
|
||||
/* Use the deferral mechanism only for NFSv4.0 compounds */
|
||||
rqstp->rq_usedeferral = (args->minorversion == 0);
|
||||
/*
|
||||
* Don't use the deferral mechanism for NFSv4; compounds make it
|
||||
* too hard to avoid non-idempotency problems.
|
||||
*/
|
||||
rqstp->rq_usedeferral = 0;
|
||||
|
||||
/*
|
||||
* According to RFC3010, this takes precedence over all other errors.
|
||||
|
|
|
|||
|
|
@ -207,7 +207,6 @@ 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_cb_conn *cb = &stp->st_stateowner->so_client->cl_cb_conn;
|
||||
|
||||
dprintk("NFSD alloc_init_deleg\n");
|
||||
/*
|
||||
|
|
@ -234,7 +233,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
|
|||
nfs4_file_get_access(fp, O_RDONLY);
|
||||
dp->dl_flock = NULL;
|
||||
dp->dl_type = type;
|
||||
dp->dl_ident = cb->cb_ident;
|
||||
dp->dl_stateid.si_boot = boot_time;
|
||||
dp->dl_stateid.si_stateownerid = current_delegid++;
|
||||
dp->dl_stateid.si_fileid = 0;
|
||||
|
|
@ -535,94 +533,6 @@ gen_sessionid(struct nfsd4_session *ses)
|
|||
*/
|
||||
#define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)
|
||||
|
||||
/*
|
||||
* Give the client the number of ca_maxresponsesize_cached slots it
|
||||
* requests, of size bounded by NFSD_SLOT_CACHE_SIZE,
|
||||
* NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more
|
||||
* than NFSD_MAX_SLOTS_PER_SESSION.
|
||||
*
|
||||
* If we run out of reserved DRC memory we should (up to a point)
|
||||
* re-negotiate active sessions and reduce their slot usage to make
|
||||
* rooom for new connections. For now we just fail the create session.
|
||||
*/
|
||||
static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan)
|
||||
{
|
||||
int mem, size = fchan->maxresp_cached;
|
||||
|
||||
if (fchan->maxreqs < 1)
|
||||
return nfserr_inval;
|
||||
|
||||
if (size < NFSD_MIN_HDR_SEQ_SZ)
|
||||
size = NFSD_MIN_HDR_SEQ_SZ;
|
||||
size -= NFSD_MIN_HDR_SEQ_SZ;
|
||||
if (size > NFSD_SLOT_CACHE_SIZE)
|
||||
size = NFSD_SLOT_CACHE_SIZE;
|
||||
|
||||
/* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */
|
||||
mem = fchan->maxreqs * size;
|
||||
if (mem > NFSD_MAX_MEM_PER_SESSION) {
|
||||
fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size;
|
||||
if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
|
||||
fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
|
||||
mem = fchan->maxreqs * size;
|
||||
}
|
||||
|
||||
spin_lock(&nfsd_drc_lock);
|
||||
/* bound the total session drc memory ussage */
|
||||
if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) {
|
||||
fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size;
|
||||
mem = fchan->maxreqs * size;
|
||||
}
|
||||
nfsd_drc_mem_used += mem;
|
||||
spin_unlock(&nfsd_drc_lock);
|
||||
|
||||
if (fchan->maxreqs == 0)
|
||||
return nfserr_jukebox;
|
||||
|
||||
fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fchan holds the client values on input, and the server values on output
|
||||
* sv_max_mesg is the maximum payload plus one page for overhead.
|
||||
*/
|
||||
static int init_forechannel_attrs(struct svc_rqst *rqstp,
|
||||
struct nfsd4_channel_attrs *session_fchan,
|
||||
struct nfsd4_channel_attrs *fchan)
|
||||
{
|
||||
int status = 0;
|
||||
__u32 maxcount = nfsd_serv->sv_max_mesg;
|
||||
|
||||
/* headerpadsz set to zero in encode routine */
|
||||
|
||||
/* Use the client's max request and max response size if possible */
|
||||
if (fchan->maxreq_sz > maxcount)
|
||||
fchan->maxreq_sz = maxcount;
|
||||
session_fchan->maxreq_sz = fchan->maxreq_sz;
|
||||
|
||||
if (fchan->maxresp_sz > maxcount)
|
||||
fchan->maxresp_sz = maxcount;
|
||||
session_fchan->maxresp_sz = fchan->maxresp_sz;
|
||||
|
||||
/* Use the client's maxops if possible */
|
||||
if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
|
||||
fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
|
||||
session_fchan->maxops = fchan->maxops;
|
||||
|
||||
/* FIXME: Error means no more DRC pages so the server should
|
||||
* recover pages from existing sessions. For now fail session
|
||||
* creation.
|
||||
*/
|
||||
status = set_forechannel_drc_size(fchan);
|
||||
|
||||
session_fchan->maxresp_cached = fchan->maxresp_cached;
|
||||
session_fchan->maxreqs = fchan->maxreqs;
|
||||
|
||||
dprintk("%s status %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
free_session_slots(struct nfsd4_session *ses)
|
||||
{
|
||||
|
|
@ -641,65 +551,240 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca)
|
|||
return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
|
||||
}
|
||||
|
||||
static int
|
||||
alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
|
||||
struct nfsd4_create_session *cses)
|
||||
static int nfsd4_sanitize_slot_size(u32 size)
|
||||
{
|
||||
struct nfsd4_session *new, tmp;
|
||||
struct nfsd4_slot *sp;
|
||||
int idx, slotsize, cachesize, i;
|
||||
int status;
|
||||
size -= NFSD_MIN_HDR_SEQ_SZ; /* We don't cache the rpc header */
|
||||
size = min_t(u32, size, NFSD_SLOT_CACHE_SIZE);
|
||||
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
return size;
|
||||
}
|
||||
|
||||
/* FIXME: For now, we just accept the client back channel attributes. */
|
||||
tmp.se_bchannel = cses->back_channel;
|
||||
status = init_forechannel_attrs(rqstp, &tmp.se_fchannel,
|
||||
&cses->fore_channel);
|
||||
if (status)
|
||||
goto out;
|
||||
/*
|
||||
* XXX: If we run out of reserved DRC memory we could (up to a point)
|
||||
* re-negotiate active sessions and reduce their slot usage to make
|
||||
* rooom for new connections. For now we just fail the create session.
|
||||
*/
|
||||
static int nfsd4_get_drc_mem(int slotsize, u32 num)
|
||||
{
|
||||
int avail;
|
||||
|
||||
BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot)
|
||||
+ sizeof(struct nfsd4_session) > PAGE_SIZE);
|
||||
num = min_t(u32, num, NFSD_MAX_SLOTS_PER_SESSION);
|
||||
|
||||
status = nfserr_jukebox;
|
||||
/* allocate struct nfsd4_session and slot table pointers in one piece */
|
||||
slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *);
|
||||
new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
|
||||
spin_lock(&nfsd_drc_lock);
|
||||
avail = min_t(int, NFSD_MAX_MEM_PER_SESSION,
|
||||
nfsd_drc_max_mem - nfsd_drc_mem_used);
|
||||
num = min_t(int, num, avail / slotsize);
|
||||
nfsd_drc_mem_used += num * slotsize;
|
||||
spin_unlock(&nfsd_drc_lock);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void nfsd4_put_drc_mem(int slotsize, int num)
|
||||
{
|
||||
spin_lock(&nfsd_drc_lock);
|
||||
nfsd_drc_mem_used -= slotsize * num;
|
||||
spin_unlock(&nfsd_drc_lock);
|
||||
}
|
||||
|
||||
static struct nfsd4_session *alloc_session(int slotsize, int numslots)
|
||||
{
|
||||
struct nfsd4_session *new;
|
||||
int mem, i;
|
||||
|
||||
BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
|
||||
+ sizeof(struct nfsd4_session) > PAGE_SIZE);
|
||||
mem = numslots * sizeof(struct nfsd4_slot *);
|
||||
|
||||
new = kzalloc(sizeof(*new) + mem, GFP_KERNEL);
|
||||
if (!new)
|
||||
goto out;
|
||||
|
||||
memcpy(new, &tmp, sizeof(*new));
|
||||
|
||||
return NULL;
|
||||
/* allocate each struct nfsd4_slot and data cache in one piece */
|
||||
cachesize = slot_bytes(&new->se_fchannel);
|
||||
for (i = 0; i < new->se_fchannel.maxreqs; i++) {
|
||||
sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL);
|
||||
if (!sp)
|
||||
for (i = 0; i < numslots; i++) {
|
||||
mem = sizeof(struct nfsd4_slot) + slotsize;
|
||||
new->se_slots[i] = kzalloc(mem, GFP_KERNEL);
|
||||
if (!new->se_slots[i])
|
||||
goto out_free;
|
||||
new->se_slots[i] = sp;
|
||||
}
|
||||
return new;
|
||||
out_free:
|
||||
while (i--)
|
||||
kfree(new->se_slots[i]);
|
||||
kfree(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize)
|
||||
{
|
||||
u32 maxrpc = nfsd_serv->sv_max_mesg;
|
||||
|
||||
new->maxreqs = numslots;
|
||||
new->maxresp_cached = slotsize + NFSD_MIN_HDR_SEQ_SZ;
|
||||
new->maxreq_sz = min_t(u32, req->maxreq_sz, maxrpc);
|
||||
new->maxresp_sz = min_t(u32, req->maxresp_sz, maxrpc);
|
||||
new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
|
||||
}
|
||||
|
||||
static void free_conn(struct nfsd4_conn *c)
|
||||
{
|
||||
svc_xprt_put(c->cn_xprt);
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static void nfsd4_conn_lost(struct svc_xpt_user *u)
|
||||
{
|
||||
struct nfsd4_conn *c = container_of(u, struct nfsd4_conn, cn_xpt_user);
|
||||
struct nfs4_client *clp = c->cn_session->se_client;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
if (!list_empty(&c->cn_persession)) {
|
||||
list_del(&c->cn_persession);
|
||||
free_conn(c);
|
||||
}
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
|
||||
static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags)
|
||||
{
|
||||
struct nfsd4_conn *conn;
|
||||
|
||||
conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
svc_xprt_get(rqstp->rq_xprt);
|
||||
conn->cn_xprt = rqstp->rq_xprt;
|
||||
conn->cn_flags = flags;
|
||||
INIT_LIST_HEAD(&conn->cn_xpt_user.list);
|
||||
return conn;
|
||||
}
|
||||
|
||||
static void __nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
|
||||
{
|
||||
conn->cn_session = ses;
|
||||
list_add(&conn->cn_persession, &ses->se_conns);
|
||||
}
|
||||
|
||||
static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
|
||||
{
|
||||
struct nfs4_client *clp = ses->se_client;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
__nfsd4_hash_conn(conn, ses);
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
|
||||
static void nfsd4_register_conn(struct nfsd4_conn *conn)
|
||||
{
|
||||
conn->cn_xpt_user.callback = nfsd4_conn_lost;
|
||||
register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
|
||||
}
|
||||
|
||||
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
|
||||
{
|
||||
struct nfsd4_conn *conn;
|
||||
u32 flags = NFS4_CDFC4_FORE;
|
||||
|
||||
if (ses->se_flags & SESSION4_BACK_CHAN)
|
||||
flags |= NFS4_CDFC4_BACK;
|
||||
conn = alloc_conn(rqstp, flags);
|
||||
if (!conn)
|
||||
return nfserr_jukebox;
|
||||
nfsd4_hash_conn(conn, ses);
|
||||
nfsd4_register_conn(conn);
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
static void nfsd4_del_conns(struct nfsd4_session *s)
|
||||
{
|
||||
struct nfs4_client *clp = s->se_client;
|
||||
struct nfsd4_conn *c;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
while (!list_empty(&s->se_conns)) {
|
||||
c = list_first_entry(&s->se_conns, struct nfsd4_conn, cn_persession);
|
||||
list_del_init(&c->cn_persession);
|
||||
spin_unlock(&clp->cl_lock);
|
||||
|
||||
unregister_xpt_user(c->cn_xprt, &c->cn_xpt_user);
|
||||
free_conn(c);
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
}
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
|
||||
void free_session(struct kref *kref)
|
||||
{
|
||||
struct nfsd4_session *ses;
|
||||
int mem;
|
||||
|
||||
ses = container_of(kref, struct nfsd4_session, se_ref);
|
||||
nfsd4_del_conns(ses);
|
||||
spin_lock(&nfsd_drc_lock);
|
||||
mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
|
||||
nfsd_drc_mem_used -= mem;
|
||||
spin_unlock(&nfsd_drc_lock);
|
||||
free_session_slots(ses);
|
||||
kfree(ses);
|
||||
}
|
||||
|
||||
static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
|
||||
{
|
||||
struct nfsd4_session *new;
|
||||
struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
|
||||
int numslots, slotsize;
|
||||
int status;
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* Note decreasing slot size below client's request may
|
||||
* make it difficult for client to function correctly, whereas
|
||||
* decreasing the number of slots will (just?) affect
|
||||
* performance. When short on memory we therefore prefer to
|
||||
* decrease number of slots instead of their size.
|
||||
*/
|
||||
slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached);
|
||||
numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs);
|
||||
|
||||
new = alloc_session(slotsize, numslots);
|
||||
if (!new) {
|
||||
nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
|
||||
return NULL;
|
||||
}
|
||||
init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);
|
||||
|
||||
new->se_client = clp;
|
||||
gen_sessionid(new);
|
||||
idx = hash_sessionid(&new->se_sessionid);
|
||||
memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
|
||||
NFS4_MAX_SESSIONID_LEN);
|
||||
|
||||
INIT_LIST_HEAD(&new->se_conns);
|
||||
|
||||
new->se_cb_seq_nr = 1;
|
||||
new->se_flags = cses->flags;
|
||||
new->se_cb_prog = cses->callback_prog;
|
||||
kref_init(&new->se_ref);
|
||||
idx = hash_sessionid(&new->se_sessionid);
|
||||
spin_lock(&client_lock);
|
||||
list_add(&new->se_hash, &sessionid_hashtbl[idx]);
|
||||
list_add(&new->se_perclnt, &clp->cl_sessions);
|
||||
spin_unlock(&client_lock);
|
||||
|
||||
status = nfs_ok;
|
||||
out:
|
||||
return status;
|
||||
out_free:
|
||||
free_session_slots(new);
|
||||
kfree(new);
|
||||
goto out;
|
||||
status = nfsd4_new_conn(rqstp, new);
|
||||
/* whoops: benny points out, status is ignored! (err, or bogus) */
|
||||
if (status) {
|
||||
free_session(&new->se_ref);
|
||||
return NULL;
|
||||
}
|
||||
if (!clp->cl_cb_session && (cses->flags & SESSION4_BACK_CHAN)) {
|
||||
struct sockaddr *sa = svc_addr(rqstp);
|
||||
|
||||
clp->cl_cb_session = new;
|
||||
clp->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
|
||||
svc_xprt_get(rqstp->rq_xprt);
|
||||
rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
|
||||
clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
|
||||
nfsd4_probe_callback(clp);
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
/* caller must hold client_lock */
|
||||
|
|
@ -731,21 +816,6 @@ unhash_session(struct nfsd4_session *ses)
|
|||
list_del(&ses->se_perclnt);
|
||||
}
|
||||
|
||||
void
|
||||
free_session(struct kref *kref)
|
||||
{
|
||||
struct nfsd4_session *ses;
|
||||
int mem;
|
||||
|
||||
ses = container_of(kref, struct nfsd4_session, se_ref);
|
||||
spin_lock(&nfsd_drc_lock);
|
||||
mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
|
||||
nfsd_drc_mem_used -= mem;
|
||||
spin_unlock(&nfsd_drc_lock);
|
||||
free_session_slots(ses);
|
||||
kfree(ses);
|
||||
}
|
||||
|
||||
/* must be called under the client_lock */
|
||||
static inline void
|
||||
renew_client_locked(struct nfs4_client *clp)
|
||||
|
|
@ -812,6 +882,13 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
|
|||
static inline void
|
||||
free_client(struct nfs4_client *clp)
|
||||
{
|
||||
while (!list_empty(&clp->cl_sessions)) {
|
||||
struct nfsd4_session *ses;
|
||||
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
|
||||
se_perclnt);
|
||||
list_del(&ses->se_perclnt);
|
||||
nfsd4_put_session(ses);
|
||||
}
|
||||
if (clp->cl_cred.cr_group_info)
|
||||
put_group_info(clp->cl_cred.cr_group_info);
|
||||
kfree(clp->cl_principal);
|
||||
|
|
@ -838,15 +915,12 @@ release_session_client(struct nfsd4_session *session)
|
|||
static inline void
|
||||
unhash_client_locked(struct nfs4_client *clp)
|
||||
{
|
||||
struct nfsd4_session *ses;
|
||||
|
||||
mark_client_expired(clp);
|
||||
list_del(&clp->cl_lru);
|
||||
while (!list_empty(&clp->cl_sessions)) {
|
||||
struct nfsd4_session *ses;
|
||||
ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
|
||||
se_perclnt);
|
||||
unhash_session(ses);
|
||||
nfsd4_put_session(ses);
|
||||
}
|
||||
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
|
||||
list_del_init(&ses->se_hash);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -875,7 +949,7 @@ expire_client(struct nfs4_client *clp)
|
|||
sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient);
|
||||
release_openowner(sop);
|
||||
}
|
||||
nfsd4_set_callback_client(clp, NULL);
|
||||
nfsd4_shutdown_callback(clp);
|
||||
if (clp->cl_cb_conn.cb_xprt)
|
||||
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
|
||||
list_del(&clp->cl_idhash);
|
||||
|
|
@ -960,6 +1034,8 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
|
|||
if (clp == NULL)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&clp->cl_sessions);
|
||||
|
||||
princ = svc_gss_principal(rqstp);
|
||||
if (princ) {
|
||||
clp->cl_principal = kstrdup(princ, GFP_KERNEL);
|
||||
|
|
@ -976,8 +1052,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
|
|||
INIT_LIST_HEAD(&clp->cl_strhash);
|
||||
INIT_LIST_HEAD(&clp->cl_openowners);
|
||||
INIT_LIST_HEAD(&clp->cl_delegations);
|
||||
INIT_LIST_HEAD(&clp->cl_sessions);
|
||||
INIT_LIST_HEAD(&clp->cl_lru);
|
||||
spin_lock_init(&clp->cl_lock);
|
||||
INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
|
||||
clp->cl_time = get_seconds();
|
||||
clear_bit(0, &clp->cl_cb_slot_busy);
|
||||
rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
|
||||
|
|
@ -986,7 +1063,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
|
|||
clp->cl_flavor = rqstp->rq_flavor;
|
||||
copy_cred(&clp->cl_cred, &rqstp->rq_cred);
|
||||
gen_confirm(clp);
|
||||
|
||||
clp->cl_cb_session = NULL;
|
||||
return clp;
|
||||
}
|
||||
|
||||
|
|
@ -1098,7 +1175,7 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval,
|
|||
static void
|
||||
gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
|
||||
{
|
||||
struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
|
||||
struct nfs4_cb_conn *conn = &clp->cl_cb_conn;
|
||||
unsigned short expected_family;
|
||||
|
||||
/* Currently, we only support tcp and tcp6 for the callback channel */
|
||||
|
|
@ -1111,24 +1188,23 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
|
|||
else
|
||||
goto out_err;
|
||||
|
||||
cb->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
|
||||
conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
|
||||
se->se_callback_addr_len,
|
||||
(struct sockaddr *) &cb->cb_addr,
|
||||
sizeof(cb->cb_addr));
|
||||
(struct sockaddr *)&conn->cb_addr,
|
||||
sizeof(conn->cb_addr));
|
||||
|
||||
if (!cb->cb_addrlen || cb->cb_addr.ss_family != expected_family)
|
||||
if (!conn->cb_addrlen || conn->cb_addr.ss_family != expected_family)
|
||||
goto out_err;
|
||||
|
||||
if (cb->cb_addr.ss_family == AF_INET6)
|
||||
((struct sockaddr_in6 *) &cb->cb_addr)->sin6_scope_id = scopeid;
|
||||
if (conn->cb_addr.ss_family == AF_INET6)
|
||||
((struct sockaddr_in6 *)&conn->cb_addr)->sin6_scope_id = scopeid;
|
||||
|
||||
cb->cb_minorversion = 0;
|
||||
cb->cb_prog = se->se_callback_prog;
|
||||
cb->cb_ident = se->se_callback_ident;
|
||||
conn->cb_prog = se->se_callback_prog;
|
||||
conn->cb_ident = se->se_callback_ident;
|
||||
return;
|
||||
out_err:
|
||||
cb->cb_addr.ss_family = AF_UNSPEC;
|
||||
cb->cb_addrlen = 0;
|
||||
conn->cb_addr.ss_family = AF_UNSPEC;
|
||||
conn->cb_addrlen = 0;
|
||||
dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
|
||||
"will not receive delegations\n",
|
||||
clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
|
||||
|
|
@ -1415,7 +1491,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
{
|
||||
struct sockaddr *sa = svc_addr(rqstp);
|
||||
struct nfs4_client *conf, *unconf;
|
||||
struct nfsd4_session *new;
|
||||
struct nfsd4_clid_slot *cs_slot = NULL;
|
||||
bool confirm_me = false;
|
||||
int status = 0;
|
||||
|
||||
nfs4_lock_state();
|
||||
|
|
@ -1438,7 +1516,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
cs_slot->sl_seqid, cr_ses->seqid);
|
||||
goto out;
|
||||
}
|
||||
cs_slot->sl_seqid++;
|
||||
} else if (unconf) {
|
||||
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
|
||||
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
|
||||
|
|
@ -1451,48 +1528,41 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
if (status) {
|
||||
/* an unconfirmed replay returns misordered */
|
||||
status = nfserr_seq_misordered;
|
||||
goto out_cache;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cs_slot->sl_seqid++; /* from 0 to 1 */
|
||||
move_to_confirmed(unconf);
|
||||
|
||||
if (cr_ses->flags & SESSION4_BACK_CHAN) {
|
||||
unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
|
||||
svc_xprt_get(rqstp->rq_xprt);
|
||||
rpc_copy_addr(
|
||||
(struct sockaddr *)&unconf->cl_cb_conn.cb_addr,
|
||||
sa);
|
||||
unconf->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
|
||||
unconf->cl_cb_conn.cb_minorversion =
|
||||
cstate->minorversion;
|
||||
unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog;
|
||||
unconf->cl_cb_seq_nr = 1;
|
||||
nfsd4_probe_callback(unconf, &unconf->cl_cb_conn);
|
||||
}
|
||||
confirm_me = true;
|
||||
conf = unconf;
|
||||
} else {
|
||||
status = nfserr_stale_clientid;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: we should probably set this at creation time, and check
|
||||
* for consistent minorversion use throughout:
|
||||
*/
|
||||
conf->cl_minorversion = 1;
|
||||
/*
|
||||
* We do not support RDMA or persistent sessions
|
||||
*/
|
||||
cr_ses->flags &= ~SESSION4_PERSIST;
|
||||
cr_ses->flags &= ~SESSION4_RDMA;
|
||||
|
||||
status = alloc_init_session(rqstp, conf, cr_ses);
|
||||
if (status)
|
||||
status = nfserr_jukebox;
|
||||
new = alloc_init_session(rqstp, conf, cr_ses);
|
||||
if (!new)
|
||||
goto out;
|
||||
|
||||
memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data,
|
||||
status = nfs_ok;
|
||||
memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
|
||||
NFS4_MAX_SESSIONID_LEN);
|
||||
cs_slot->sl_seqid++;
|
||||
cr_ses->seqid = cs_slot->sl_seqid;
|
||||
|
||||
out_cache:
|
||||
/* cache solo and embedded create sessions under the state lock */
|
||||
nfsd4_cache_create_session(cr_ses, cs_slot, status);
|
||||
if (confirm_me)
|
||||
move_to_confirmed(conf);
|
||||
out:
|
||||
nfs4_unlock_state();
|
||||
dprintk("%s returns %d\n", __func__, ntohl(status));
|
||||
|
|
@ -1546,8 +1616,11 @@ nfsd4_destroy_session(struct svc_rqst *r,
|
|||
|
||||
nfs4_lock_state();
|
||||
/* wait for callbacks */
|
||||
nfsd4_set_callback_client(ses->se_client, NULL);
|
||||
nfsd4_shutdown_callback(ses->se_client);
|
||||
nfs4_unlock_state();
|
||||
|
||||
nfsd4_del_conns(ses);
|
||||
|
||||
nfsd4_put_session(ses);
|
||||
status = nfs_ok;
|
||||
out:
|
||||
|
|
@ -1555,6 +1628,36 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_session *s)
|
||||
{
|
||||
struct nfsd4_conn *c;
|
||||
|
||||
list_for_each_entry(c, &s->se_conns, cn_persession) {
|
||||
if (c->cn_xprt == xpt) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
|
||||
{
|
||||
struct nfs4_client *clp = ses->se_client;
|
||||
struct nfsd4_conn *c;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
c = __nfsd4_find_conn(new->cn_xprt, ses);
|
||||
if (c) {
|
||||
spin_unlock(&clp->cl_lock);
|
||||
free_conn(new);
|
||||
return;
|
||||
}
|
||||
__nfsd4_hash_conn(new, ses);
|
||||
spin_unlock(&clp->cl_lock);
|
||||
nfsd4_register_conn(new);
|
||||
return;
|
||||
}
|
||||
|
||||
__be32
|
||||
nfsd4_sequence(struct svc_rqst *rqstp,
|
||||
struct nfsd4_compound_state *cstate,
|
||||
|
|
@ -1563,11 +1666,20 @@ nfsd4_sequence(struct svc_rqst *rqstp,
|
|||
struct nfsd4_compoundres *resp = rqstp->rq_resp;
|
||||
struct nfsd4_session *session;
|
||||
struct nfsd4_slot *slot;
|
||||
struct nfsd4_conn *conn;
|
||||
int status;
|
||||
|
||||
if (resp->opcnt != 1)
|
||||
return nfserr_sequence_pos;
|
||||
|
||||
/*
|
||||
* Will be either used or freed by nfsd4_sequence_check_conn
|
||||
* below.
|
||||
*/
|
||||
conn = alloc_conn(rqstp, NFS4_CDFC4_FORE);
|
||||
if (!conn)
|
||||
return nfserr_jukebox;
|
||||
|
||||
spin_lock(&client_lock);
|
||||
status = nfserr_badsession;
|
||||
session = find_in_sessionid_hashtbl(&seq->sessionid);
|
||||
|
|
@ -1599,6 +1711,9 @@ nfsd4_sequence(struct svc_rqst *rqstp,
|
|||
if (status)
|
||||
goto out;
|
||||
|
||||
nfsd4_sequence_check_conn(conn, session);
|
||||
conn = NULL;
|
||||
|
||||
/* Success! bump slot seqid */
|
||||
slot->sl_inuse = true;
|
||||
slot->sl_seqid = seq->seqid;
|
||||
|
|
@ -1613,6 +1728,7 @@ out:
|
|||
nfsd4_get_session(cstate->session);
|
||||
atomic_inc(&session->se_client->cl_refcount);
|
||||
}
|
||||
kfree(conn);
|
||||
spin_unlock(&client_lock);
|
||||
dprintk("%s: return %d\n", __func__, ntohl(status));
|
||||
return status;
|
||||
|
|
@ -1747,6 +1863,11 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
goto out;
|
||||
gen_clid(new);
|
||||
}
|
||||
/*
|
||||
* XXX: we should probably set this at creation time, and check
|
||||
* for consistent minorversion use throughout:
|
||||
*/
|
||||
new->cl_minorversion = 0;
|
||||
gen_callback(new, setclid, rpc_get_scope_id(sa));
|
||||
add_to_unconfirmed(new, strhashval);
|
||||
setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
|
||||
|
|
@ -1807,7 +1928,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
|||
status = nfserr_clid_inuse;
|
||||
else {
|
||||
atomic_set(&conf->cl_cb_set, 0);
|
||||
nfsd4_probe_callback(conf, &unconf->cl_cb_conn);
|
||||
nfsd4_change_callback(conf, &unconf->cl_cb_conn);
|
||||
nfsd4_probe_callback(conf);
|
||||
expire_client(unconf);
|
||||
status = nfs_ok;
|
||||
|
||||
|
|
@ -1841,7 +1963,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
|||
}
|
||||
move_to_confirmed(unconf);
|
||||
conf = unconf;
|
||||
nfsd4_probe_callback(conf, &conf->cl_cb_conn);
|
||||
nfsd4_probe_callback(conf);
|
||||
status = nfs_ok;
|
||||
}
|
||||
} else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
|
||||
|
|
@ -2944,7 +3066,11 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
|
|||
if (STALE_STATEID(stateid))
|
||||
goto out;
|
||||
|
||||
status = nfserr_bad_stateid;
|
||||
/*
|
||||
* We assume that any stateid that has the current boot time,
|
||||
* but that we can't find, is expired:
|
||||
*/
|
||||
status = nfserr_expired;
|
||||
if (is_delegation_stateid(stateid)) {
|
||||
dp = find_delegation_stateid(ino, stateid);
|
||||
if (!dp)
|
||||
|
|
@ -2964,6 +3090,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
|
|||
stp = find_stateid(stateid, flags);
|
||||
if (!stp)
|
||||
goto out;
|
||||
status = nfserr_bad_stateid;
|
||||
if (nfs4_check_fh(current_fh, stp))
|
||||
goto out;
|
||||
if (!stp->st_stateowner->so_confirmed)
|
||||
|
|
@ -3038,8 +3165,9 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
|
|||
* a replayed close:
|
||||
*/
|
||||
sop = search_close_lru(stateid->si_stateownerid, flags);
|
||||
/* It's not stale; let's assume it's expired: */
|
||||
if (sop == NULL)
|
||||
return nfserr_bad_stateid;
|
||||
return nfserr_expired;
|
||||
*sopp = sop;
|
||||
goto check_replay;
|
||||
}
|
||||
|
|
@ -3304,6 +3432,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
status = nfserr_bad_stateid;
|
||||
if (!is_delegation_stateid(stateid))
|
||||
goto out;
|
||||
status = nfserr_expired;
|
||||
dp = find_delegation_stateid(inode, stateid);
|
||||
if (!dp)
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -1805,19 +1805,23 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
|||
goto out_nfserr;
|
||||
}
|
||||
}
|
||||
if ((buflen -= 16) < 0)
|
||||
goto out_resource;
|
||||
|
||||
if (unlikely(bmval2)) {
|
||||
if (bmval2) {
|
||||
if ((buflen -= 16) < 0)
|
||||
goto out_resource;
|
||||
WRITE32(3);
|
||||
WRITE32(bmval0);
|
||||
WRITE32(bmval1);
|
||||
WRITE32(bmval2);
|
||||
} else if (likely(bmval1)) {
|
||||
} else if (bmval1) {
|
||||
if ((buflen -= 12) < 0)
|
||||
goto out_resource;
|
||||
WRITE32(2);
|
||||
WRITE32(bmval0);
|
||||
WRITE32(bmval1);
|
||||
} else {
|
||||
if ((buflen -= 8) < 0)
|
||||
goto out_resource;
|
||||
WRITE32(1);
|
||||
WRITE32(bmval0);
|
||||
}
|
||||
|
|
@ -1828,15 +1832,17 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
|||
u32 word1 = nfsd_suppattrs1(minorversion);
|
||||
u32 word2 = nfsd_suppattrs2(minorversion);
|
||||
|
||||
if ((buflen -= 12) < 0)
|
||||
goto out_resource;
|
||||
if (!aclsupport)
|
||||
word0 &= ~FATTR4_WORD0_ACL;
|
||||
if (!word2) {
|
||||
if ((buflen -= 12) < 0)
|
||||
goto out_resource;
|
||||
WRITE32(2);
|
||||
WRITE32(word0);
|
||||
WRITE32(word1);
|
||||
} else {
|
||||
if ((buflen -= 16) < 0)
|
||||
goto out_resource;
|
||||
WRITE32(3);
|
||||
WRITE32(word0);
|
||||
WRITE32(word1);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
enum {
|
||||
NFSD_Root = 1,
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
NFSD_Svc,
|
||||
NFSD_Add,
|
||||
NFSD_Del,
|
||||
|
|
@ -29,6 +30,7 @@ enum {
|
|||
NFSD_Unexport,
|
||||
NFSD_Getfd,
|
||||
NFSD_Getfs,
|
||||
#endif
|
||||
NFSD_List,
|
||||
NFSD_Export_features,
|
||||
NFSD_Fh,
|
||||
|
|
@ -54,6 +56,7 @@ enum {
|
|||
/*
|
||||
* write() for these nodes.
|
||||
*/
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
static ssize_t write_svc(struct file *file, char *buf, size_t size);
|
||||
static ssize_t write_add(struct file *file, char *buf, size_t size);
|
||||
static ssize_t write_del(struct file *file, char *buf, size_t size);
|
||||
|
|
@ -61,6 +64,7 @@ static ssize_t write_export(struct file *file, char *buf, size_t size);
|
|||
static ssize_t write_unexport(struct file *file, char *buf, size_t size);
|
||||
static ssize_t write_getfd(struct file *file, char *buf, size_t size);
|
||||
static ssize_t write_getfs(struct file *file, char *buf, size_t size);
|
||||
#endif
|
||||
static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
|
||||
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
|
||||
static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
|
||||
|
|
@ -76,6 +80,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
|
|||
#endif
|
||||
|
||||
static ssize_t (*write_op[])(struct file *, char *, size_t) = {
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
[NFSD_Svc] = write_svc,
|
||||
[NFSD_Add] = write_add,
|
||||
[NFSD_Del] = write_del,
|
||||
|
|
@ -83,6 +88,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
|
|||
[NFSD_Unexport] = write_unexport,
|
||||
[NFSD_Getfd] = write_getfd,
|
||||
[NFSD_Getfs] = write_getfs,
|
||||
#endif
|
||||
[NFSD_Fh] = write_filehandle,
|
||||
[NFSD_FO_UnlockIP] = write_unlock_ip,
|
||||
[NFSD_FO_UnlockFS] = write_unlock_fs,
|
||||
|
|
@ -121,6 +127,14 @@ static ssize_t nfsctl_transaction_write(struct file *file, const char __user *bu
|
|||
|
||||
static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
|
||||
{
|
||||
static int warned;
|
||||
if (file->f_dentry->d_name.name[0] == '.' && !warned) {
|
||||
printk(KERN_INFO
|
||||
"Warning: \"%s\" uses deprecated NFSD interface: %s."
|
||||
" This will be removed in 2.6.40\n",
|
||||
current->comm, file->f_dentry->d_name.name);
|
||||
warned = 1;
|
||||
}
|
||||
if (! file->private_data) {
|
||||
/* An attempt to read a transaction file without writing
|
||||
* causes a 0-byte write so that the file can return
|
||||
|
|
@ -187,6 +201,7 @@ static const struct file_operations pool_stats_operations = {
|
|||
* payload - write methods
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
/**
|
||||
* write_svc - Start kernel's NFSD server
|
||||
*
|
||||
|
|
@ -402,7 +417,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
|
|||
|
||||
ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
|
||||
|
||||
clp = auth_unix_lookup(&in6);
|
||||
clp = auth_unix_lookup(&init_net, &in6);
|
||||
if (!clp)
|
||||
err = -EPERM;
|
||||
else {
|
||||
|
|
@ -465,7 +480,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
|
|||
|
||||
ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
|
||||
|
||||
clp = auth_unix_lookup(&in6);
|
||||
clp = auth_unix_lookup(&init_net, &in6);
|
||||
if (!clp)
|
||||
err = -EPERM;
|
||||
else {
|
||||
|
|
@ -482,6 +497,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
|
|||
out:
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_NFSD_DEPRECATED */
|
||||
|
||||
/**
|
||||
* write_unlock_ip - Release all locks used by a client
|
||||
|
|
@ -1000,12 +1016,12 @@ static ssize_t __write_ports_addxprt(char *buf)
|
|||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = svc_create_xprt(nfsd_serv, transport,
|
||||
err = svc_create_xprt(nfsd_serv, transport, &init_net,
|
||||
PF_INET, port, SVC_SOCK_ANONYMOUS);
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
|
||||
err = svc_create_xprt(nfsd_serv, transport,
|
||||
err = svc_create_xprt(nfsd_serv, transport, &init_net,
|
||||
PF_INET6, port, SVC_SOCK_ANONYMOUS);
|
||||
if (err < 0 && err != -EAFNOSUPPORT)
|
||||
goto out_close;
|
||||
|
|
@ -1356,6 +1372,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
|
|||
static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
|
||||
{
|
||||
static struct tree_descr nfsd_files[] = {
|
||||
#ifdef CONFIG_NFSD_DEPRECATED
|
||||
[NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
|
||||
[NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
|
||||
[NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
|
||||
|
|
@ -1363,6 +1380,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
|
|||
[NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
|
||||
[NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
|
||||
[NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
|
||||
#endif
|
||||
[NFSD_List] = {"exports", &exports_operations, S_IRUGO},
|
||||
[NFSD_Export_features] = {"export_features",
|
||||
&export_features_operations, S_IRUGO},
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ extern time_t nfsd4_grace;
|
|||
#define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */
|
||||
#define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */
|
||||
|
||||
#define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */
|
||||
#define NFSD_LAUNDROMAT_MINTIMEOUT 1 /* seconds */
|
||||
|
||||
/*
|
||||
* The following attributes are currently not supported by the NFSv4 server:
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/lockd/bind.h>
|
||||
#include <linux/nfsacl.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include "nfsd.h"
|
||||
#include "cache.h"
|
||||
#include "vfs.h"
|
||||
|
|
@ -186,12 +187,12 @@ static int nfsd_init_socks(int port)
|
|||
if (!list_empty(&nfsd_serv->sv_permsocks))
|
||||
return 0;
|
||||
|
||||
error = svc_create_xprt(nfsd_serv, "udp", PF_INET, port,
|
||||
error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, port,
|
||||
SVC_SOCK_DEFAULTS);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = svc_create_xprt(nfsd_serv, "tcp", PF_INET, port,
|
||||
error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, port,
|
||||
SVC_SOCK_DEFAULTS);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#ifndef _NFSD4_STATE_H
|
||||
#define _NFSD4_STATE_H
|
||||
|
||||
#include <linux/sunrpc/svc_xprt.h>
|
||||
#include <linux/nfsd/nfsfh.h>
|
||||
#include "nfsfh.h"
|
||||
|
||||
|
|
@ -64,19 +65,12 @@ typedef struct {
|
|||
(s)->si_fileid, \
|
||||
(s)->si_generation
|
||||
|
||||
struct nfsd4_cb_sequence {
|
||||
/* args/res */
|
||||
u32 cbs_minorversion;
|
||||
struct nfs4_client *cbs_clp;
|
||||
};
|
||||
|
||||
struct nfs4_rpc_args {
|
||||
void *args_op;
|
||||
struct nfsd4_cb_sequence args_seq;
|
||||
};
|
||||
|
||||
struct nfsd4_callback {
|
||||
struct nfs4_rpc_args cb_args;
|
||||
void *cb_op;
|
||||
struct nfs4_client *cb_clp;
|
||||
u32 cb_minorversion;
|
||||
struct rpc_message cb_msg;
|
||||
const struct rpc_call_ops *cb_ops;
|
||||
struct work_struct cb_work;
|
||||
};
|
||||
|
||||
|
|
@ -91,7 +85,6 @@ struct nfs4_delegation {
|
|||
u32 dl_type;
|
||||
time_t dl_time;
|
||||
/* For recall: */
|
||||
u32 dl_ident;
|
||||
stateid_t dl_stateid;
|
||||
struct knfsd_fh dl_fh;
|
||||
int dl_retries;
|
||||
|
|
@ -103,8 +96,8 @@ struct nfs4_cb_conn {
|
|||
/* SETCLIENTID info */
|
||||
struct sockaddr_storage cb_addr;
|
||||
size_t cb_addrlen;
|
||||
u32 cb_prog;
|
||||
u32 cb_minorversion;
|
||||
u32 cb_prog; /* used only in 4.0 case;
|
||||
per-session otherwise */
|
||||
u32 cb_ident; /* minorversion 0 only */
|
||||
struct svc_xprt *cb_xprt; /* minorversion 1 only */
|
||||
};
|
||||
|
|
@ -160,6 +153,15 @@ struct nfsd4_clid_slot {
|
|||
struct nfsd4_create_session sl_cr_ses;
|
||||
};
|
||||
|
||||
struct nfsd4_conn {
|
||||
struct list_head cn_persession;
|
||||
struct svc_xprt *cn_xprt;
|
||||
struct svc_xpt_user cn_xpt_user;
|
||||
struct nfsd4_session *cn_session;
|
||||
/* CDFC4_FORE, CDFC4_BACK: */
|
||||
unsigned char cn_flags;
|
||||
};
|
||||
|
||||
struct nfsd4_session {
|
||||
struct kref se_ref;
|
||||
struct list_head se_hash; /* hash by sessionid */
|
||||
|
|
@ -169,6 +171,9 @@ struct nfsd4_session {
|
|||
struct nfs4_sessionid se_sessionid;
|
||||
struct nfsd4_channel_attrs se_fchannel;
|
||||
struct nfsd4_channel_attrs se_bchannel;
|
||||
struct list_head se_conns;
|
||||
u32 se_cb_prog;
|
||||
u32 se_cb_seq_nr;
|
||||
struct nfsd4_slot *se_slots[]; /* forward channel slots */
|
||||
};
|
||||
|
||||
|
|
@ -221,24 +226,32 @@ struct nfs4_client {
|
|||
clientid_t cl_clientid; /* generated by server */
|
||||
nfs4_verifier cl_confirm; /* generated by server */
|
||||
u32 cl_firststate; /* recovery dir creation */
|
||||
u32 cl_minorversion;
|
||||
|
||||
/* for v4.0 and v4.1 callbacks: */
|
||||
struct nfs4_cb_conn cl_cb_conn;
|
||||
#define NFSD4_CLIENT_CB_UPDATE 1
|
||||
#define NFSD4_CLIENT_KILL 2
|
||||
unsigned long cl_cb_flags;
|
||||
struct rpc_clnt *cl_cb_client;
|
||||
u32 cl_cb_ident;
|
||||
atomic_t cl_cb_set;
|
||||
struct nfsd4_callback cl_cb_null;
|
||||
struct nfsd4_session *cl_cb_session;
|
||||
|
||||
/* for all client information that callback code might need: */
|
||||
spinlock_t cl_lock;
|
||||
|
||||
/* for nfs41 */
|
||||
struct list_head cl_sessions;
|
||||
struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */
|
||||
u32 cl_exchange_flags;
|
||||
struct nfs4_sessionid cl_sessionid;
|
||||
/* number of rpc's in progress over an associated session: */
|
||||
atomic_t cl_refcount;
|
||||
|
||||
/* for nfs41 callbacks */
|
||||
/* We currently support a single back channel with a single slot */
|
||||
unsigned long cl_cb_slot_busy;
|
||||
u32 cl_cb_seq_nr;
|
||||
struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */
|
||||
/* wait here for slots */
|
||||
};
|
||||
|
|
@ -440,12 +453,13 @@ extern int nfs4_in_grace(void);
|
|||
extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
|
||||
extern void nfs4_free_stateowner(struct kref *kref);
|
||||
extern int set_callback_cred(void);
|
||||
extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
|
||||
extern void nfsd4_probe_callback(struct nfs4_client *clp);
|
||||
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
|
||||
extern void nfsd4_do_callback_rpc(struct work_struct *);
|
||||
extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
|
||||
extern int nfsd4_create_callback_queue(void);
|
||||
extern void nfsd4_destroy_callback_queue(void);
|
||||
extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *);
|
||||
extern void nfsd4_shutdown_callback(struct nfs4_client *);
|
||||
extern void nfs4_put_delegation(struct nfs4_delegation *dp);
|
||||
extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
|
||||
extern void nfsd4_init_recdir(char *recdir_name);
|
||||
|
|
|
|||
Loading…
Reference in a new issue