Merge branch 'for-2.6.38' of git://linux-nfs.org/~bfields/linux

* 'for-2.6.38' of git://linux-nfs.org/~bfields/linux: (62 commits)
  nfsd4: fix callback restarting
  nfsd: break lease on unlink, link, and rename
  nfsd4: break lease on nfsd setattr
  nfsd: don't support msnfs export option
  nfsd4: initialize cb_per_client
  nfsd4: allow restarting callbacks
  nfsd4: simplify nfsd4_cb_prepare
  nfsd4: give out delegations more quickly in 4.1 case
  nfsd4: add helper function to run callbacks
  nfsd4: make sure sequence flags are set after destroy_session
  nfsd4: re-probe callback on connection loss
  nfsd4: set sequence flag when backchannel is down
  nfsd4: keep finer-grained callback status
  rpc: allow xprt_class->setup to return a preexisting xprt
  rpc: keep backchannel xprt as long as server connection
  rpc: move sk_bc_xprt to svc_xprt
  nfsd4: allow backchannel recovery
  nfsd4: support BIND_CONN_TO_SESSION
  nfsd4: modify session list under cl_lock
  Documentation: fl_mylease no longer exists
  ...

Fix up conflicts in fs/nfsd/vfs.c with the vfs-scale work.  The
vfs-scale work touched some msnfs cases, and this merge removes support
for that entirely, so the conflict was trivial to resolve.
This commit is contained in:
Linus Torvalds 2011-01-14 13:17:26 -08:00
commit 18bce371ae
38 changed files with 608 additions and 383 deletions

View File

@ -343,7 +343,6 @@ prototypes:
int (*fl_grant)(struct file_lock *, struct file_lock *, int); int (*fl_grant)(struct file_lock *, struct file_lock *, int);
void (*fl_release_private)(struct file_lock *); void (*fl_release_private)(struct file_lock *);
void (*fl_break)(struct file_lock *); /* break_lease callback */ void (*fl_break)(struct file_lock *); /* break_lease callback */
int (*fl_mylease)(struct file_lock *, struct file_lock *);
int (*fl_change)(struct file_lock **, int); int (*fl_change)(struct file_lock **, int);
locking rules: locking rules:
@ -353,7 +352,6 @@ fl_notify: yes no
fl_grant: no no fl_grant: no no
fl_release_private: maybe no fl_release_private: maybe no
fl_break: yes no fl_break: yes no
fl_mylease: yes no
fl_change yes no fl_change yes no
--------------------------- buffer_head ----------------------------------- --------------------------- buffer_head -----------------------------------

View File

@ -444,15 +444,9 @@ static void lease_release_private_callback(struct file_lock *fl)
fl->fl_file->f_owner.signum = 0; fl->fl_file->f_owner.signum = 0;
} }
static int lease_mylease_callback(struct file_lock *fl, struct file_lock *try)
{
return fl->fl_file == try->fl_file;
}
static const struct lock_manager_operations lease_manager_ops = { static const struct lock_manager_operations lease_manager_ops = {
.fl_break = lease_break_callback, .fl_break = lease_break_callback,
.fl_release_private = lease_release_private_callback, .fl_release_private = lease_release_private_callback,
.fl_mylease = lease_mylease_callback,
.fl_change = lease_modify, .fl_change = lease_modify,
}; };
@ -1405,7 +1399,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
for (before = &inode->i_flock; for (before = &inode->i_flock;
((fl = *before) != NULL) && IS_LEASE(fl); ((fl = *before) != NULL) && IS_LEASE(fl);
before = &fl->fl_next) { before = &fl->fl_next) {
if (lease->fl_lmops->fl_mylease(fl, lease)) if (fl->fl_file == filp)
my_before = before; my_before = before;
else if (fl->fl_type == (F_INPROGRESS | F_UNLCK)) else if (fl->fl_type == (F_INPROGRESS | F_UNLCK))
/* /*

View File

@ -1,6 +1,4 @@
/* /*
* include/linux/nfs4_acl.c
*
* Common NFSv4 ACL handling definitions. * Common NFSv4 ACL handling definitions.
* *
* Copyright (c) 2002 The Regents of the University of Michigan. * Copyright (c) 2002 The Regents of the University of Michigan.

View File

@ -1,4 +1,3 @@
#define MSNFS /* HACK HACK */
/* /*
* NFS exporting and validation. * NFS exporting and validation.
* *
@ -1444,9 +1443,6 @@ static struct flags {
{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
{ NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
{ NFSEXP_V4ROOT, {"v4root", ""}}, { NFSEXP_V4ROOT, {"v4root", ""}},
#ifdef MSNFS
{ NFSEXP_MSNFS, {"msnfs", ""}},
#endif
{ 0, {"", ""}} { 0, {"", ""}}
}; };

View File

@ -1,6 +1,4 @@
/* /*
* include/linux/nfsd_idmap.h
*
* Mapping of UID to name and vice versa. * Mapping of UID to name and vice versa.
* *
* Copyright (c) 2002, 2003 The Regents of the University of * Copyright (c) 2002, 2003 The Regents of the University of
@ -56,8 +54,8 @@ static inline void nfsd_idmap_shutdown(void)
} }
#endif #endif
int nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, __u32 *); __be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, __u32 *);
int nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, __u32 *); __be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, __u32 *);
int nfsd_map_uid_to_name(struct svc_rqst *, __u32, char *); int nfsd_map_uid_to_name(struct svc_rqst *, __u32, char *);
int nfsd_map_gid_to_name(struct svc_rqst *, __u32, char *); int nfsd_map_gid_to_name(struct svc_rqst *, __u32, char *);

View File

@ -151,10 +151,10 @@ nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp,
__be32 nfserr; __be32 nfserr;
u32 max_blocksize = svc_max_payload(rqstp); u32 max_blocksize = svc_max_payload(rqstp);
dprintk("nfsd: READ(3) %s %lu bytes at %lu\n", dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
(unsigned long) argp->count, (unsigned long) argp->count,
(unsigned long) argp->offset); (unsigned long long) argp->offset);
/* Obtain buffer pointer for payload. /* Obtain buffer pointer for payload.
* 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof) * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
@ -191,10 +191,10 @@ nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
__be32 nfserr; __be32 nfserr;
unsigned long cnt = argp->len; unsigned long cnt = argp->len;
dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n", dprintk("nfsd: WRITE(3) %s %d bytes at %Lu%s\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
argp->len, argp->len,
(unsigned long) argp->offset, (unsigned long long) argp->offset,
argp->stable? " stable" : ""); argp->stable? " stable" : "");
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);

View File

@ -36,7 +36,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs4_acl.h> #include "acl.h"
/* mode bit translations: */ /* mode bit translations: */

View File

@ -628,10 +628,8 @@ static int max_cb_time(void)
return max(nfsd4_lease/10, (time_t)1) * HZ; return max(nfsd4_lease/10, (time_t)1) * HZ;
} }
/* 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 *conn) static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
{ {
struct rpc_timeout timeparms = { struct rpc_timeout timeparms = {
.to_initval = max_cb_time(), .to_initval = max_cb_time(),
@ -641,6 +639,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
.net = &init_net, .net = &init_net,
.address = (struct sockaddr *) &conn->cb_addr, .address = (struct sockaddr *) &conn->cb_addr,
.addrsize = conn->cb_addrlen, .addrsize = conn->cb_addrlen,
.saddress = (struct sockaddr *) &conn->cb_saddr,
.timeout = &timeparms, .timeout = &timeparms,
.program = &cb_program, .program = &cb_program,
.version = 0, .version = 0,
@ -657,6 +656,10 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
args.protocol = XPRT_TRANSPORT_TCP; args.protocol = XPRT_TRANSPORT_TCP;
clp->cl_cb_ident = conn->cb_ident; clp->cl_cb_ident = conn->cb_ident;
} else { } else {
if (!conn->cb_xprt)
return -EINVAL;
clp->cl_cb_conn.cb_xprt = conn->cb_xprt;
clp->cl_cb_session = ses;
args.bc_xprt = conn->cb_xprt; args.bc_xprt = conn->cb_xprt;
args.prognumber = clp->cl_cb_session->se_cb_prog; args.prognumber = clp->cl_cb_session->se_cb_prog;
args.protocol = XPRT_TRANSPORT_BC_TCP; args.protocol = XPRT_TRANSPORT_BC_TCP;
@ -679,14 +682,20 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason)
(int)clp->cl_name.len, clp->cl_name.data, reason); (int)clp->cl_name.len, clp->cl_name.data, reason);
} }
static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
{
clp->cl_cb_state = NFSD4_CB_DOWN;
warn_no_callback_path(clp, reason);
}
static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
{ {
struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null); struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
if (task->tk_status) if (task->tk_status)
warn_no_callback_path(clp, task->tk_status); nfsd4_mark_cb_down(clp, task->tk_status);
else else
atomic_set(&clp->cl_cb_set, 1); clp->cl_cb_state = NFSD4_CB_UP;
} }
static const struct rpc_call_ops nfsd4_cb_probe_ops = { static const struct rpc_call_ops nfsd4_cb_probe_ops = {
@ -709,6 +718,11 @@ int set_callback_cred(void)
static struct workqueue_struct *callback_wq; static struct workqueue_struct *callback_wq;
static void run_nfsd4_cb(struct nfsd4_callback *cb)
{
queue_work(callback_wq, &cb->cb_work);
}
static void do_probe_callback(struct nfs4_client *clp) static void do_probe_callback(struct nfs4_client *clp)
{ {
struct nfsd4_callback *cb = &clp->cl_cb_null; struct nfsd4_callback *cb = &clp->cl_cb_null;
@ -723,7 +737,7 @@ static void do_probe_callback(struct nfs4_client *clp)
cb->cb_ops = &nfsd4_cb_probe_ops; cb->cb_ops = &nfsd4_cb_probe_ops;
queue_work(callback_wq, &cb->cb_work); run_nfsd4_cb(cb);
} }
/* /*
@ -732,14 +746,21 @@ static void do_probe_callback(struct nfs4_client *clp)
*/ */
void nfsd4_probe_callback(struct nfs4_client *clp) void nfsd4_probe_callback(struct nfs4_client *clp)
{ {
/* XXX: atomicity? Also, should we be using cl_cb_flags? */
clp->cl_cb_state = NFSD4_CB_UNKNOWN;
set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
do_probe_callback(clp); do_probe_callback(clp);
} }
void nfsd4_probe_callback_sync(struct nfs4_client *clp)
{
nfsd4_probe_callback(clp);
flush_workqueue(callback_wq);
}
void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{ {
BUG_ON(atomic_read(&clp->cl_cb_set)); clp->cl_cb_state = NFSD4_CB_UNKNOWN;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
@ -750,24 +771,14 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
* If the slot is available, then mark it busy. Otherwise, set the * If the slot is available, then mark it busy. Otherwise, set the
* thread for sleeping on the callback RPC wait queue. * thread for sleeping on the callback RPC wait queue.
*/ */
static int nfsd41_cb_setup_sequence(struct nfs4_client *clp, static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task)
struct rpc_task *task)
{ {
u32 *ptr = (u32 *)clp->cl_cb_session->se_sessionid.data;
int status = 0;
dprintk("%s: %u:%u:%u:%u\n", __func__,
ptr[0], ptr[1], ptr[2], ptr[3]);
if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) { if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL); rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
dprintk("%s slot is busy\n", __func__); dprintk("%s slot is busy\n", __func__);
status = -EAGAIN; return false;
goto out;
} }
out: return true;
dprintk("%s status=%d\n", __func__, status);
return status;
} }
/* /*
@ -780,20 +791,19 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client; struct nfs4_client *clp = dp->dl_client;
u32 minorversion = clp->cl_minorversion; u32 minorversion = clp->cl_minorversion;
int status = 0;
cb->cb_minorversion = minorversion; cb->cb_minorversion = minorversion;
if (minorversion) { if (minorversion) {
status = nfsd41_cb_setup_sequence(clp, task); if (!nfsd41_cb_get_slot(clp, task))
if (status) {
if (status != -EAGAIN) {
/* terminate rpc task */
task->tk_status = status;
task->tk_action = NULL;
}
return; return;
}
} }
spin_lock(&clp->cl_lock);
if (list_empty(&cb->cb_per_client)) {
/* This is the first call, not a restart */
cb->cb_done = false;
list_add(&cb->cb_per_client, &clp->cl_callbacks);
}
spin_unlock(&clp->cl_lock);
rpc_call_start(task); rpc_call_start(task);
} }
@ -829,15 +839,18 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
nfsd4_cb_done(task, calldata); nfsd4_cb_done(task, calldata);
if (current_rpc_client == NULL) { if (current_rpc_client != task->tk_client) {
/* We're shutting down; give up. */ /* We're shutting down or changing cl_cb_client; leave
/* XXX: err, or is it ok just to fall through * it to nfsd4_process_cb_update to restart the call if
* and rpc_restart_call? */ * necessary. */
return; return;
} }
if (cb->cb_done)
return;
switch (task->tk_status) { switch (task->tk_status) {
case 0: case 0:
cb->cb_done = true;
return; return;
case -EBADHANDLE: case -EBADHANDLE:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
@ -846,32 +859,30 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
break; break;
default: default:
/* Network partition? */ /* Network partition? */
atomic_set(&clp->cl_cb_set, 0); nfsd4_mark_cb_down(clp, task->tk_status);
warn_no_callback_path(clp, task->tk_status);
if (current_rpc_client != task->tk_client) {
/* queue a callback on the new connection: */
atomic_inc(&dp->dl_count);
nfsd4_cb_recall(dp);
return;
}
} }
if (dp->dl_retries--) { if (dp->dl_retries--) {
rpc_delay(task, 2*HZ); rpc_delay(task, 2*HZ);
task->tk_status = 0; task->tk_status = 0;
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
} else {
atomic_set(&clp->cl_cb_set, 0);
warn_no_callback_path(clp, task->tk_status);
} }
nfsd4_mark_cb_down(clp, task->tk_status);
cb->cb_done = true;
} }
static void nfsd4_cb_recall_release(void *calldata) static void nfsd4_cb_recall_release(void *calldata)
{ {
struct nfsd4_callback *cb = calldata; struct nfsd4_callback *cb = calldata;
struct nfs4_client *clp = cb->cb_clp;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
nfs4_put_delegation(dp); if (cb->cb_done) {
spin_lock(&clp->cl_lock);
list_del(&cb->cb_per_client);
spin_unlock(&clp->cl_lock);
nfs4_put_delegation(dp);
}
} }
static const struct rpc_call_ops nfsd4_cb_recall_ops = { static const struct rpc_call_ops nfsd4_cb_recall_ops = {
@ -906,16 +917,33 @@ void nfsd4_shutdown_callback(struct nfs4_client *clp)
flush_workqueue(callback_wq); flush_workqueue(callback_wq);
} }
void nfsd4_release_cb(struct nfsd4_callback *cb) static void nfsd4_release_cb(struct nfsd4_callback *cb)
{ {
if (cb->cb_ops->rpc_release) if (cb->cb_ops->rpc_release)
cb->cb_ops->rpc_release(cb); cb->cb_ops->rpc_release(cb);
} }
void nfsd4_process_cb_update(struct nfsd4_callback *cb) /* requires cl_lock: */
static struct nfsd4_conn * __nfsd4_find_backchannel(struct nfs4_client *clp)
{
struct nfsd4_session *s;
struct nfsd4_conn *c;
list_for_each_entry(s, &clp->cl_sessions, se_perclnt) {
list_for_each_entry(c, &s->se_conns, cn_persession) {
if (c->cn_flags & NFS4_CDFC4_BACK)
return c;
}
}
return NULL;
}
static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
{ {
struct nfs4_cb_conn conn; struct nfs4_cb_conn conn;
struct nfs4_client *clp = cb->cb_clp; struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *ses = NULL;
struct nfsd4_conn *c;
int err; int err;
/* /*
@ -926,6 +954,10 @@ void nfsd4_process_cb_update(struct nfsd4_callback *cb)
rpc_shutdown_client(clp->cl_cb_client); rpc_shutdown_client(clp->cl_cb_client);
clp->cl_cb_client = NULL; clp->cl_cb_client = NULL;
} }
if (clp->cl_cb_conn.cb_xprt) {
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
clp->cl_cb_conn.cb_xprt = NULL;
}
if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags)) if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags))
return; return;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
@ -936,11 +968,22 @@ void nfsd4_process_cb_update(struct nfsd4_callback *cb)
BUG_ON(!clp->cl_cb_flags); BUG_ON(!clp->cl_cb_flags);
clear_bit(NFSD4_CLIENT_CB_UPDATE, &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)); memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn));
c = __nfsd4_find_backchannel(clp);
if (c) {
svc_xprt_get(c->cn_xprt);
conn.cb_xprt = c->cn_xprt;
ses = c->cn_session;
}
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
err = setup_callback_client(clp, &conn); err = setup_callback_client(clp, &conn, ses);
if (err) if (err) {
warn_no_callback_path(clp, err); warn_no_callback_path(clp, err);
return;
}
/* Yay, the callback channel's back! Restart any callbacks: */
list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client)
run_nfsd4_cb(cb);
} }
void nfsd4_do_callback_rpc(struct work_struct *w) void nfsd4_do_callback_rpc(struct work_struct *w)
@ -965,10 +1008,11 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
void nfsd4_cb_recall(struct nfs4_delegation *dp) void nfsd4_cb_recall(struct nfs4_delegation *dp)
{ {
struct nfsd4_callback *cb = &dp->dl_recall; struct nfsd4_callback *cb = &dp->dl_recall;
struct nfs4_client *clp = dp->dl_client;
dp->dl_retries = 1; dp->dl_retries = 1;
cb->cb_op = dp; cb->cb_op = dp;
cb->cb_clp = dp->dl_client; cb->cb_clp = clp;
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
cb->cb_msg.rpc_argp = cb; cb->cb_msg.rpc_argp = cb;
cb->cb_msg.rpc_resp = cb; cb->cb_msg.rpc_resp = cb;
@ -977,5 +1021,8 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
cb->cb_ops = &nfsd4_cb_recall_ops; cb->cb_ops = &nfsd4_cb_recall_ops;
dp->dl_retries = 1; dp->dl_retries = 1;
queue_work(callback_wq, &dp->dl_recall.cb_work); INIT_LIST_HEAD(&cb->cb_per_client);
cb->cb_done = true;
run_nfsd4_cb(&dp->dl_recall);
} }

View File

@ -33,10 +33,11 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/nfsd_idmap.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "idmap.h"
#include "nfsd.h"
/* /*
* Cache entry * Cache entry
@ -514,7 +515,7 @@ rqst_authname(struct svc_rqst *rqstp)
return clp->name; return clp->name;
} }
static int static __be32
idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen,
uid_t *id) uid_t *id)
{ {
@ -524,15 +525,15 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
int ret; int ret;
if (namelen + 1 > sizeof(key.name)) if (namelen + 1 > sizeof(key.name))
return -EINVAL; return nfserr_badowner;
memcpy(key.name, name, namelen); memcpy(key.name, name, namelen);
key.name[namelen] = '\0'; key.name[namelen] = '\0';
strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item); ret = idmap_lookup(rqstp, nametoid_lookup, &key, &nametoid_cache, &item);
if (ret == -ENOENT) if (ret == -ENOENT)
ret = -ESRCH; /* nfserr_badname */ return nfserr_badowner;
if (ret) if (ret)
return ret; return nfserrno(ret);
*id = item->id; *id = item->id;
cache_put(&item->h, &nametoid_cache); cache_put(&item->h, &nametoid_cache);
return 0; return 0;
@ -560,14 +561,14 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
return ret; return ret;
} }
int __be32
nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen,
__u32 *id) __u32 *id)
{ {
return idmap_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); return idmap_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id);
} }
int __be32
nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
__u32 *id) __u32 *id)
{ {

View File

@ -604,9 +604,7 @@ nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status; return status;
} }
static __be32 static __be32 nfsd4_do_lookupp(struct svc_rqst *rqstp, struct svc_fh *fh)
nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
void *arg)
{ {
struct svc_fh tmp_fh; struct svc_fh tmp_fh;
__be32 ret; __be32 ret;
@ -615,13 +613,19 @@ nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
ret = exp_pseudoroot(rqstp, &tmp_fh); ret = exp_pseudoroot(rqstp, &tmp_fh);
if (ret) if (ret)
return ret; return ret;
if (tmp_fh.fh_dentry == cstate->current_fh.fh_dentry) { if (tmp_fh.fh_dentry == fh->fh_dentry) {
fh_put(&tmp_fh); fh_put(&tmp_fh);
return nfserr_noent; return nfserr_noent;
} }
fh_put(&tmp_fh); fh_put(&tmp_fh);
return nfsd_lookup(rqstp, &cstate->current_fh, return nfsd_lookup(rqstp, fh, "..", 2, fh);
"..", 2, &cstate->current_fh); }
static __be32
nfsd4_lookupp(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
void *arg)
{
return nfsd4_do_lookupp(rqstp, &cstate->current_fh);
} }
static __be32 static __be32
@ -769,9 +773,35 @@ nfsd4_secinfo(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
} else } else
secinfo->si_exp = exp; secinfo->si_exp = exp;
dput(dentry); dput(dentry);
if (cstate->minorversion)
/* See rfc 5661 section 2.6.3.1.1.8 */
fh_put(&cstate->current_fh);
return err; return err;
} }
static __be32
nfsd4_secinfo_no_name(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_secinfo_no_name *sin)
{
__be32 err;
switch (sin->sin_style) {
case NFS4_SECINFO_STYLE4_CURRENT_FH:
break;
case NFS4_SECINFO_STYLE4_PARENT:
err = nfsd4_do_lookupp(rqstp, &cstate->current_fh);
if (err)
return err;
break;
default:
return nfserr_inval;
}
exp_get(cstate->current_fh.fh_export);
sin->sin_exp = cstate->current_fh.fh_export;
fh_put(&cstate->current_fh);
return nfs_ok;
}
static __be32 static __be32
nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_setattr *setattr) struct nfsd4_setattr *setattr)
@ -974,8 +1004,8 @@ static const char *nfsd4_op_name(unsigned opnum);
* Also note, enforced elsewhere: * Also note, enforced elsewhere:
* - SEQUENCE other than as first op results in * - SEQUENCE other than as first op results in
* NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().)
* - BIND_CONN_TO_SESSION must be the only op in its compound * - BIND_CONN_TO_SESSION must be the only op in its compound.
* (Will be enforced in nfsd4_bind_conn_to_session().) * (Enforced in nfsd4_bind_conn_to_session().)
* - DESTROY_SESSION must be the final operation in a compound, if * - DESTROY_SESSION must be the final operation in a compound, if
* sessionid's in SEQUENCE and DESTROY_SESSION are the same. * sessionid's in SEQUENCE and DESTROY_SESSION are the same.
* (Enforced in nfsd4_destroy_session().) * (Enforced in nfsd4_destroy_session().)
@ -1126,10 +1156,6 @@ encode_op:
nfsd4_increment_op_stats(op->opnum); nfsd4_increment_op_stats(op->opnum);
} }
if (!rqstp->rq_usedeferral && status == nfserr_dropit) {
dprintk("%s Dropit - send NFS4ERR_DELAY\n", __func__);
status = nfserr_jukebox;
}
resp->cstate.status = status; resp->cstate.status = status;
fh_put(&resp->cstate.current_fh); fh_put(&resp->cstate.current_fh);
@ -1300,6 +1326,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_EXCHANGE_ID", .op_name = "OP_EXCHANGE_ID",
}, },
[OP_BIND_CONN_TO_SESSION] = {
.op_func = (nfsd4op_func)nfsd4_bind_conn_to_session,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
.op_name = "OP_BIND_CONN_TO_SESSION",
},
[OP_CREATE_SESSION] = { [OP_CREATE_SESSION] = {
.op_func = (nfsd4op_func)nfsd4_create_session, .op_func = (nfsd4op_func)nfsd4_create_session,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
@ -1320,6 +1351,10 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_flags = ALLOWED_WITHOUT_FH, .op_flags = ALLOWED_WITHOUT_FH,
.op_name = "OP_RECLAIM_COMPLETE", .op_name = "OP_RECLAIM_COMPLETE",
}, },
[OP_SECINFO_NO_NAME] = {
.op_func = (nfsd4op_func)nfsd4_secinfo_no_name,
.op_name = "OP_SECINFO_NO_NAME",
},
}; };
static const char *nfsd4_op_name(unsigned opnum) static const char *nfsd4_op_name(unsigned opnum)

View File

@ -302,7 +302,6 @@ purge_old(struct dentry *parent, struct dentry *child)
{ {
int status; int status;
/* note: we currently use this path only for minorversion 0 */
if (nfs4_has_reclaimed_state(child->d_name.name, false)) if (nfs4_has_reclaimed_state(child->d_name.name, false))
return 0; return 0;

View File

@ -230,7 +230,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
dp->dl_client = clp; dp->dl_client = clp;
get_nfs4_file(fp); get_nfs4_file(fp);
dp->dl_file = fp; dp->dl_file = fp;
nfs4_file_get_access(fp, O_RDONLY); dp->dl_vfs_file = find_readable_file(fp);
get_file(dp->dl_vfs_file);
dp->dl_flock = NULL; dp->dl_flock = NULL;
dp->dl_type = type; dp->dl_type = type;
dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_boot = boot_time;
@ -252,6 +253,7 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
if (atomic_dec_and_test(&dp->dl_count)) { if (atomic_dec_and_test(&dp->dl_count)) {
dprintk("NFSD: freeing dp %p\n",dp); dprintk("NFSD: freeing dp %p\n",dp);
put_nfs4_file(dp->dl_file); put_nfs4_file(dp->dl_file);
fput(dp->dl_vfs_file);
kmem_cache_free(deleg_slab, dp); kmem_cache_free(deleg_slab, dp);
num_delegations--; num_delegations--;
} }
@ -265,12 +267,10 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
static void static void
nfs4_close_delegation(struct nfs4_delegation *dp) nfs4_close_delegation(struct nfs4_delegation *dp)
{ {
struct file *filp = find_readable_file(dp->dl_file);
dprintk("NFSD: close_delegation dp %p\n",dp); dprintk("NFSD: close_delegation dp %p\n",dp);
/* XXX: do we even need this check?: */
if (dp->dl_flock) if (dp->dl_flock)
vfs_setlease(filp, F_UNLCK, &dp->dl_flock); vfs_setlease(dp->dl_vfs_file, F_UNLCK, &dp->dl_flock);
nfs4_file_put_access(dp->dl_file, O_RDONLY);
} }
/* Called under the state lock. */ /* Called under the state lock. */
@ -642,6 +642,7 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u)
free_conn(c); free_conn(c);
} }
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
nfsd4_probe_callback(clp);
} }
static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags) static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags)
@ -679,15 +680,12 @@ static int nfsd4_register_conn(struct nfsd4_conn *conn)
return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
} }
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir)
{ {
struct nfsd4_conn *conn; struct nfsd4_conn *conn;
u32 flags = NFS4_CDFC4_FORE;
int ret; int ret;
if (ses->se_flags & SESSION4_BACK_CHAN) conn = alloc_conn(rqstp, dir);
flags |= NFS4_CDFC4_BACK;
conn = alloc_conn(rqstp, flags);
if (!conn) if (!conn)
return nfserr_jukebox; return nfserr_jukebox;
nfsd4_hash_conn(conn, ses); nfsd4_hash_conn(conn, ses);
@ -698,6 +696,17 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
return nfs_ok; return nfs_ok;
} }
static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses)
{
u32 dir = NFS4_CDFC4_FORE;
if (ses->se_flags & SESSION4_BACK_CHAN)
dir |= NFS4_CDFC4_BACK;
return nfsd4_new_conn(rqstp, ses, dir);
}
/* must be called under client_lock */
static void nfsd4_del_conns(struct nfsd4_session *s) static void nfsd4_del_conns(struct nfsd4_session *s)
{ {
struct nfs4_client *clp = s->se_client; struct nfs4_client *clp = s->se_client;
@ -749,6 +758,8 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
*/ */
slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached); slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached);
numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs); numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs);
if (numslots < 1)
return NULL;
new = alloc_session(slotsize, numslots); new = alloc_session(slotsize, numslots);
if (!new) { if (!new) {
@ -769,25 +780,30 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
idx = hash_sessionid(&new->se_sessionid); idx = hash_sessionid(&new->se_sessionid);
spin_lock(&client_lock); spin_lock(&client_lock);
list_add(&new->se_hash, &sessionid_hashtbl[idx]); list_add(&new->se_hash, &sessionid_hashtbl[idx]);
spin_lock(&clp->cl_lock);
list_add(&new->se_perclnt, &clp->cl_sessions); list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&clp->cl_lock);
spin_unlock(&client_lock); spin_unlock(&client_lock);
status = nfsd4_new_conn(rqstp, new); status = nfsd4_new_conn_from_crses(rqstp, new);
/* whoops: benny points out, status is ignored! (err, or bogus) */ /* whoops: benny points out, status is ignored! (err, or bogus) */
if (status) { if (status) {
free_session(&new->se_ref); free_session(&new->se_ref);
return NULL; return NULL;
} }
if (!clp->cl_cb_session && (cses->flags & SESSION4_BACK_CHAN)) { if (cses->flags & SESSION4_BACK_CHAN) {
struct sockaddr *sa = svc_addr(rqstp); struct sockaddr *sa = svc_addr(rqstp);
/*
clp->cl_cb_session = new; * This is a little silly; with sessions there's no real
clp->cl_cb_conn.cb_xprt = rqstp->rq_xprt; * use for the callback address. Use the peer address
svc_xprt_get(rqstp->rq_xprt); * as a reasonable default for now, but consider fixing
* the rpc client not to require an address in the
* future:
*/
rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
nfsd4_probe_callback(clp);
} }
nfsd4_probe_callback(clp);
return new; return new;
} }
@ -817,7 +833,9 @@ static void
unhash_session(struct nfsd4_session *ses) unhash_session(struct nfsd4_session *ses)
{ {
list_del(&ses->se_hash); list_del(&ses->se_hash);
spin_lock(&ses->se_client->cl_lock);
list_del(&ses->se_perclnt); list_del(&ses->se_perclnt);
spin_unlock(&ses->se_client->cl_lock);
} }
/* must be called under the client_lock */ /* must be called under the client_lock */
@ -923,8 +941,10 @@ unhash_client_locked(struct nfs4_client *clp)
mark_client_expired(clp); mark_client_expired(clp);
list_del(&clp->cl_lru); list_del(&clp->cl_lru);
spin_lock(&clp->cl_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
list_del_init(&ses->se_hash); list_del_init(&ses->se_hash);
spin_unlock(&clp->cl_lock);
} }
static void static void
@ -1051,12 +1071,13 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
atomic_set(&clp->cl_refcount, 0); atomic_set(&clp->cl_refcount, 0);
atomic_set(&clp->cl_cb_set, 0); clp->cl_cb_state = NFSD4_CB_UNKNOWN;
INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_idhash);
INIT_LIST_HEAD(&clp->cl_strhash); INIT_LIST_HEAD(&clp->cl_strhash);
INIT_LIST_HEAD(&clp->cl_openowners); INIT_LIST_HEAD(&clp->cl_openowners);
INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_lru); INIT_LIST_HEAD(&clp->cl_lru);
INIT_LIST_HEAD(&clp->cl_callbacks);
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
clp->cl_time = get_seconds(); clp->cl_time = get_seconds();
@ -1132,54 +1153,55 @@ find_unconfirmed_client(clientid_t *clid)
return NULL; return NULL;
} }
/* static bool clp_used_exchangeid(struct nfs4_client *clp)
* Return 1 iff clp's clientid establishment method matches the use_exchange_id
* parameter. Matching is based on the fact the at least one of the
* EXCHGID4_FLAG_USE_{NON_PNFS,PNFS_MDS,PNFS_DS} flags must be set for v4.1
*
* FIXME: we need to unify the clientid namespaces for nfsv4.x
* and correctly deal with client upgrade/downgrade in EXCHANGE_ID
* and SET_CLIENTID{,_CONFIRM}
*/
static inline int
match_clientid_establishment(struct nfs4_client *clp, bool use_exchange_id)
{ {
bool has_exchange_flags = (clp->cl_exchange_flags != 0); return clp->cl_exchange_flags != 0;
return use_exchange_id == has_exchange_flags; }
}
static struct nfs4_client * static struct nfs4_client *
find_confirmed_client_by_str(const char *dname, unsigned int hashval, find_confirmed_client_by_str(const char *dname, unsigned int hashval)
bool use_exchange_id)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) {
if (same_name(clp->cl_recdir, dname) && if (same_name(clp->cl_recdir, dname))
match_clientid_establishment(clp, use_exchange_id))
return clp; return clp;
} }
return NULL; return NULL;
} }
static struct nfs4_client * static struct nfs4_client *
find_unconfirmed_client_by_str(const char *dname, unsigned int hashval, find_unconfirmed_client_by_str(const char *dname, unsigned int hashval)
bool use_exchange_id)
{ {
struct nfs4_client *clp; struct nfs4_client *clp;
list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) {
if (same_name(clp->cl_recdir, dname) && if (same_name(clp->cl_recdir, dname))
match_clientid_establishment(clp, use_exchange_id))
return clp; return clp;
} }
return NULL; return NULL;
} }
static void rpc_svcaddr2sockaddr(struct sockaddr *sa, unsigned short family, union svc_addr_u *svcaddr)
{
switch (family) {
case AF_INET:
((struct sockaddr_in *)sa)->sin_family = AF_INET;
((struct sockaddr_in *)sa)->sin_addr = svcaddr->addr;
return;
case AF_INET6:
((struct sockaddr_in6 *)sa)->sin6_family = AF_INET6;
((struct sockaddr_in6 *)sa)->sin6_addr = svcaddr->addr6;
return;
}
}
static void static void
gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid) gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_rqst *rqstp)
{ {
struct nfs4_cb_conn *conn = &clp->cl_cb_conn; struct nfs4_cb_conn *conn = &clp->cl_cb_conn;
struct sockaddr *sa = svc_addr(rqstp);
u32 scopeid = rpc_get_scope_id(sa);
unsigned short expected_family; unsigned short expected_family;
/* Currently, we only support tcp and tcp6 for the callback channel */ /* Currently, we only support tcp and tcp6 for the callback channel */
@ -1205,6 +1227,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
conn->cb_prog = se->se_callback_prog; conn->cb_prog = se->se_callback_prog;
conn->cb_ident = se->se_callback_ident; conn->cb_ident = se->se_callback_ident;
rpc_svcaddr2sockaddr((struct sockaddr *)&conn->cb_saddr, expected_family, &rqstp->rq_daddr);
return; return;
out_err: out_err:
conn->cb_addr.ss_family = AF_UNSPEC; conn->cb_addr.ss_family = AF_UNSPEC;
@ -1344,7 +1367,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
case SP4_NONE: case SP4_NONE:
break; break;
case SP4_SSV: case SP4_SSV:
return nfserr_encr_alg_unsupp; return nfserr_serverfault;
default: default:
BUG(); /* checked by xdr code */ BUG(); /* checked by xdr code */
case SP4_MACH_CRED: case SP4_MACH_CRED:
@ -1361,8 +1384,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
nfs4_lock_state(); nfs4_lock_state();
status = nfs_ok; status = nfs_ok;
conf = find_confirmed_client_by_str(dname, strhashval, true); conf = find_confirmed_client_by_str(dname, strhashval);
if (conf) { if (conf) {
if (!clp_used_exchangeid(conf)) {
status = nfserr_clid_inuse; /* XXX: ? */
goto out;
}
if (!same_verf(&verf, &conf->cl_verifier)) { if (!same_verf(&verf, &conf->cl_verifier)) {
/* 18.35.4 case 8 */ /* 18.35.4 case 8 */
if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
@ -1403,7 +1430,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
goto out; goto out;
} }
unconf = find_unconfirmed_client_by_str(dname, strhashval, true); unconf = find_unconfirmed_client_by_str(dname, strhashval);
if (unconf) { if (unconf) {
/* /*
* Possible retry or client restart. Per 18.35.4 case 4, * Possible retry or client restart. Per 18.35.4 case 4,
@ -1560,6 +1587,8 @@ nfsd4_create_session(struct svc_rqst *rqstp,
status = nfs_ok; status = nfs_ok;
memcpy(cr_ses->sessionid.data, new->se_sessionid.data, memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN); NFS4_MAX_SESSIONID_LEN);
memcpy(&cr_ses->fore_channel, &new->se_fchannel,
sizeof(struct nfsd4_channel_attrs));
cs_slot->sl_seqid++; cs_slot->sl_seqid++;
cr_ses->seqid = cs_slot->sl_seqid; cr_ses->seqid = cs_slot->sl_seqid;
@ -1581,6 +1610,45 @@ static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
return argp->opcnt == resp->opcnt; return argp->opcnt == resp->opcnt;
} }
static __be32 nfsd4_map_bcts_dir(u32 *dir)
{
switch (*dir) {
case NFS4_CDFC4_FORE:
case NFS4_CDFC4_BACK:
return nfs_ok;
case NFS4_CDFC4_FORE_OR_BOTH:
case NFS4_CDFC4_BACK_OR_BOTH:
*dir = NFS4_CDFC4_BOTH;
return nfs_ok;
};
return nfserr_inval;
}
__be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
struct nfsd4_bind_conn_to_session *bcts)
{
__be32 status;
if (!nfsd4_last_compound_op(rqstp))
return nfserr_not_only_op;
spin_lock(&client_lock);
cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid);
/* Sorta weird: we only need the refcnt'ing because new_conn acquires
* client_lock iself: */
if (cstate->session) {
nfsd4_get_session(cstate->session);
atomic_inc(&cstate->session->se_client->cl_refcount);
}
spin_unlock(&client_lock);
if (!cstate->session)
return nfserr_badsession;
status = nfsd4_map_bcts_dir(&bcts->dir);
nfsd4_new_conn(rqstp, cstate->session, bcts->dir);
return nfs_ok;
}
static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid)
{ {
if (!session) if (!session)
@ -1619,8 +1687,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
spin_unlock(&client_lock); spin_unlock(&client_lock);
nfs4_lock_state(); nfs4_lock_state();
/* wait for callbacks */ nfsd4_probe_callback_sync(ses->se_client);
nfsd4_shutdown_callback(ses->se_client);
nfs4_unlock_state(); nfs4_unlock_state();
nfsd4_del_conns(ses); nfsd4_del_conns(ses);
@ -1733,8 +1800,12 @@ nfsd4_sequence(struct svc_rqst *rqstp,
out: out:
/* Hold a session reference until done processing the compound. */ /* Hold a session reference until done processing the compound. */
if (cstate->session) { if (cstate->session) {
struct nfs4_client *clp = session->se_client;
nfsd4_get_session(cstate->session); nfsd4_get_session(cstate->session);
atomic_inc(&session->se_client->cl_refcount); atomic_inc(&clp->cl_refcount);
if (clp->cl_cb_state == NFSD4_CB_DOWN)
seq->status_flags |= SEQ4_STATUS_CB_PATH_DOWN;
} }
kfree(conn); kfree(conn);
spin_unlock(&client_lock); spin_unlock(&client_lock);
@ -1775,7 +1846,6 @@ __be32
nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_setclientid *setclid) struct nfsd4_setclientid *setclid)
{ {
struct sockaddr *sa = svc_addr(rqstp);
struct xdr_netobj clname = { struct xdr_netobj clname = {
.len = setclid->se_namelen, .len = setclid->se_namelen,
.data = setclid->se_name, .data = setclid->se_name,
@ -1801,10 +1871,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
strhashval = clientstr_hashval(dname); strhashval = clientstr_hashval(dname);
nfs4_lock_state(); nfs4_lock_state();
conf = find_confirmed_client_by_str(dname, strhashval, false); conf = find_confirmed_client_by_str(dname, strhashval);
if (conf) { if (conf) {
/* RFC 3530 14.2.33 CASE 0: */ /* RFC 3530 14.2.33 CASE 0: */
status = nfserr_clid_inuse; status = nfserr_clid_inuse;
if (clp_used_exchangeid(conf))
goto out;
if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
char addr_str[INET6_ADDRSTRLEN]; char addr_str[INET6_ADDRSTRLEN];
rpc_ntop((struct sockaddr *) &conf->cl_addr, addr_str, rpc_ntop((struct sockaddr *) &conf->cl_addr, addr_str,
@ -1819,7 +1891,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* has a description of SETCLIENTID request processing consisting * has a description of SETCLIENTID request processing consisting
* of 5 bullet points, labeled as CASE0 - CASE4 below. * of 5 bullet points, labeled as CASE0 - CASE4 below.
*/ */
unconf = find_unconfirmed_client_by_str(dname, strhashval, false); unconf = find_unconfirmed_client_by_str(dname, strhashval);
status = nfserr_resource; status = nfserr_resource;
if (!conf) { if (!conf) {
/* /*
@ -1876,7 +1948,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* for consistent minorversion use throughout: * for consistent minorversion use throughout:
*/ */
new->cl_minorversion = 0; new->cl_minorversion = 0;
gen_callback(new, setclid, rpc_get_scope_id(sa)); gen_callback(new, setclid, rqstp);
add_to_unconfirmed(new, strhashval); add_to_unconfirmed(new, strhashval);
setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot; setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
setclid->se_clientid.cl_id = new->cl_clientid.cl_id; setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
@ -1935,7 +2007,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) if (!same_creds(&conf->cl_cred, &unconf->cl_cred))
status = nfserr_clid_inuse; status = nfserr_clid_inuse;
else { else {
atomic_set(&conf->cl_cb_set, 0);
nfsd4_change_callback(conf, &unconf->cl_cb_conn); nfsd4_change_callback(conf, &unconf->cl_cb_conn);
nfsd4_probe_callback(conf); nfsd4_probe_callback(conf);
expire_client(unconf); expire_client(unconf);
@ -1964,7 +2035,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
unsigned int hash = unsigned int hash =
clientstr_hashval(unconf->cl_recdir); clientstr_hashval(unconf->cl_recdir);
conf = find_confirmed_client_by_str(unconf->cl_recdir, conf = find_confirmed_client_by_str(unconf->cl_recdir,
hash, false); hash);
if (conf) { if (conf) {
nfsd4_remove_clid_dir(conf); nfsd4_remove_clid_dir(conf);
expire_client(conf); expire_client(conf);
@ -2300,41 +2371,6 @@ void nfsd_break_deleg_cb(struct file_lock *fl)
nfsd4_cb_recall(dp); nfsd4_cb_recall(dp);
} }
/*
* The file_lock is being reapd.
*
* Called by locks_free_lock() with lock_flocks() held.
*/
static
void nfsd_release_deleg_cb(struct file_lock *fl)
{
struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner;
dprintk("NFSD nfsd_release_deleg_cb: fl %p dp %p dl_count %d\n", fl,dp, atomic_read(&dp->dl_count));
if (!(fl->fl_flags & FL_LEASE) || !dp)
return;
dp->dl_flock = NULL;
}
/*
* Called from setlease() with lock_flocks() held
*/
static
int nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try)
{
struct nfs4_delegation *onlistd =
(struct nfs4_delegation *)onlist->fl_owner;
struct nfs4_delegation *tryd =
(struct nfs4_delegation *)try->fl_owner;
if (onlist->fl_lmops != try->fl_lmops)
return 0;
return onlistd->dl_client == tryd->dl_client;
}
static static
int nfsd_change_deleg_cb(struct file_lock **onlist, int arg) int nfsd_change_deleg_cb(struct file_lock **onlist, int arg)
{ {
@ -2346,8 +2382,6 @@ int nfsd_change_deleg_cb(struct file_lock **onlist, int arg)
static const struct lock_manager_operations nfsd_lease_mng_ops = { static const struct lock_manager_operations nfsd_lease_mng_ops = {
.fl_break = nfsd_break_deleg_cb, .fl_break = nfsd_break_deleg_cb,
.fl_release_private = nfsd_release_deleg_cb,
.fl_mylease = nfsd_same_client_deleg_cb,
.fl_change = nfsd_change_deleg_cb, .fl_change = nfsd_change_deleg_cb,
}; };
@ -2514,8 +2548,6 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file
if (!fp->fi_fds[oflag]) { if (!fp->fi_fds[oflag]) {
status = nfsd_open(rqstp, cur_fh, S_IFREG, access, status = nfsd_open(rqstp, cur_fh, S_IFREG, access,
&fp->fi_fds[oflag]); &fp->fi_fds[oflag]);
if (status == nfserr_dropit)
status = nfserr_jukebox;
if (status) if (status)
return status; return status;
} }
@ -2596,6 +2628,19 @@ nfs4_set_claim_prev(struct nfsd4_open *open)
open->op_stateowner->so_client->cl_firststate = 1; open->op_stateowner->so_client->cl_firststate = 1;
} }
/* Should we give out recallable state?: */
static bool nfsd4_cb_channel_good(struct nfs4_client *clp)
{
if (clp->cl_cb_state == NFSD4_CB_UP)
return true;
/*
* In the sessions case, since we don't have to establish a
* separate connection for callbacks, we assume it's OK
* until we hear otherwise:
*/
return clp->cl_minorversion && clp->cl_cb_state == NFSD4_CB_UNKNOWN;
}
/* /*
* Attempt to hand out a delegation. * Attempt to hand out a delegation.
*/ */
@ -2604,10 +2649,11 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
{ {
struct nfs4_delegation *dp; struct nfs4_delegation *dp;
struct nfs4_stateowner *sop = stp->st_stateowner; struct nfs4_stateowner *sop = stp->st_stateowner;
int cb_up = atomic_read(&sop->so_client->cl_cb_set); int cb_up;
struct file_lock *fl; struct file_lock *fl;
int status, flag = 0; int status, flag = 0;
cb_up = nfsd4_cb_channel_good(sop->so_client);
flag = NFS4_OPEN_DELEGATE_NONE; flag = NFS4_OPEN_DELEGATE_NONE;
open->op_recall = 0; open->op_recall = 0;
switch (open->op_claim_type) { switch (open->op_claim_type) {
@ -2655,7 +2701,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
dp->dl_flock = fl; dp->dl_flock = fl;
/* vfs_setlease checks to see if delegation should be handed out. /* vfs_setlease checks to see if delegation should be handed out.
* the lock_manager callbacks fl_mylease and fl_change are used * the lock_manager callback fl_change is used
*/ */
if ((status = vfs_setlease(fl->fl_file, fl->fl_type, &fl))) { if ((status = vfs_setlease(fl->fl_file, fl->fl_type, &fl))) {
dprintk("NFSD: setlease failed [%d], no delegation\n", status); dprintk("NFSD: setlease failed [%d], no delegation\n", status);
@ -2794,7 +2840,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
renew_client(clp); renew_client(clp);
status = nfserr_cb_path_down; status = nfserr_cb_path_down;
if (!list_empty(&clp->cl_delegations) if (!list_empty(&clp->cl_delegations)
&& !atomic_read(&clp->cl_cb_set)) && clp->cl_cb_state != NFSD4_CB_UP)
goto out; goto out;
status = nfs_ok; status = nfs_ok;
out: out:
@ -3081,9 +3127,10 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
if (status) if (status)
goto out; goto out;
renew_client(dp->dl_client); renew_client(dp->dl_client);
if (filpp) if (filpp) {
*filpp = find_readable_file(dp->dl_file); *filpp = find_readable_file(dp->dl_file);
BUG_ON(!*filpp); BUG_ON(!*filpp);
}
} else { /* open or lock stateid */ } else { /* open or lock stateid */
stp = find_stateid(stateid, flags); stp = find_stateid(stateid, flags);
if (!stp) if (!stp)
@ -4107,7 +4154,7 @@ nfs4_has_reclaimed_state(const char *name, bool use_exchange_id)
unsigned int strhashval = clientstr_hashval(name); unsigned int strhashval = clientstr_hashval(name);
struct nfs4_client *clp; struct nfs4_client *clp;
clp = find_confirmed_client_by_str(name, strhashval, use_exchange_id); clp = find_confirmed_client_by_str(name, strhashval);
return clp ? 1 : 0; return clp ? 1 : 0;
} }

View File

@ -44,13 +44,14 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/nfsd_idmap.h>
#include <linux/nfs4_acl.h>
#include <linux/sunrpc/svcauth_gss.h> #include <linux/sunrpc/svcauth_gss.h>
#include "idmap.h"
#include "acl.h"
#include "xdr4.h" #include "xdr4.h"
#include "vfs.h" #include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_XDR #define NFSDDBG_FACILITY NFSDDBG_XDR
/* /*
@ -288,17 +289,17 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
len += XDR_QUADLEN(dummy32) << 2; len += XDR_QUADLEN(dummy32) << 2;
READMEM(buf, dummy32); READMEM(buf, dummy32);
ace->whotype = nfs4_acl_get_whotype(buf, dummy32); ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
host_err = 0; status = nfs_ok;
if (ace->whotype != NFS4_ACL_WHO_NAMED) if (ace->whotype != NFS4_ACL_WHO_NAMED)
ace->who = 0; ace->who = 0;
else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
host_err = nfsd_map_name_to_gid(argp->rqstp, status = nfsd_map_name_to_gid(argp->rqstp,
buf, dummy32, &ace->who); buf, dummy32, &ace->who);
else else
host_err = nfsd_map_name_to_uid(argp->rqstp, status = nfsd_map_name_to_uid(argp->rqstp,
buf, dummy32, &ace->who); buf, dummy32, &ace->who);
if (host_err) if (status)
goto out_nfserr; return status;
} }
} else } else
*acl = NULL; *acl = NULL;
@ -420,6 +421,21 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access
DECODE_TAIL; DECODE_TAIL;
} }
static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts)
{
DECODE_HEAD;
u32 dummy;
READ_BUF(NFS4_MAX_SESSIONID_LEN + 8);
COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
READ32(bcts->dir);
/* XXX: Perhaps Tom Tucker could help us figure out how we
* should be using ctsa_use_conn_in_rdma_mode: */
READ32(dummy);
DECODE_TAIL;
}
static __be32 static __be32
nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
{ {
@ -846,6 +862,17 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
DECODE_TAIL; DECODE_TAIL;
} }
static __be32
nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
struct nfsd4_secinfo_no_name *sin)
{
DECODE_HEAD;
READ_BUF(4);
READ32(sin->sin_style);
DECODE_TAIL;
}
static __be32 static __be32
nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
{ {
@ -1005,7 +1032,7 @@ static __be32
nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
struct nfsd4_exchange_id *exid) struct nfsd4_exchange_id *exid)
{ {
int dummy; int dummy, tmp;
DECODE_HEAD; DECODE_HEAD;
READ_BUF(NFS4_VERIFIER_SIZE); READ_BUF(NFS4_VERIFIER_SIZE);
@ -1053,15 +1080,23 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
/* ssp_hash_algs<> */ /* ssp_hash_algs<> */
READ_BUF(4); READ_BUF(4);
READ32(dummy); READ32(tmp);
READ_BUF(dummy); while (tmp--) {
p += XDR_QUADLEN(dummy); READ_BUF(4);
READ32(dummy);
READ_BUF(dummy);
p += XDR_QUADLEN(dummy);
}
/* ssp_encr_algs<> */ /* ssp_encr_algs<> */
READ_BUF(4); READ_BUF(4);
READ32(dummy); READ32(tmp);
READ_BUF(dummy); while (tmp--) {
p += XDR_QUADLEN(dummy); READ_BUF(4);
READ32(dummy);
READ_BUF(dummy);
p += XDR_QUADLEN(dummy);
}
/* ssp_window and ssp_num_gss_handles */ /* ssp_window and ssp_num_gss_handles */
READ_BUF(8); READ_BUF(8);
@ -1339,7 +1374,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
/* new operations for NFSv4.1 */ /* new operations for NFSv4.1 */
[OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_notsupp, [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session,
[OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id,
[OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session,
[OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session, [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
@ -1350,7 +1385,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
[OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name,
[OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence, [OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence,
[OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp,
@ -2309,8 +2344,6 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
case nfserr_resource: case nfserr_resource:
nfserr = nfserr_toosmall; nfserr = nfserr_toosmall;
goto fail; goto fail;
case nfserr_dropit:
goto fail;
case nfserr_noent: case nfserr_noent:
goto skip_entry; goto skip_entry;
default: default:
@ -2365,6 +2398,21 @@ nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
return nfserr; return nfserr;
} }
static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts)
{
__be32 *p;
if (!nfserr) {
RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 8);
WRITEMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
WRITE32(bcts->dir);
/* XXX: ? */
WRITE32(0);
ADJUST_ARGS();
}
return nfserr;
}
static __be32 static __be32
nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close) nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
{ {
@ -2826,11 +2874,10 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
} }
static __be32 static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
struct nfsd4_secinfo *secinfo) __be32 nfserr,struct svc_export *exp)
{ {
int i = 0; int i = 0;
struct svc_export *exp = secinfo->si_exp;
u32 nflavs; u32 nflavs;
struct exp_flavor_info *flavs; struct exp_flavor_info *flavs;
struct exp_flavor_info def_flavs[2]; struct exp_flavor_info def_flavs[2];
@ -2892,6 +2939,20 @@ out:
return nfserr; return nfserr;
} }
static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_secinfo *secinfo)
{
return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->si_exp);
}
static __be32
nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_secinfo_no_name *secinfo)
{
return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->sin_exp);
}
/* /*
* The SETATTR encode routine is special -- it always encodes a bitmap, * The SETATTR encode routine is special -- it always encodes a bitmap,
* regardless of the error status. * regardless of the error status.
@ -3076,13 +3137,9 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
WRITE32(seq->seqid); WRITE32(seq->seqid);
WRITE32(seq->slotid); WRITE32(seq->slotid);
WRITE32(seq->maxslots); WRITE32(seq->maxslots);
/* /* For now: target_maxslots = maxslots */
* FIXME: for now:
* target_maxslots = maxslots
* status_flags = 0
*/
WRITE32(seq->maxslots); WRITE32(seq->maxslots);
WRITE32(0); WRITE32(seq->status_flags);
ADJUST_ARGS(); ADJUST_ARGS();
resp->cstate.datap = p; /* DRC cache data pointer */ resp->cstate.datap = p; /* DRC cache data pointer */
@ -3143,7 +3200,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
/* NFSv4.1 operations */ /* NFSv4.1 operations */
[OP_BACKCHANNEL_CTL] = (nfsd4_enc)nfsd4_encode_noop, [OP_BACKCHANNEL_CTL] = (nfsd4_enc)nfsd4_encode_noop,
[OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_noop, [OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session,
[OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id, [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id,
[OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session, [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session,
[OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session, [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
@ -3154,7 +3211,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
[OP_LAYOUTCOMMIT] = (nfsd4_enc)nfsd4_encode_noop, [OP_LAYOUTCOMMIT] = (nfsd4_enc)nfsd4_encode_noop,
[OP_LAYOUTGET] = (nfsd4_enc)nfsd4_encode_noop, [OP_LAYOUTGET] = (nfsd4_enc)nfsd4_encode_noop,
[OP_LAYOUTRETURN] = (nfsd4_enc)nfsd4_encode_noop, [OP_LAYOUTRETURN] = (nfsd4_enc)nfsd4_encode_noop,
[OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_noop, [OP_SECINFO_NO_NAME] = (nfsd4_enc)nfsd4_encode_secinfo_no_name,
[OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence, [OP_SEQUENCE] = (nfsd4_enc)nfsd4_encode_sequence,
[OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop, [OP_SET_SSV] = (nfsd4_enc)nfsd4_encode_noop,
[OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop, [OP_TEST_STATEID] = (nfsd4_enc)nfsd4_encode_noop,

View File

@ -8,12 +8,12 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/nfsd_idmap.h>
#include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/svcsock.h>
#include <linux/nfsd/syscall.h> #include <linux/nfsd/syscall.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include "idmap.h"
#include "nfsd.h" #include "nfsd.h"
#include "cache.h" #include "cache.h"
@ -127,6 +127,7 @@ 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 ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
{ {
#ifdef CONFIG_NFSD_DEPRECATED
static int warned; static int warned;
if (file->f_dentry->d_name.name[0] == '.' && !warned) { if (file->f_dentry->d_name.name[0] == '.' && !warned) {
printk(KERN_INFO printk(KERN_INFO
@ -135,6 +136,7 @@ static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size
current->comm, file->f_dentry->d_name.name); current->comm, file->f_dentry->d_name.name);
warned = 1; warned = 1;
} }
#endif
if (! file->private_data) { if (! file->private_data) {
/* An attempt to read a transaction file without writing /* An attempt to read a transaction file without writing
* causes a 0-byte write so that the file can return * causes a 0-byte write so that the file can return

View File

@ -158,6 +158,7 @@ void nfsd_lockd_shutdown(void);
#define nfserr_attrnotsupp cpu_to_be32(NFSERR_ATTRNOTSUPP) #define nfserr_attrnotsupp cpu_to_be32(NFSERR_ATTRNOTSUPP)
#define nfserr_bad_xdr cpu_to_be32(NFSERR_BAD_XDR) #define nfserr_bad_xdr cpu_to_be32(NFSERR_BAD_XDR)
#define nfserr_openmode cpu_to_be32(NFSERR_OPENMODE) #define nfserr_openmode cpu_to_be32(NFSERR_OPENMODE)
#define nfserr_badowner cpu_to_be32(NFSERR_BADOWNER)
#define nfserr_locks_held cpu_to_be32(NFSERR_LOCKS_HELD) #define nfserr_locks_held cpu_to_be32(NFSERR_LOCKS_HELD)
#define nfserr_op_illegal cpu_to_be32(NFSERR_OP_ILLEGAL) #define nfserr_op_illegal cpu_to_be32(NFSERR_OP_ILLEGAL)
#define nfserr_grace cpu_to_be32(NFSERR_GRACE) #define nfserr_grace cpu_to_be32(NFSERR_GRACE)

View File

@ -735,9 +735,9 @@ nfserrno (int errno)
{ nfserr_stale, -ESTALE }, { nfserr_stale, -ESTALE },
{ nfserr_jukebox, -ETIMEDOUT }, { nfserr_jukebox, -ETIMEDOUT },
{ nfserr_jukebox, -ERESTARTSYS }, { nfserr_jukebox, -ERESTARTSYS },
{ nfserr_dropit, -EAGAIN }, { nfserr_jukebox, -EAGAIN },
{ nfserr_dropit, -ENOMEM }, { nfserr_jukebox, -EWOULDBLOCK },
{ nfserr_badname, -ESRCH }, { nfserr_jukebox, -ENOMEM },
{ nfserr_io, -ETXTBSY }, { nfserr_io, -ETXTBSY },
{ nfserr_notsupp, -EOPNOTSUPP }, { nfserr_notsupp, -EOPNOTSUPP },
{ nfserr_toosmall, -ETOOSMALL }, { nfserr_toosmall, -ETOOSMALL },

View File

@ -608,7 +608,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
/* Now call the procedure handler, and encode NFS status. */ /* Now call the procedure handler, and encode NFS status. */
nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
nfserr = map_new_errors(rqstp->rq_vers, nfserr); nfserr = map_new_errors(rqstp->rq_vers, nfserr);
if (nfserr == nfserr_dropit) { if (nfserr == nfserr_dropit || rqstp->rq_dropme) {
dprintk("nfsd: Dropping request; may be revisited later\n"); dprintk("nfsd: Dropping request; may be revisited later\n");
nfsd_cache_update(rqstp, RC_NOCACHE, NULL); nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
return 0; return 0;

View File

@ -68,10 +68,12 @@ typedef struct {
struct nfsd4_callback { struct nfsd4_callback {
void *cb_op; void *cb_op;
struct nfs4_client *cb_clp; struct nfs4_client *cb_clp;
struct list_head cb_per_client;
u32 cb_minorversion; u32 cb_minorversion;
struct rpc_message cb_msg; struct rpc_message cb_msg;
const struct rpc_call_ops *cb_ops; const struct rpc_call_ops *cb_ops;
struct work_struct cb_work; struct work_struct cb_work;
bool cb_done;
}; };
struct nfs4_delegation { struct nfs4_delegation {
@ -81,6 +83,7 @@ struct nfs4_delegation {
atomic_t dl_count; /* ref count */ atomic_t dl_count; /* ref count */
struct nfs4_client *dl_client; struct nfs4_client *dl_client;
struct nfs4_file *dl_file; struct nfs4_file *dl_file;
struct file *dl_vfs_file;
struct file_lock *dl_flock; struct file_lock *dl_flock;
u32 dl_type; u32 dl_type;
time_t dl_time; time_t dl_time;
@ -95,6 +98,7 @@ struct nfs4_delegation {
struct nfs4_cb_conn { struct nfs4_cb_conn {
/* SETCLIENTID info */ /* SETCLIENTID info */
struct sockaddr_storage cb_addr; struct sockaddr_storage cb_addr;
struct sockaddr_storage cb_saddr;
size_t cb_addrlen; size_t cb_addrlen;
u32 cb_prog; /* used only in 4.0 case; u32 cb_prog; /* used only in 4.0 case;
per-session otherwise */ per-session otherwise */
@ -146,6 +150,11 @@ struct nfsd4_create_session {
u32 gid; u32 gid;
}; };
struct nfsd4_bind_conn_to_session {
struct nfs4_sessionid sessionid;
u32 dir;
};
/* The single slot clientid cache structure */ /* The single slot clientid cache structure */
struct nfsd4_clid_slot { struct nfsd4_clid_slot {
u32 sl_seqid; u32 sl_seqid;
@ -235,9 +244,13 @@ struct nfs4_client {
unsigned long cl_cb_flags; unsigned long cl_cb_flags;
struct rpc_clnt *cl_cb_client; struct rpc_clnt *cl_cb_client;
u32 cl_cb_ident; u32 cl_cb_ident;
atomic_t cl_cb_set; #define NFSD4_CB_UP 0
#define NFSD4_CB_UNKNOWN 1
#define NFSD4_CB_DOWN 2
int cl_cb_state;
struct nfsd4_callback cl_cb_null; struct nfsd4_callback cl_cb_null;
struct nfsd4_session *cl_cb_session; struct nfsd4_session *cl_cb_session;
struct list_head cl_callbacks; /* list of in-progress callbacks */
/* for all client information that callback code might need: */ /* for all client information that callback code might need: */
spinlock_t cl_lock; spinlock_t cl_lock;
@ -454,6 +467,7 @@ extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
extern void nfs4_free_stateowner(struct kref *kref); extern void nfs4_free_stateowner(struct kref *kref);
extern int set_callback_cred(void); extern int set_callback_cred(void);
extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_probe_callback(struct nfs4_client *clp);
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); 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_do_callback_rpc(struct work_struct *);
extern void nfsd4_cb_recall(struct nfs4_delegation *dp); extern void nfsd4_cb_recall(struct nfs4_delegation *dp);

View File

@ -1,4 +1,3 @@
#define MSNFS /* HACK HACK */
/* /*
* File operations used by nfsd. Some of these have been ripped from * File operations used by nfsd. Some of these have been ripped from
* other parts of the kernel because they weren't exported, others * other parts of the kernel because they weren't exported, others
@ -35,8 +34,8 @@
#endif /* CONFIG_NFSD_V3 */ #endif /* CONFIG_NFSD_V3 */
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
#include <linux/nfs4_acl.h> #include "acl.h"
#include <linux/nfsd_idmap.h> #include "idmap.h"
#endif /* CONFIG_NFSD_V4 */ #endif /* CONFIG_NFSD_V4 */
#include "nfsd.h" #include "nfsd.h"
@ -273,6 +272,13 @@ out:
return err; return err;
} }
static int nfsd_break_lease(struct inode *inode)
{
if (!S_ISREG(inode->i_mode))
return 0;
return break_lease(inode, O_WRONLY | O_NONBLOCK);
}
/* /*
* Commit metadata changes to stable storage. * Commit metadata changes to stable storage.
*/ */
@ -375,16 +381,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
goto out; goto out;
} }
/*
* If we are changing the size of the file, then
* we need to break all leases.
*/
host_err = break_lease(inode, O_WRONLY | O_NONBLOCK);
if (host_err == -EWOULDBLOCK)
host_err = -ETIMEDOUT;
if (host_err) /* ENOMEM or EWOULDBLOCK */
goto out_nfserr;
host_err = get_write_access(inode); host_err = get_write_access(inode);
if (host_err) if (host_err)
goto out_nfserr; goto out_nfserr;
@ -425,7 +421,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
err = nfserr_notsync; err = nfserr_notsync;
if (!check_guard || guardtime == inode->i_ctime.tv_sec) { if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
host_err = nfsd_break_lease(inode);
if (host_err)
goto out_nfserr;
fh_lock(fhp); fh_lock(fhp);
host_err = notify_change(dentry, iap); host_err = notify_change(dentry, iap);
err = nfserrno(host_err); err = nfserrno(host_err);
fh_unlock(fhp); fh_unlock(fhp);
@ -752,8 +752,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
*/ */
if (!(access & NFSD_MAY_NOT_BREAK_LEASE)) if (!(access & NFSD_MAY_NOT_BREAK_LEASE))
host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0)); host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0));
if (host_err == -EWOULDBLOCK)
host_err = -ETIMEDOUT;
if (host_err) /* NOMEM or WOULDBLOCK */ if (host_err) /* NOMEM or WOULDBLOCK */
goto out_nfserr; goto out_nfserr;
@ -874,15 +872,6 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
return __splice_from_pipe(pipe, sd, nfsd_splice_actor); return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
} }
static inline int svc_msnfs(struct svc_fh *ffhp)
{
#ifdef MSNFS
return (ffhp->fh_export->ex_flags & NFSEXP_MSNFS);
#else
return 0;
#endif
}
static __be32 static __be32
nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count) loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
@ -895,9 +884,6 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
err = nfserr_perm; err = nfserr_perm;
inode = file->f_path.dentry->d_inode; inode = file->f_path.dentry->d_inode;
if (svc_msnfs(fhp) && !lock_may_read(inode, offset, *count))
goto out;
if (file->f_op->splice_read && rqstp->rq_splice_ok) { if (file->f_op->splice_read && rqstp->rq_splice_ok) {
struct splice_desc sd = { struct splice_desc sd = {
.len = 0, .len = 0,
@ -922,7 +908,6 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
fsnotify_access(file); fsnotify_access(file);
} else } else
err = nfserrno(host_err); err = nfserrno(host_err);
out:
return err; return err;
} }
@ -987,14 +972,6 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
int stable = *stablep; int stable = *stablep;
int use_wgather; int use_wgather;
#ifdef MSNFS
err = nfserr_perm;
if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
(!lock_may_write(file->f_path.dentry->d_inode, offset, *cnt)))
goto out;
#endif
dentry = file->f_path.dentry; dentry = file->f_path.dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
exp = fhp->fh_export; exp = fhp->fh_export;
@ -1045,7 +1022,6 @@ out_nfserr:
err = 0; err = 0;
else else
err = nfserrno(host_err); err = nfserrno(host_err);
out:
return err; return err;
} }
@ -1665,6 +1641,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = nfserrno(host_err); err = nfserrno(host_err);
goto out_dput; goto out_dput;
} }
err = nfserr_noent;
if (!dold->d_inode)
goto out_drop_write;
host_err = nfsd_break_lease(dold->d_inode);
if (host_err)
goto out_drop_write;
host_err = vfs_link(dold, dirp, dnew); host_err = vfs_link(dold, dirp, dnew);
if (!host_err) { if (!host_err) {
err = nfserrno(commit_metadata(ffhp)); err = nfserrno(commit_metadata(ffhp));
@ -1676,6 +1658,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
else else
err = nfserrno(host_err); err = nfserrno(host_err);
} }
out_drop_write:
mnt_drop_write(tfhp->fh_export->ex_path.mnt); mnt_drop_write(tfhp->fh_export->ex_path.mnt);
out_dput: out_dput:
dput(dnew); dput(dnew);
@ -1750,12 +1733,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (ndentry == trap) if (ndentry == trap)
goto out_dput_new; goto out_dput_new;
if (svc_msnfs(ffhp) &&
((odentry->d_count > 1) || (ndentry->d_count > 1))) {
host_err = -EPERM;
goto out_dput_new;
}
host_err = -EXDEV; host_err = -EXDEV;
if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt) if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
goto out_dput_new; goto out_dput_new;
@ -1763,15 +1740,17 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (host_err) if (host_err)
goto out_dput_new; goto out_dput_new;
host_err = nfsd_break_lease(odentry->d_inode);
if (host_err)
goto out_drop_write;
host_err = vfs_rename(fdir, odentry, tdir, ndentry); host_err = vfs_rename(fdir, odentry, tdir, ndentry);
if (!host_err) { if (!host_err) {
host_err = commit_metadata(tfhp); host_err = commit_metadata(tfhp);
if (!host_err) if (!host_err)
host_err = commit_metadata(ffhp); host_err = commit_metadata(ffhp);
} }
out_drop_write:
mnt_drop_write(ffhp->fh_export->ex_path.mnt); mnt_drop_write(ffhp->fh_export->ex_path.mnt);
out_dput_new: out_dput_new:
dput(ndentry); dput(ndentry);
out_dput_old: out_dput_old:
@ -1834,18 +1813,14 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (host_err) if (host_err)
goto out_nfserr; goto out_nfserr;
if (type != S_IFDIR) { /* It's UNLINK */ host_err = nfsd_break_lease(rdentry->d_inode);
#ifdef MSNFS if (host_err)
if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && goto out_put;
(rdentry->d_count > 1)) { if (type != S_IFDIR)
host_err = -EPERM;
} else
#endif
host_err = vfs_unlink(dirp, rdentry); host_err = vfs_unlink(dirp, rdentry);
} else { /* It's RMDIR */ else
host_err = vfs_rmdir(dirp, rdentry); host_err = vfs_rmdir(dirp, rdentry);
} out_put:
dput(rdentry); dput(rdentry);
if (!host_err) if (!host_err)

View File

@ -311,6 +311,11 @@ struct nfsd4_secinfo {
struct svc_export *si_exp; /* response */ struct svc_export *si_exp; /* response */
}; };
struct nfsd4_secinfo_no_name {
u32 sin_style; /* request */
struct svc_export *sin_exp; /* response */
};
struct nfsd4_setattr { struct nfsd4_setattr {
stateid_t sa_stateid; /* request */ stateid_t sa_stateid; /* request */
u32 sa_bmval[3]; /* request */ u32 sa_bmval[3]; /* request */
@ -373,8 +378,8 @@ struct nfsd4_sequence {
u32 cachethis; /* request */ u32 cachethis; /* request */
#if 0 #if 0
u32 target_maxslots; /* response */ u32 target_maxslots; /* response */
u32 status_flags; /* response */
#endif /* not yet */ #endif /* not yet */
u32 status_flags; /* response */
}; };
struct nfsd4_destroy_session { struct nfsd4_destroy_session {
@ -422,6 +427,7 @@ struct nfsd4_op {
/* NFSv4.1 */ /* NFSv4.1 */
struct nfsd4_exchange_id exchange_id; struct nfsd4_exchange_id exchange_id;
struct nfsd4_bind_conn_to_session bind_conn_to_session;
struct nfsd4_create_session create_session; struct nfsd4_create_session create_session;
struct nfsd4_destroy_session destroy_session; struct nfsd4_destroy_session destroy_session;
struct nfsd4_sequence sequence; struct nfsd4_sequence sequence;
@ -518,6 +524,7 @@ extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
struct nfsd4_sequence *seq); struct nfsd4_sequence *seq);
extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_exchange_id *); struct nfsd4_compound_state *, struct nfsd4_exchange_id *);
extern __be32 nfsd4_bind_conn_to_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_bind_conn_to_session *);
extern __be32 nfsd4_create_session(struct svc_rqst *, extern __be32 nfsd4_create_session(struct svc_rqst *,
struct nfsd4_compound_state *, struct nfsd4_compound_state *,
struct nfsd4_create_session *); struct nfsd4_create_session *);

View File

@ -1066,7 +1066,6 @@ struct lock_manager_operations {
int (*fl_grant)(struct file_lock *, struct file_lock *, int); int (*fl_grant)(struct file_lock *, struct file_lock *, int);
void (*fl_release_private)(struct file_lock *); void (*fl_release_private)(struct file_lock *);
void (*fl_break)(struct file_lock *); void (*fl_break)(struct file_lock *);
int (*fl_mylease)(struct file_lock *, struct file_lock *);
int (*fl_change)(struct file_lock **, int); int (*fl_change)(struct file_lock **, int);
}; };

View File

@ -65,6 +65,9 @@
#define NFS4_CDFC4_FORE 0x1 #define NFS4_CDFC4_FORE 0x1
#define NFS4_CDFC4_BACK 0x2 #define NFS4_CDFC4_BACK 0x2
#define NFS4_CDFC4_BOTH 0x3
#define NFS4_CDFC4_FORE_OR_BOTH 0x3
#define NFS4_CDFC4_BACK_OR_BOTH 0x7
#define NFS4_SET_TO_SERVER_TIME 0 #define NFS4_SET_TO_SERVER_TIME 0
#define NFS4_SET_TO_CLIENT_TIME 1 #define NFS4_SET_TO_CLIENT_TIME 1
@ -140,6 +143,9 @@
#define SEQ4_STATUS_CB_PATH_DOWN_SESSION 0x00000200 #define SEQ4_STATUS_CB_PATH_DOWN_SESSION 0x00000200
#define SEQ4_STATUS_BACKCHANNEL_FAULT 0x00000400 #define SEQ4_STATUS_BACKCHANNEL_FAULT 0x00000400
#define NFS4_SECINFO_STYLE4_CURRENT_FH 0
#define NFS4_SECINFO_STYLE4_PARENT 1
#define NFS4_MAX_UINT64 (~(u64)0) #define NFS4_MAX_UINT64 (~(u64)0)
/* An NFS4 sessions server must support at least NFS4_MAX_OPS operations. /* An NFS4 sessions server must support at least NFS4_MAX_OPS operations.

View File

@ -35,7 +35,7 @@
#define NFSEXP_NOHIDE 0x0200 #define NFSEXP_NOHIDE 0x0200
#define NFSEXP_NOSUBTREECHECK 0x0400 #define NFSEXP_NOSUBTREECHECK 0x0400
#define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */ #define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */
#define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect */ #define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect; no longer supported */
#define NFSEXP_FSID 0x2000 #define NFSEXP_FSID 0x2000
#define NFSEXP_CROSSMOUNT 0x4000 #define NFSEXP_CROSSMOUNT 0x4000
#define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */ #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */

View File

@ -256,10 +256,13 @@ static inline time_t get_expiry(char **bpp)
return rv - boot.tv_sec; return rv - boot.tv_sec;
} }
#ifdef CONFIG_NFSD_DEPRECATED
static inline void sunrpc_invalidate(struct cache_head *h, static inline void sunrpc_invalidate(struct cache_head *h,
struct cache_detail *detail) struct cache_detail *detail)
{ {
h->expiry_time = seconds_since_boot() - 1; h->expiry_time = seconds_since_boot() - 1;
detail->nextcheck = seconds_since_boot(); detail->nextcheck = seconds_since_boot();
} }
#endif /* CONFIG_NFSD_DEPRECATED */
#endif /* _LINUX_SUNRPC_CACHE_H_ */ #endif /* _LINUX_SUNRPC_CACHE_H_ */

View File

@ -269,6 +269,7 @@ struct svc_rqst {
struct cache_req rq_chandle; /* handle passed to caches for struct cache_req rq_chandle; /* handle passed to caches for
* request delaying * request delaying
*/ */
bool rq_dropme;
/* Catering to nfsd */ /* Catering to nfsd */
struct auth_domain * rq_client; /* RPC peer info */ struct auth_domain * rq_client; /* RPC peer info */
struct auth_domain * rq_gssclient; /* "gss/"-style peer info */ struct auth_domain * rq_gssclient; /* "gss/"-style peer info */

View File

@ -63,7 +63,6 @@ struct svc_xprt {
#define XPT_LISTENER 11 /* listening endpoint */ #define XPT_LISTENER 11 /* listening endpoint */
#define XPT_CACHE_AUTH 12 /* cache auth info */ #define XPT_CACHE_AUTH 12 /* cache auth info */
struct svc_pool *xpt_pool; /* current pool iff queued */
struct svc_serv *xpt_server; /* service for transport */ struct svc_serv *xpt_server; /* service for transport */
atomic_t xpt_reserved; /* space on outq that is rsvd */ atomic_t xpt_reserved; /* space on outq that is rsvd */
struct mutex xpt_mutex; /* to serialize sending data */ struct mutex xpt_mutex; /* to serialize sending data */
@ -81,6 +80,7 @@ struct svc_xprt {
void *xpt_bc_sid; /* back channel session ID */ void *xpt_bc_sid; /* back channel session ID */
struct net *xpt_net; struct net *xpt_net;
struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */
}; };
static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u) static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)

View File

@ -28,7 +28,6 @@ struct svc_sock {
/* private TCP part */ /* private TCP part */
u32 sk_reclen; /* length of record */ u32 sk_reclen; /* length of record */
u32 sk_tcplen; /* current read length */ u32 sk_tcplen; /* current read length */
struct rpc_xprt *sk_bc_xprt; /* NFSv4.1 backchannel xprt */
}; };
/* /*

View File

@ -321,6 +321,7 @@ void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);
#define XPRT_CLOSING (6) #define XPRT_CLOSING (6)
#define XPRT_CONNECTION_ABORT (7) #define XPRT_CONNECTION_ABORT (7)
#define XPRT_CONNECTION_CLOSE (8) #define XPRT_CONNECTION_CLOSE (8)
#define XPRT_INITIALIZED (9)
static inline void xprt_set_connected(struct rpc_xprt *xprt) static inline void xprt_set_connected(struct rpc_xprt *xprt)
{ {

View File

@ -137,7 +137,7 @@ arcfour_hmac_md5_usage_to_salt(unsigned int usage, u8 salt[4])
ms_usage = 13; ms_usage = 13;
break; break;
default: default:
return EINVAL;; return -EINVAL;
} }
salt[0] = (ms_usage >> 0) & 0xff; salt[0] = (ms_usage >> 0) & 0xff;
salt[1] = (ms_usage >> 8) & 0xff; salt[1] = (ms_usage >> 8) & 0xff;

View File

@ -67,7 +67,6 @@ static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)
#define RSI_HASHBITS 6 #define RSI_HASHBITS 6
#define RSI_HASHMAX (1<<RSI_HASHBITS) #define RSI_HASHMAX (1<<RSI_HASHBITS)
#define RSI_HASHMASK (RSI_HASHMAX-1)
struct rsi { struct rsi {
struct cache_head h; struct cache_head h;
@ -319,7 +318,6 @@ static struct rsi *rsi_update(struct rsi *new, struct rsi *old)
#define RSC_HASHBITS 10 #define RSC_HASHBITS 10
#define RSC_HASHMAX (1<<RSC_HASHBITS) #define RSC_HASHMAX (1<<RSC_HASHBITS)
#define RSC_HASHMASK (RSC_HASHMAX-1)
#define GSS_SEQ_WIN 128 #define GSS_SEQ_WIN 128

View File

@ -37,7 +37,7 @@
#define RPCDBG_FACILITY RPCDBG_CACHE #define RPCDBG_FACILITY RPCDBG_CACHE
static void cache_defer_req(struct cache_req *req, struct cache_head *item); static bool cache_defer_req(struct cache_req *req, struct cache_head *item);
static void cache_revisit_request(struct cache_head *item); static void cache_revisit_request(struct cache_head *item);
static void cache_init(struct cache_head *h) static void cache_init(struct cache_head *h)
@ -128,6 +128,7 @@ static void cache_fresh_locked(struct cache_head *head, time_t expiry)
{ {
head->expiry_time = expiry; head->expiry_time = expiry;
head->last_refresh = seconds_since_boot(); head->last_refresh = seconds_since_boot();
smp_wmb(); /* paired with smp_rmb() in cache_is_valid() */
set_bit(CACHE_VALID, &head->flags); set_bit(CACHE_VALID, &head->flags);
} }
@ -208,11 +209,36 @@ static inline int cache_is_valid(struct cache_detail *detail, struct cache_head
/* entry is valid */ /* entry is valid */
if (test_bit(CACHE_NEGATIVE, &h->flags)) if (test_bit(CACHE_NEGATIVE, &h->flags))
return -ENOENT; return -ENOENT;
else else {
/*
* In combination with write barrier in
* sunrpc_cache_update, ensures that anyone
* using the cache entry after this sees the
* updated contents:
*/
smp_rmb();
return 0; return 0;
}
} }
} }
static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h)
{
int rv;
write_lock(&detail->hash_lock);
rv = cache_is_valid(detail, h);
if (rv != -EAGAIN) {
write_unlock(&detail->hash_lock);
return rv;
}
set_bit(CACHE_NEGATIVE, &h->flags);
cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
write_unlock(&detail->hash_lock);
cache_fresh_unlocked(h, detail);
return -ENOENT;
}
/* /*
* This is the generic cache management routine for all * This is the generic cache management routine for all
* the authentication caches. * the authentication caches.
@ -251,14 +277,8 @@ int cache_check(struct cache_detail *detail,
case -EINVAL: case -EINVAL:
clear_bit(CACHE_PENDING, &h->flags); clear_bit(CACHE_PENDING, &h->flags);
cache_revisit_request(h); cache_revisit_request(h);
if (rv == -EAGAIN) { rv = try_to_negate_entry(detail, h);
set_bit(CACHE_NEGATIVE, &h->flags);
cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
cache_fresh_unlocked(h, detail);
rv = -ENOENT;
}
break; break;
case -EAGAIN: case -EAGAIN:
clear_bit(CACHE_PENDING, &h->flags); clear_bit(CACHE_PENDING, &h->flags);
cache_revisit_request(h); cache_revisit_request(h);
@ -268,9 +288,11 @@ int cache_check(struct cache_detail *detail,
} }
if (rv == -EAGAIN) { if (rv == -EAGAIN) {
cache_defer_req(rqstp, h); if (!cache_defer_req(rqstp, h)) {
if (!test_bit(CACHE_PENDING, &h->flags)) { /*
/* Request is not deferred */ * Request was not deferred; handle it as best
* we can ourselves:
*/
rv = cache_is_valid(detail, h); rv = cache_is_valid(detail, h);
if (rv == -EAGAIN) if (rv == -EAGAIN)
rv = -ETIMEDOUT; rv = -ETIMEDOUT;
@ -618,18 +640,19 @@ static void cache_limit_defers(void)
discard->revisit(discard, 1); discard->revisit(discard, 1);
} }
static void cache_defer_req(struct cache_req *req, struct cache_head *item) /* Return true if and only if a deferred request is queued. */
static bool cache_defer_req(struct cache_req *req, struct cache_head *item)
{ {
struct cache_deferred_req *dreq; struct cache_deferred_req *dreq;
if (req->thread_wait) { if (req->thread_wait) {
cache_wait_req(req, item); cache_wait_req(req, item);
if (!test_bit(CACHE_PENDING, &item->flags)) if (!test_bit(CACHE_PENDING, &item->flags))
return; return false;
} }
dreq = req->defer(req); dreq = req->defer(req);
if (dreq == NULL) if (dreq == NULL)
return; return false;
setup_deferral(dreq, item, 1); setup_deferral(dreq, item, 1);
if (!test_bit(CACHE_PENDING, &item->flags)) if (!test_bit(CACHE_PENDING, &item->flags))
/* Bit could have been cleared before we managed to /* Bit could have been cleared before we managed to
@ -638,6 +661,7 @@ static void cache_defer_req(struct cache_req *req, struct cache_head *item)
cache_revisit_request(item); cache_revisit_request(item);
cache_limit_defers(); cache_limit_defers();
return true;
} }
static void cache_revisit_request(struct cache_head *item) static void cache_revisit_request(struct cache_head *item)

View File

@ -1001,6 +1001,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
rqstp->rq_splice_ok = 1; rqstp->rq_splice_ok = 1;
/* Will be turned off only when NFSv4 Sessions are used */ /* Will be turned off only when NFSv4 Sessions are used */
rqstp->rq_usedeferral = 1; rqstp->rq_usedeferral = 1;
rqstp->rq_dropme = false;
/* Setup reply header */ /* Setup reply header */
rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp); rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp);
@ -1102,7 +1103,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
*statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); *statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
/* Encode reply */ /* Encode reply */
if (*statp == rpc_drop_reply) { if (rqstp->rq_dropme) {
if (procp->pc_release) if (procp->pc_release)
procp->pc_release(rqstp, NULL, rqstp->rq_resp); procp->pc_release(rqstp, NULL, rqstp->rq_resp);
goto dropit; goto dropit;

View File

@ -13,6 +13,7 @@
#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> #include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/xprt.h>
#define RPCDBG_FACILITY RPCDBG_SVCXPRT #define RPCDBG_FACILITY RPCDBG_SVCXPRT
@ -128,6 +129,9 @@ static void svc_xprt_free(struct kref *kref)
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))
svcauth_unix_info_release(xprt); svcauth_unix_info_release(xprt);
put_net(xprt->xpt_net); put_net(xprt->xpt_net);
/* See comment on corresponding get in xs_setup_bc_tcp(): */
if (xprt->xpt_bc_xprt)
xprt_put(xprt->xpt_bc_xprt);
xprt->xpt_ops->xpo_free(xprt); xprt->xpt_ops->xpo_free(xprt);
module_put(owner); module_put(owner);
} }
@ -303,6 +307,15 @@ static void svc_thread_dequeue(struct svc_pool *pool, struct svc_rqst *rqstp)
list_del(&rqstp->rq_list); list_del(&rqstp->rq_list);
} }
static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt)
{
if (xprt->xpt_flags & ((1<<XPT_CONN)|(1<<XPT_CLOSE)))
return true;
if (xprt->xpt_flags & ((1<<XPT_DATA)|(1<<XPT_DEFERRED)))
return xprt->xpt_ops->xpo_has_wspace(xprt);
return false;
}
/* /*
* Queue up a transport with data pending. If there are idle nfsd * Queue up a transport with data pending. If there are idle nfsd
* processes, wake 'em up. * processes, wake 'em up.
@ -315,8 +328,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
struct svc_rqst *rqstp; struct svc_rqst *rqstp;
int cpu; int cpu;
if (!(xprt->xpt_flags & if (!svc_xprt_has_something_to_do(xprt))
((1<<XPT_CONN)|(1<<XPT_DATA)|(1<<XPT_CLOSE)|(1<<XPT_DEFERRED))))
return; return;
cpu = get_cpu(); cpu = get_cpu();
@ -343,28 +355,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
dprintk("svc: transport %p busy, not enqueued\n", xprt); dprintk("svc: transport %p busy, not enqueued\n", xprt);
goto out_unlock; goto out_unlock;
} }
BUG_ON(xprt->xpt_pool != NULL);
xprt->xpt_pool = pool;
/* Handle pending connection */
if (test_bit(XPT_CONN, &xprt->xpt_flags))
goto process;
/* Handle close in-progress */
if (test_bit(XPT_CLOSE, &xprt->xpt_flags))
goto process;
/* Check if we have space to reply to a request */
if (!xprt->xpt_ops->xpo_has_wspace(xprt)) {
/* Don't enqueue while not enough space for reply */
dprintk("svc: no write space, transport %p not enqueued\n",
xprt);
xprt->xpt_pool = NULL;
clear_bit(XPT_BUSY, &xprt->xpt_flags);
goto out_unlock;
}
process:
if (!list_empty(&pool->sp_threads)) { if (!list_empty(&pool->sp_threads)) {
rqstp = list_entry(pool->sp_threads.next, rqstp = list_entry(pool->sp_threads.next,
struct svc_rqst, struct svc_rqst,
@ -381,13 +372,11 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
rqstp->rq_reserved = serv->sv_max_mesg; rqstp->rq_reserved = serv->sv_max_mesg;
atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved); atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
pool->sp_stats.threads_woken++; pool->sp_stats.threads_woken++;
BUG_ON(xprt->xpt_pool != pool);
wake_up(&rqstp->rq_wait); wake_up(&rqstp->rq_wait);
} else { } else {
dprintk("svc: transport %p put into queue\n", xprt); dprintk("svc: transport %p put into queue\n", xprt);
list_add_tail(&xprt->xpt_ready, &pool->sp_sockets); list_add_tail(&xprt->xpt_ready, &pool->sp_sockets);
pool->sp_stats.sockets_queued++; pool->sp_stats.sockets_queued++;
BUG_ON(xprt->xpt_pool != pool);
} }
out_unlock: out_unlock:
@ -426,7 +415,6 @@ static struct svc_xprt *svc_xprt_dequeue(struct svc_pool *pool)
void svc_xprt_received(struct svc_xprt *xprt) void svc_xprt_received(struct svc_xprt *xprt)
{ {
BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags)); BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags));
xprt->xpt_pool = NULL;
/* As soon as we clear busy, the xprt could be closed and /* As soon as we clear busy, the xprt could be closed and
* 'put', so we need a reference to call svc_xprt_enqueue with: * 'put', so we need a reference to call svc_xprt_enqueue with:
*/ */
@ -722,7 +710,10 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) { if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) {
dprintk("svc_recv: found XPT_CLOSE\n"); dprintk("svc_recv: found XPT_CLOSE\n");
svc_delete_xprt(xprt); svc_delete_xprt(xprt);
} else if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) { /* Leave XPT_BUSY set on the dead xprt: */
goto out;
}
if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) {
struct svc_xprt *newxpt; struct svc_xprt *newxpt;
newxpt = xprt->xpt_ops->xpo_accept(xprt); newxpt = xprt->xpt_ops->xpo_accept(xprt);
if (newxpt) { if (newxpt) {
@ -747,28 +738,23 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
spin_unlock_bh(&serv->sv_lock); spin_unlock_bh(&serv->sv_lock);
svc_xprt_received(newxpt); svc_xprt_received(newxpt);
} }
svc_xprt_received(xprt); } else if (xprt->xpt_ops->xpo_has_wspace(xprt)) {
} else {
dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n", dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",
rqstp, pool->sp_id, xprt, rqstp, pool->sp_id, xprt,
atomic_read(&xprt->xpt_ref.refcount)); atomic_read(&xprt->xpt_ref.refcount));
rqstp->rq_deferred = svc_deferred_dequeue(xprt); rqstp->rq_deferred = svc_deferred_dequeue(xprt);
if (rqstp->rq_deferred) { if (rqstp->rq_deferred)
svc_xprt_received(xprt);
len = svc_deferred_recv(rqstp); len = svc_deferred_recv(rqstp);
} else { else
len = xprt->xpt_ops->xpo_recvfrom(rqstp); len = xprt->xpt_ops->xpo_recvfrom(rqstp);
svc_xprt_received(xprt);
}
dprintk("svc: got len=%d\n", len); dprintk("svc: got len=%d\n", len);
} }
svc_xprt_received(xprt);
/* No data, incomplete (TCP) read, or accept() */ /* No data, incomplete (TCP) read, or accept() */
if (len == 0 || len == -EAGAIN) { if (len == 0 || len == -EAGAIN)
rqstp->rq_res.len = 0; goto out;
svc_xprt_release(rqstp);
return -EAGAIN;
}
clear_bit(XPT_OLD, &xprt->xpt_flags); clear_bit(XPT_OLD, &xprt->xpt_flags);
rqstp->rq_secure = svc_port_is_privileged(svc_addr(rqstp)); rqstp->rq_secure = svc_port_is_privileged(svc_addr(rqstp));
@ -777,6 +763,10 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
if (serv->sv_stats) if (serv->sv_stats)
serv->sv_stats->netcnt++; serv->sv_stats->netcnt++;
return len; return len;
out:
rqstp->rq_res.len = 0;
svc_xprt_release(rqstp);
return -EAGAIN;
} }
EXPORT_SYMBOL_GPL(svc_recv); EXPORT_SYMBOL_GPL(svc_recv);
@ -935,7 +925,12 @@ void svc_close_xprt(struct svc_xprt *xprt)
if (test_and_set_bit(XPT_BUSY, &xprt->xpt_flags)) if (test_and_set_bit(XPT_BUSY, &xprt->xpt_flags))
/* someone else will have to effect the close */ /* someone else will have to effect the close */
return; return;
/*
* We expect svc_close_xprt() to work even when no threads are
* running (e.g., while configuring the server before starting
* any threads), so if the transport isn't busy, we delete
* it ourself:
*/
svc_delete_xprt(xprt); svc_delete_xprt(xprt);
} }
EXPORT_SYMBOL_GPL(svc_close_xprt); EXPORT_SYMBOL_GPL(svc_close_xprt);
@ -945,16 +940,16 @@ void svc_close_all(struct list_head *xprt_list)
struct svc_xprt *xprt; struct svc_xprt *xprt;
struct svc_xprt *tmp; struct svc_xprt *tmp;
/*
* The server is shutting down, and no more threads are running.
* svc_xprt_enqueue() might still be running, but at worst it
* will re-add the xprt to sp_sockets, which will soon get
* freed. So we don't bother with any more locking, and don't
* leave the close to the (nonexistent) server threads:
*/
list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) { list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) {
set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_CLOSE, &xprt->xpt_flags);
if (test_bit(XPT_BUSY, &xprt->xpt_flags)) { svc_delete_xprt(xprt);
/* Waiting to be processed, but no threads left,
* So just remove it from the waiting list
*/
list_del_init(&xprt->xpt_ready);
clear_bit(XPT_BUSY, &xprt->xpt_flags);
}
svc_close_xprt(xprt);
} }
} }
@ -1028,6 +1023,7 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)
} }
svc_xprt_get(rqstp->rq_xprt); svc_xprt_get(rqstp->rq_xprt);
dr->xprt = rqstp->rq_xprt; dr->xprt = rqstp->rq_xprt;
rqstp->rq_dropme = true;
dr->handle.revisit = svc_revisit; dr->handle.revisit = svc_revisit;
return &dr->handle; return &dr->handle;
@ -1065,14 +1061,13 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt)
if (!test_bit(XPT_DEFERRED, &xprt->xpt_flags)) if (!test_bit(XPT_DEFERRED, &xprt->xpt_flags))
return NULL; return NULL;
spin_lock(&xprt->xpt_lock); spin_lock(&xprt->xpt_lock);
clear_bit(XPT_DEFERRED, &xprt->xpt_flags);
if (!list_empty(&xprt->xpt_deferred)) { if (!list_empty(&xprt->xpt_deferred)) {
dr = list_entry(xprt->xpt_deferred.next, dr = list_entry(xprt->xpt_deferred.next,
struct svc_deferred_req, struct svc_deferred_req,
handle.recent); handle.recent);
list_del_init(&dr->handle.recent); list_del_init(&dr->handle.recent);
set_bit(XPT_DEFERRED, &xprt->xpt_flags); } else
} clear_bit(XPT_DEFERRED, &xprt->xpt_flags);
spin_unlock(&xprt->xpt_lock); spin_unlock(&xprt->xpt_lock);
return dr; return dr;
} }

View File

@ -118,7 +118,6 @@ EXPORT_SYMBOL_GPL(svc_auth_unregister);
#define DN_HASHBITS 6 #define DN_HASHBITS 6
#define DN_HASHMAX (1<<DN_HASHBITS) #define DN_HASHMAX (1<<DN_HASHBITS)
#define DN_HASHMASK (DN_HASHMAX-1)
static struct hlist_head auth_domain_table[DN_HASHMAX]; static struct hlist_head auth_domain_table[DN_HASHMAX];
static spinlock_t auth_domain_lock = static spinlock_t auth_domain_lock =

View File

@ -30,7 +30,9 @@
struct unix_domain { struct unix_domain {
struct auth_domain h; struct auth_domain h;
#ifdef CONFIG_NFSD_DEPRECATED
int addr_changes; int addr_changes;
#endif /* CONFIG_NFSD_DEPRECATED */
/* other stuff later */ /* other stuff later */
}; };
@ -64,7 +66,9 @@ struct auth_domain *unix_domain_find(char *name)
return NULL; return NULL;
} }
new->h.flavour = &svcauth_unix; new->h.flavour = &svcauth_unix;
#ifdef CONFIG_NFSD_DEPRECATED
new->addr_changes = 0; new->addr_changes = 0;
#endif /* CONFIG_NFSD_DEPRECATED */
rv = auth_domain_lookup(name, &new->h); rv = auth_domain_lookup(name, &new->h);
} }
} }
@ -85,14 +89,15 @@ static void svcauth_unix_domain_release(struct auth_domain *dom)
*/ */
#define IP_HASHBITS 8 #define IP_HASHBITS 8
#define IP_HASHMAX (1<<IP_HASHBITS) #define IP_HASHMAX (1<<IP_HASHBITS)
#define IP_HASHMASK (IP_HASHMAX-1)
struct ip_map { struct ip_map {
struct cache_head h; struct cache_head h;
char m_class[8]; /* e.g. "nfsd" */ char m_class[8]; /* e.g. "nfsd" */
struct in6_addr m_addr; struct in6_addr m_addr;
struct unix_domain *m_client; struct unix_domain *m_client;
#ifdef CONFIG_NFSD_DEPRECATED
int m_add_change; int m_add_change;
#endif /* CONFIG_NFSD_DEPRECATED */
}; };
static void ip_map_put(struct kref *kref) static void ip_map_put(struct kref *kref)
@ -146,7 +151,9 @@ static void update(struct cache_head *cnew, struct cache_head *citem)
kref_get(&item->m_client->h.ref); kref_get(&item->m_client->h.ref);
new->m_client = item->m_client; new->m_client = item->m_client;
#ifdef CONFIG_NFSD_DEPRECATED
new->m_add_change = item->m_add_change; new->m_add_change = item->m_add_change;
#endif /* CONFIG_NFSD_DEPRECATED */
} }
static struct cache_head *ip_map_alloc(void) static struct cache_head *ip_map_alloc(void)
{ {
@ -331,6 +338,7 @@ static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
ip.h.flags = 0; ip.h.flags = 0;
if (!udom) if (!udom)
set_bit(CACHE_NEGATIVE, &ip.h.flags); set_bit(CACHE_NEGATIVE, &ip.h.flags);
#ifdef CONFIG_NFSD_DEPRECATED
else { else {
ip.m_add_change = udom->addr_changes; ip.m_add_change = udom->addr_changes;
/* if this is from the legacy set_client system call, /* if this is from the legacy set_client system call,
@ -339,6 +347,7 @@ static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
if (expiry == NEVER) if (expiry == NEVER)
ip.m_add_change++; ip.m_add_change++;
} }
#endif /* CONFIG_NFSD_DEPRECATED */
ip.h.expiry_time = expiry; ip.h.expiry_time = expiry;
ch = sunrpc_cache_update(cd, &ip.h, &ipm->h, ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
hash_str(ipm->m_class, IP_HASHBITS) ^ hash_str(ipm->m_class, IP_HASHBITS) ^
@ -358,6 +367,7 @@ static inline int ip_map_update(struct net *net, struct ip_map *ipm,
return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry); return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
} }
#ifdef CONFIG_NFSD_DEPRECATED
int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom) int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom)
{ {
struct unix_domain *udom; struct unix_domain *udom;
@ -402,8 +412,7 @@ struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)
return NULL; return NULL;
if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) { if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) {
if (test_and_set_bit(CACHE_NEGATIVE, &ipm->h.flags) == 0) sunrpc_invalidate(&ipm->h, sn->ip_map_cache);
auth_domain_put(&ipm->m_client->h);
rv = NULL; rv = NULL;
} else { } else {
rv = &ipm->m_client->h; rv = &ipm->m_client->h;
@ -413,6 +422,7 @@ struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)
return rv; return rv;
} }
EXPORT_SYMBOL_GPL(auth_unix_lookup); EXPORT_SYMBOL_GPL(auth_unix_lookup);
#endif /* CONFIG_NFSD_DEPRECATED */
void svcauth_unix_purge(void) void svcauth_unix_purge(void)
{ {
@ -497,7 +507,6 @@ svcauth_unix_info_release(struct svc_xprt *xpt)
*/ */
#define GID_HASHBITS 8 #define GID_HASHBITS 8
#define GID_HASHMAX (1<<GID_HASHBITS) #define GID_HASHMAX (1<<GID_HASHBITS)
#define GID_HASHMASK (GID_HASHMAX - 1)
struct unix_gid { struct unix_gid {
struct cache_head h; struct cache_head h;

View File

@ -331,19 +331,21 @@ int svc_sock_names(struct svc_serv *serv, char *buf, const size_t buflen,
len = onelen; len = onelen;
break; break;
} }
if (toclose && strcmp(toclose, buf + len) == 0) if (toclose && strcmp(toclose, buf + len) == 0) {
closesk = svsk; closesk = svsk;
else svc_xprt_get(&closesk->sk_xprt);
} 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...
*/ */
svc_close_xprt(&closesk->sk_xprt); svc_close_xprt(&closesk->sk_xprt);
else if (toclose) svc_xprt_put(&closesk->sk_xprt);
} else if (toclose)
return -ENOENT; return -ENOENT;
return len; return len;
} }
@ -992,15 +994,17 @@ static int svc_process_calldir(struct svc_sock *svsk, struct svc_rqst *rqstp,
vec[0] = rqstp->rq_arg.head[0]; vec[0] = rqstp->rq_arg.head[0];
} else { } else {
/* REPLY */ /* REPLY */
if (svsk->sk_bc_xprt) struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt;
req = xprt_lookup_rqst(svsk->sk_bc_xprt, xid);
if (bc_xprt)
req = xprt_lookup_rqst(bc_xprt, xid);
if (!req) { if (!req) {
printk(KERN_NOTICE printk(KERN_NOTICE
"%s: Got unrecognized reply: " "%s: Got unrecognized reply: "
"calldir 0x%x sk_bc_xprt %p xid %08x\n", "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
__func__, ntohl(calldir), __func__, ntohl(calldir),
svsk->sk_bc_xprt, xid); bc_xprt, xid);
vec[0] = rqstp->rq_arg.head[0]; vec[0] = rqstp->rq_arg.head[0];
goto out; goto out;
} }

View File

@ -965,6 +965,7 @@ struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req)
xprt = kzalloc(size, GFP_KERNEL); xprt = kzalloc(size, GFP_KERNEL);
if (xprt == NULL) if (xprt == NULL)
goto out; goto out;
kref_init(&xprt->kref);
xprt->max_reqs = max_req; xprt->max_reqs = max_req;
xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL); xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL);
@ -1101,8 +1102,10 @@ found:
-PTR_ERR(xprt)); -PTR_ERR(xprt));
return xprt; return xprt;
} }
if (test_and_set_bit(XPRT_INITIALIZED, &xprt->state))
/* ->setup returned a pre-initialized xprt: */
return xprt;
kref_init(&xprt->kref);
spin_lock_init(&xprt->transport_lock); spin_lock_init(&xprt->transport_lock);
spin_lock_init(&xprt->reserve_lock); spin_lock_init(&xprt->reserve_lock);

View File

@ -2359,6 +2359,15 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
struct svc_sock *bc_sock; struct svc_sock *bc_sock;
struct rpc_xprt *ret; struct rpc_xprt *ret;
if (args->bc_xprt->xpt_bc_xprt) {
/*
* This server connection already has a backchannel
* export; we can't create a new one, as we wouldn't be
* able to match replies based on xid any more. So,
* reuse the already-existing one:
*/
return args->bc_xprt->xpt_bc_xprt;
}
xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries); xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);
if (IS_ERR(xprt)) if (IS_ERR(xprt))
return xprt; return xprt;
@ -2375,16 +2384,6 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
xprt->reestablish_timeout = 0; xprt->reestablish_timeout = 0;
xprt->idle_timeout = 0; xprt->idle_timeout = 0;
/*
* The backchannel uses the same socket connection as the
* forechannel
*/
xprt->bc_xprt = args->bc_xprt;
bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt);
bc_sock->sk_bc_xprt = xprt;
transport->sock = bc_sock->sk_sock;
transport->inet = bc_sock->sk_sk;
xprt->ops = &bc_tcp_ops; xprt->ops = &bc_tcp_ops;
switch (addr->sa_family) { switch (addr->sa_family) {
@ -2406,6 +2405,20 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
xprt->address_strings[RPC_DISPLAY_PORT], xprt->address_strings[RPC_DISPLAY_PORT],
xprt->address_strings[RPC_DISPLAY_PROTO]); xprt->address_strings[RPC_DISPLAY_PROTO]);
/*
* Once we've associated a backchannel xprt with a connection,
* we want to keep it around as long as long as the connection
* lasts, in case we need to start using it for a backchannel
* again; this reference won't be dropped until bc_xprt is
* destroyed.
*/
xprt_get(xprt);
args->bc_xprt->xpt_bc_xprt = xprt;
xprt->bc_xprt = args->bc_xprt;
bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt);
transport->sock = bc_sock->sk_sock;
transport->inet = bc_sock->sk_sk;
/* /*
* Since we don't want connections for the backchannel, we set * Since we don't want connections for the backchannel, we set
* the xprt status to connected * the xprt status to connected
@ -2415,6 +2428,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
if (try_module_get(THIS_MODULE)) if (try_module_get(THIS_MODULE))
return xprt; return xprt;
xprt_put(xprt);
ret = ERR_PTR(-EINVAL); ret = ERR_PTR(-EINVAL);
out_err: out_err:
xprt_free(xprt); xprt_free(xprt);