提交 df36b439 编写于 作者: L Linus Torvalds

Merge branch 'for-2.6.31' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* 'for-2.6.31' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (128 commits)
  nfs41: sunrpc: xprt_alloc_bc_request() should not use spin_lock_bh()
  nfs41: Move initialization of nfs4_opendata seq_res to nfs4_init_opendata_res
  nfs: remove unnecessary NFS_INO_INVALID_ACL checks
  NFS: More "sloppy" parsing problems
  NFS: Invalid mount option values should always fail, even with "sloppy"
  NFS: Remove unused XDR decoder functions
  NFS: Update MNT and MNT3 reply decoding functions
  NFS: add XDR decoder for mountd version 3 auth-flavor lists
  NFS: add new file handle decoders to in-kernel mountd client
  NFS: Add separate mountd status code decoders for each mountd version
  NFS: remove unused function in fs/nfs/mount_clnt.c
  NFS: Use xdr_stream-based XDR encoder for MNT's dirpath argument
  NFS: Clean up MNT program definitions
  lockd: Don't bother with RPC ping for NSM upcalls
  lockd: Update NSM state from SM_MON replies
  NFS: Fix false error return from nfs_callback_up() if ipv6.ko is not available
  NFS: Return error code from nfs_callback_up() to user space
  NFS: Do not display the setting of the "intr" mount option
  NFS: add support for splice writes
  nfs41: Backchannel: CB_SEQUENCE validation
  ...
......@@ -126,7 +126,6 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
struct nlm_lock *lock = &argp->lock;
nlmclnt_next_cookie(&argp->cookie);
argp->state = nsm_local_state;
memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh));
lock->caller = utsname()->nodename;
lock->oh.data = req->a_owner;
......@@ -165,6 +164,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
/* Set up the argument struct */
nlmclnt_setlockargs(call, fl);
lock_kernel();
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
if (fl->fl_type != F_UNLCK) {
call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
......@@ -178,6 +178,7 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL;
unlock_kernel();
dprintk("lockd: clnt proc returns %d\n", status);
return status;
......@@ -519,6 +520,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
if (nsm_monitor(host) < 0)
goto out;
req->a_args.state = nsm_local_state;
fl->fl_flags |= FL_ACCESS;
status = do_vfs_lock(fl);
......
......@@ -53,7 +53,7 @@ static DEFINE_SPINLOCK(nsm_lock);
/*
* Local NSM state
*/
int __read_mostly nsm_local_state;
u32 __read_mostly nsm_local_state;
int __read_mostly nsm_use_hostnames;
static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
......@@ -112,6 +112,7 @@ static struct rpc_clnt *nsm_create(void)
.program = &nsm_program,
.version = NSM_VERSION,
.authflavor = RPC_AUTH_NULL,
.flags = RPC_CLNT_CREATE_NOPING,
};
return rpc_create(&args);
......@@ -184,13 +185,19 @@ int nsm_monitor(const struct nlm_host *host)
nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
status = nsm_mon_unmon(nsm, NSMPROC_MON, &res);
if (res.status != 0)
if (unlikely(res.status != 0))
status = -EIO;
if (status < 0)
if (unlikely(status < 0)) {
printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
else
nsm->sm_monitored = 1;
return status;
return status;
}
nsm->sm_monitored = 1;
if (unlikely(nsm_local_state != res.state)) {
nsm_local_state = res.state;
dprintk("lockd: NSM state changed to %d\n", nsm_local_state);
}
return 0;
}
/**
......
......@@ -74,6 +74,15 @@ config NFS_V4
If unsure, say N.
config NFS_V4_1
bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)"
depends on NFS_V4 && EXPERIMENTAL
help
This option enables support for minor version 1 of the NFSv4 protocol
(draft-ietf-nfsv4-minorversion1) in the kernel's NFS client.
Unless you're an NFS developer, say N.
config ROOT_NFS
bool "Root file system on NFS"
depends on NFS_FS=y && IP_PNP
......
......@@ -17,6 +17,9 @@
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/sunrpc/svcauth_gss.h>
#if defined(CONFIG_NFS_V4_1)
#include <linux/sunrpc/bc_xprt.h>
#endif
#include <net/inet_sock.h>
......@@ -28,11 +31,12 @@
struct nfs_callback_data {
unsigned int users;
struct svc_serv *serv;
struct svc_rqst *rqst;
struct task_struct *task;
};
static struct nfs_callback_data nfs_callback_info;
static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
static DEFINE_MUTEX(nfs_callback_mutex);
static struct svc_program nfs4_callback_program;
......@@ -56,10 +60,10 @@ module_param_call(callback_tcpport, param_set_port, param_get_int,
&nfs_callback_set_tcpport, 0644);
/*
* This is the callback kernel thread.
* This is the NFSv4 callback kernel thread.
*/
static int
nfs_callback_svc(void *vrqstp)
nfs4_callback_svc(void *vrqstp)
{
int err, preverr = 0;
struct svc_rqst *rqstp = vrqstp;
......@@ -97,20 +101,12 @@ nfs_callback_svc(void *vrqstp)
}
/*
* Bring up the callback thread if it is not already up.
* Prepare to bring up the NFSv4 callback service
*/
int nfs_callback_up(void)
struct svc_rqst *
nfs4_callback_up(struct svc_serv *serv)
{
struct svc_serv *serv = NULL;
int ret = 0;
mutex_lock(&nfs_callback_mutex);
if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
goto out;
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
ret = -ENOMEM;
if (!serv)
goto out_err;
int ret;
ret = svc_create_xprt(serv, "tcp", PF_INET,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
......@@ -127,27 +123,174 @@ int nfs_callback_up(void)
nfs_callback_tcpport6 = ret;
dprintk("NFS: Callback listener port = %u (af %u)\n",
nfs_callback_tcpport6, PF_INET6);
} else if (ret != -EAFNOSUPPORT)
} else if (ret == -EAFNOSUPPORT)
ret = 0;
else
goto out_err;
#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
if (IS_ERR(nfs_callback_info.rqst)) {
ret = PTR_ERR(nfs_callback_info.rqst);
nfs_callback_info.rqst = NULL;
return svc_prepare_thread(serv, &serv->sv_pools[0]);
out_err:
if (ret == 0)
ret = -ENOMEM;
return ERR_PTR(ret);
}
#if defined(CONFIG_NFS_V4_1)
/*
* The callback service for NFSv4.1 callbacks
*/
static int
nfs41_callback_svc(void *vrqstp)
{
struct svc_rqst *rqstp = vrqstp;
struct svc_serv *serv = rqstp->rq_server;
struct rpc_rqst *req;
int error;
DEFINE_WAIT(wq);
set_freezable();
/*
* FIXME: do we really need to run this under the BKL? If so, please
* add a comment about what it's intended to protect.
*/
lock_kernel();
while (!kthread_should_stop()) {
prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
spin_lock_bh(&serv->sv_cb_lock);
if (!list_empty(&serv->sv_cb_list)) {
req = list_first_entry(&serv->sv_cb_list,
struct rpc_rqst, rq_bc_list);
list_del(&req->rq_bc_list);
spin_unlock_bh(&serv->sv_cb_lock);
dprintk("Invoking bc_svc_process()\n");
error = bc_svc_process(serv, req, rqstp);
dprintk("bc_svc_process() returned w/ error code= %d\n",
error);
} else {
spin_unlock_bh(&serv->sv_cb_lock);
schedule();
}
finish_wait(&serv->sv_cb_waitq, &wq);
}
unlock_kernel();
return 0;
}
/*
* Bring up the NFSv4.1 callback service
*/
struct svc_rqst *
nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
{
struct svc_xprt *bc_xprt;
struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
dprintk("--> %s\n", __func__);
/* Create a svc_sock for the service */
bc_xprt = svc_sock_create(serv, xprt->prot);
if (!bc_xprt)
goto out;
/*
* Save the svc_serv in the transport so that it can
* be referenced when the session backchannel is initialized
*/
serv->bc_xprt = bc_xprt;
xprt->bc_serv = serv;
INIT_LIST_HEAD(&serv->sv_cb_list);
spin_lock_init(&serv->sv_cb_lock);
init_waitqueue_head(&serv->sv_cb_waitq);
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
if (IS_ERR(rqstp))
svc_sock_destroy(bc_xprt);
out:
dprintk("--> %s return %p\n", __func__, rqstp);
return rqstp;
}
static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
struct svc_serv *serv, struct rpc_xprt *xprt,
struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{
if (minorversion) {
*rqstpp = nfs41_callback_up(serv, xprt);
*callback_svc = nfs41_callback_svc;
}
return minorversion;
}
static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
struct nfs_callback_data *cb_info)
{
if (minorversion)
xprt->bc_serv = cb_info->serv;
}
#else
static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
struct svc_serv *serv, struct rpc_xprt *xprt,
struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
{
return 0;
}
static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
struct nfs_callback_data *cb_info)
{
}
#endif /* CONFIG_NFS_V4_1 */
/*
* Bring up the callback thread if it is not already up.
*/
int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
{
struct svc_serv *serv = NULL;
struct svc_rqst *rqstp;
int (*callback_svc)(void *vrqstp);
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
char svc_name[12];
int ret = 0;
int minorversion_setup;
mutex_lock(&nfs_callback_mutex);
if (cb_info->users++ || cb_info->task != NULL) {
nfs_callback_bc_serv(minorversion, xprt, cb_info);
goto out;
}
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
if (!serv) {
ret = -ENOMEM;
goto out_err;
}
minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion,
serv, xprt, &rqstp, &callback_svc);
if (!minorversion_setup) {
/* v4.0 callback setup */
rqstp = nfs4_callback_up(serv);
callback_svc = nfs4_callback_svc;
}
if (IS_ERR(rqstp)) {
ret = PTR_ERR(rqstp);
goto out_err;
}
svc_sock_update_bufs(serv);
nfs_callback_info.task = kthread_run(nfs_callback_svc,
nfs_callback_info.rqst,
"nfsv4-svc");
if (IS_ERR(nfs_callback_info.task)) {
ret = PTR_ERR(nfs_callback_info.task);
svc_exit_thread(nfs_callback_info.rqst);
nfs_callback_info.rqst = NULL;
nfs_callback_info.task = NULL;
sprintf(svc_name, "nfsv4.%u-svc", minorversion);
cb_info->serv = serv;
cb_info->rqst = rqstp;
cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
if (IS_ERR(cb_info->task)) {
ret = PTR_ERR(cb_info->task);
svc_exit_thread(cb_info->rqst);
cb_info->rqst = NULL;
cb_info->task = NULL;
goto out_err;
}
out:
......@@ -164,22 +307,25 @@ int nfs_callback_up(void)
out_err:
dprintk("NFS: Couldn't create callback socket or server thread; "
"err = %d\n", ret);
nfs_callback_info.users--;
cb_info->users--;
goto out;
}
/*
* Kill the callback thread if it's no longer being used.
*/
void nfs_callback_down(void)
void nfs_callback_down(int minorversion)
{
struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
mutex_lock(&nfs_callback_mutex);
nfs_callback_info.users--;
if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
kthread_stop(nfs_callback_info.task);
svc_exit_thread(nfs_callback_info.rqst);
nfs_callback_info.rqst = NULL;
nfs_callback_info.task = NULL;
cb_info->users--;
if (cb_info->users == 0 && cb_info->task != NULL) {
kthread_stop(cb_info->task);
svc_exit_thread(cb_info->rqst);
cb_info->serv = NULL;
cb_info->rqst = NULL;
cb_info->task = NULL;
}
mutex_unlock(&nfs_callback_mutex);
}
......
......@@ -20,13 +20,24 @@ enum nfs4_callback_procnum {
enum nfs4_callback_opnum {
OP_CB_GETATTR = 3,
OP_CB_RECALL = 4,
/* Callback operations new to NFSv4.1 */
OP_CB_LAYOUTRECALL = 5,
OP_CB_NOTIFY = 6,
OP_CB_PUSH_DELEG = 7,
OP_CB_RECALL_ANY = 8,
OP_CB_RECALLABLE_OBJ_AVAIL = 9,
OP_CB_RECALL_SLOT = 10,
OP_CB_SEQUENCE = 11,
OP_CB_WANTS_CANCELLED = 12,
OP_CB_NOTIFY_LOCK = 13,
OP_CB_NOTIFY_DEVICEID = 14,
OP_CB_ILLEGAL = 10044,
};
struct cb_compound_hdr_arg {
unsigned int taglen;
const char *tag;
unsigned int callback_ident;
unsigned int minorversion;
unsigned nops;
};
......@@ -59,16 +70,59 @@ struct cb_recallargs {
uint32_t truncate;
};
#if defined(CONFIG_NFS_V4_1)
struct referring_call {
uint32_t rc_sequenceid;
uint32_t rc_slotid;
};
struct referring_call_list {
struct nfs4_sessionid rcl_sessionid;
uint32_t rcl_nrefcalls;
struct referring_call *rcl_refcalls;
};
struct cb_sequenceargs {
struct sockaddr *csa_addr;
struct nfs4_sessionid csa_sessionid;
uint32_t csa_sequenceid;
uint32_t csa_slotid;
uint32_t csa_highestslotid;
uint32_t csa_cachethis;
uint32_t csa_nrclists;
struct referring_call_list *csa_rclists;
};
struct cb_sequenceres {
__be32 csr_status;
struct nfs4_sessionid csr_sessionid;
uint32_t csr_sequenceid;
uint32_t csr_slotid;
uint32_t csr_highestslotid;
uint32_t csr_target_highestslotid;
};
extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res);
#endif /* CONFIG_NFS_V4_1 */
extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
#ifdef CONFIG_NFS_V4
extern int nfs_callback_up(void);
extern void nfs_callback_down(void);
#else
#define nfs_callback_up() (0)
#define nfs_callback_down() do {} while(0)
#endif
extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
extern void nfs_callback_down(int minorversion);
#endif /* CONFIG_NFS_V4 */
/*
* nfs41: Callbacks are expected to not cause substantial latency,
* so we limit their concurrency to 1 by setting up the maximum number
* of slots for the backchannel.
*/
#define NFS41_BC_MIN_CALLBACKS 1
#define NFS41_BC_MAX_CALLBACKS 1
extern unsigned int nfs_callback_set_tcpport;
extern unsigned short nfs_callback_tcpport;
......
......@@ -101,3 +101,130 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
return res;
}
#if defined(CONFIG_NFS_V4_1)
/*
* Validate the sequenceID sent by the server.
* Return success if the sequenceID is one more than what we last saw on
* this slot, accounting for wraparound. Increments the slot's sequence.
*
* We don't yet implement a duplicate request cache, so at this time
* we will log replays, and process them as if we had not seen them before,
* but we don't bump the sequence in the slot. Not too worried about it,
* since we only currently implement idempotent callbacks anyway.
*
* We have a single slot backchannel at this time, so we don't bother
* checking the used_slots bit array on the table. The lower layer guarantees
* a single outstanding callback request at a time.
*/
static int
validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid)
{
struct nfs4_slot *slot;
dprintk("%s enter. slotid %d seqid %d\n",
__func__, slotid, seqid);
if (slotid > NFS41_BC_MAX_CALLBACKS)
return htonl(NFS4ERR_BADSLOT);
slot = tbl->slots + slotid;
dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr);
/* Normal */
if (likely(seqid == slot->seq_nr + 1)) {
slot->seq_nr++;
return htonl(NFS4_OK);
}
/* Replay */
if (seqid == slot->seq_nr) {
dprintk("%s seqid %d is a replay - no DRC available\n",
__func__, seqid);
return htonl(NFS4_OK);
}
/* Wraparound */
if (seqid == 1 && (slot->seq_nr + 1) == 0) {
slot->seq_nr = 1;
return htonl(NFS4_OK);
}
/* Misordered request */
return htonl(NFS4ERR_SEQ_MISORDERED);
}
/*
* Returns a pointer to a held 'struct nfs_client' that matches the server's
* address, major version number, and session ID. It is the caller's
* responsibility to release the returned reference.
*
* Returns NULL if there are no connections with sessions, or if no session
* matches the one of interest.
*/
static struct nfs_client *find_client_with_session(
const struct sockaddr *addr, u32 nfsversion,
struct nfs4_sessionid *sessionid)
{
struct nfs_client *clp;
clp = nfs_find_client(addr, 4);
if (clp == NULL)
return NULL;
do {
struct nfs_client *prev = clp;
if (clp->cl_session != NULL) {
if (memcmp(clp->cl_session->sess_id.data,
sessionid->data,
NFS4_MAX_SESSIONID_LEN) == 0) {
/* Returns a held reference to clp */
return clp;
}
}
clp = nfs_find_client_next(prev);
nfs_put_client(prev);
} while (clp != NULL);
return NULL;
}
/* FIXME: referring calls should be processed */
unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
struct cb_sequenceres *res)
{
struct nfs_client *clp;
int i, status;
for (i = 0; i < args->csa_nrclists; i++)
kfree(args->csa_rclists[i].rcl_refcalls);
kfree(args->csa_rclists);
status = htonl(NFS4ERR_BADSESSION);
clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid);
if (clp == NULL)
goto out;
status = validate_seqid(&clp->cl_session->bc_slot_table,
args->csa_slotid, args->csa_sequenceid);
if (status)
goto out_putclient;
memcpy(&res->csr_sessionid, &args->csa_sessionid,
sizeof(res->csr_sessionid));
res->csr_sequenceid = args->csa_sequenceid;
res->csr_slotid = args->csa_slotid;
res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
out_putclient:
nfs_put_client(clp);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
res->csr_status = status;
return res->csr_status;
}
#endif /* CONFIG_NFS_V4_1 */
......@@ -20,6 +20,11 @@
2 + 2 + 3 + 3)
#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#if defined(CONFIG_NFS_V4_1)
#define CB_OP_SEQUENCE_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
4 + 1 + 3)
#endif /* CONFIG_NFS_V4_1 */
#define NFSDBG_FACILITY NFSDBG_CALLBACK
typedef __be32 (*callback_process_op_t)(void *, void *);
......@@ -132,7 +137,6 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
{
__be32 *p;
unsigned int minor_version;
__be32 status;
status = decode_string(xdr, &hdr->taglen, &hdr->tag);
......@@ -147,15 +151,19 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
p = read_buf(xdr, 12);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
minor_version = ntohl(*p++);
/* Check minor version is zero. */
if (minor_version != 0) {
printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n",
__func__, minor_version);
hdr->minorversion = ntohl(*p++);
/* Check minor version is zero or one. */
if (hdr->minorversion <= 1) {
p++; /* skip callback_ident */
} else {
printk(KERN_WARNING "%s: NFSv4 server callback with "
"illegal minor version %u!\n",
__func__, hdr->minorversion);
return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
}
hdr->callback_ident = ntohl(*p++);
hdr->nops = ntohl(*p);
dprintk("%s: minorversion %d nops %d\n", __func__,
hdr->minorversion, hdr->nops);
return 0;
}
......@@ -204,6 +212,122 @@ static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr,
return status;
}
#if defined(CONFIG_NFS_V4_1)
static unsigned decode_sessionid(struct xdr_stream *xdr,
struct nfs4_sessionid *sid)
{
uint32_t *p;
int len = NFS4_MAX_SESSIONID_LEN;
p = read_buf(xdr, len);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);;
memcpy(sid->data, p, len);
return 0;
}
static unsigned decode_rc_list(struct xdr_stream *xdr,
struct referring_call_list *rc_list)
{
uint32_t *p;
int i;
unsigned status;
status = decode_sessionid(xdr, &rc_list->rcl_sessionid);
if (status)
goto out;
status = htonl(NFS4ERR_RESOURCE);
p = read_buf(xdr, sizeof(uint32_t));
if (unlikely(p == NULL))
goto out;
rc_list->rcl_nrefcalls = ntohl(*p++);
if (rc_list->rcl_nrefcalls) {
p = read_buf(xdr,
rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
if (unlikely(p == NULL))
goto out;
rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls *
sizeof(*rc_list->rcl_refcalls),
GFP_KERNEL);
if (unlikely(rc_list->rcl_refcalls == NULL))
goto out;
for (i = 0; i < rc_list->rcl_nrefcalls; i++) {
rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++);
rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++);
}
}
status = 0;
out:
return status;
}
static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp,
struct xdr_stream *xdr,
struct cb_sequenceargs *args)
{
uint32_t *p;
int i;
unsigned status;
status = decode_sessionid(xdr, &args->csa_sessionid);
if (status)
goto out;
status = htonl(NFS4ERR_RESOURCE);
p = read_buf(xdr, 5 * sizeof(uint32_t));
if (unlikely(p == NULL))
goto out;
args->csa_addr = svc_addr(rqstp);
args->csa_sequenceid = ntohl(*p++);
args->csa_slotid = ntohl(*p++);
args->csa_highestslotid = ntohl(*p++);
args->csa_cachethis = ntohl(*p++);
args->csa_nrclists = ntohl(*p++);
args->csa_rclists = NULL;
if (args->csa_nrclists) {
args->csa_rclists = kmalloc(args->csa_nrclists *
sizeof(*args->csa_rclists),
GFP_KERNEL);
if (unlikely(args->csa_rclists == NULL))
goto out;
for (i = 0; i < args->csa_nrclists; i++) {
status = decode_rc_list(xdr, &args->csa_rclists[i]);
if (status)
goto out_free;
}
}
status = 0;
dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u "
"highestslotid %u cachethis %d nrclists %u\n",
__func__,
((u32 *)&args->csa_sessionid)[0],
((u32 *)&args->csa_sessionid)[1],
((u32 *)&args->csa_sessionid)[2],
((u32 *)&args->csa_sessionid)[3],
args->csa_sequenceid, args->csa_slotid,
args->csa_highestslotid, args->csa_cachethis,
args->csa_nrclists);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status;
out_free:
for (i = 0; i < args->csa_nrclists; i++)
kfree(args->csa_rclists[i].rcl_refcalls);
kfree(args->csa_rclists);
goto out;
}
#endif /* CONFIG_NFS_V4_1 */
static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
{
__be32 *p;
......@@ -353,31 +477,134 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr,
return status;
}
static __be32 process_op(struct svc_rqst *rqstp,
#if defined(CONFIG_NFS_V4_1)
static unsigned encode_sessionid(struct xdr_stream *xdr,
const struct nfs4_sessionid *sid)
{
uint32_t *p;
int len = NFS4_MAX_SESSIONID_LEN;
p = xdr_reserve_space(xdr, len);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
memcpy(p, sid, len);
return 0;
}
static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp,
struct xdr_stream *xdr,
const struct cb_sequenceres *res)
{
uint32_t *p;
unsigned status = res->csr_status;
if (unlikely(status != 0))
goto out;
encode_sessionid(xdr, &res->csr_sessionid);
p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t));
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(res->csr_sequenceid);
*p++ = htonl(res->csr_slotid);
*p++ = htonl(res->csr_highestslotid);
*p++ = htonl(res->csr_target_highestslotid);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
return status;
}
static __be32
preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
{
if (op_nr == OP_CB_SEQUENCE) {
if (nop != 0)
return htonl(NFS4ERR_SEQUENCE_POS);
} else {
if (nop == 0)
return htonl(NFS4ERR_OP_NOT_IN_SESSION);
}
switch (op_nr) {
case OP_CB_GETATTR:
case OP_CB_RECALL:
case OP_CB_SEQUENCE:
*op = &callback_ops[op_nr];
break;
case OP_CB_LAYOUTRECALL:
case OP_CB_NOTIFY_DEVICEID:
case OP_CB_NOTIFY:
case OP_CB_PUSH_DELEG:
case OP_CB_RECALL_ANY:
case OP_CB_RECALLABLE_OBJ_AVAIL:
case OP_CB_RECALL_SLOT:
case OP_CB_WANTS_CANCELLED:
case OP_CB_NOTIFY_LOCK:
return htonl(NFS4ERR_NOTSUPP);
default:
return htonl(NFS4ERR_OP_ILLEGAL);
}
return htonl(NFS_OK);
}
#else /* CONFIG_NFS_V4_1 */
static __be32
preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
{
return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
}
#endif /* CONFIG_NFS_V4_1 */
static __be32
preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
{
switch (op_nr) {
case OP_CB_GETATTR:
case OP_CB_RECALL:
*op = &callback_ops[op_nr];
break;
default:
return htonl(NFS4ERR_OP_ILLEGAL);
}
return htonl(NFS_OK);
}
static __be32 process_op(uint32_t minorversion, int nop,
struct svc_rqst *rqstp,
struct xdr_stream *xdr_in, void *argp,
struct xdr_stream *xdr_out, void *resp)
{
struct callback_op *op = &callback_ops[0];
unsigned int op_nr = OP_CB_ILLEGAL;
__be32 status = 0;
__be32 status;
long maxlen;
__be32 res;
dprintk("%s: start\n", __func__);
status = decode_op_hdr(xdr_in, &op_nr);
if (likely(status == 0)) {
switch (op_nr) {
case OP_CB_GETATTR:
case OP_CB_RECALL:
op = &callback_ops[op_nr];
break;
default:
op_nr = OP_CB_ILLEGAL;
op = &callback_ops[0];
status = htonl(NFS4ERR_OP_ILLEGAL);
}
if (unlikely(status)) {
status = htonl(NFS4ERR_OP_ILLEGAL);
goto out;
}
dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
__func__, minorversion, nop, op_nr);
status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) :
preprocess_nfs4_op(op_nr, &op);
if (status == htonl(NFS4ERR_OP_ILLEGAL))
op_nr = OP_CB_ILLEGAL;
out:
maxlen = xdr_out->end - xdr_out->p;
if (maxlen > 0 && maxlen < PAGE_SIZE) {
if (likely(status == 0 && op->decode_args != NULL))
......@@ -425,7 +652,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
return rpc_system_err;
while (status == 0 && nops != hdr_arg.nops) {
status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp);
status = process_op(hdr_arg.minorversion, nops,
rqstp, &xdr_in, argp, &xdr_out, resp);
nops++;
}
......@@ -452,7 +680,15 @@ static struct callback_op callback_ops[] = {
.process_op = (callback_process_op_t)nfs4_callback_recall,
.decode_args = (callback_decode_arg_t)decode_recall_args,
.res_maxsize = CB_OP_RECALL_RES_MAXSZ,
}
},
#if defined(CONFIG_NFS_V4_1)
[OP_CB_SEQUENCE] = {
.process_op = (callback_process_op_t)nfs4_callback_sequence,
.decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
.encode_res = (callback_encode_res_t)encode_cb_sequence_res,
.res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ,
},
#endif /* CONFIG_NFS_V4_1 */
};
/*
......
......@@ -37,6 +37,7 @@
#include <linux/in6.h>
#include <net/ipv6.h>
#include <linux/nfs_xdr.h>
#include <linux/sunrpc/bc_xprt.h>
#include <asm/system.h>
......@@ -102,6 +103,7 @@ struct nfs_client_initdata {
size_t addrlen;
const struct nfs_rpc_ops *rpc_ops;
int proto;
u32 minorversion;
};
/*
......@@ -114,18 +116,13 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
{
struct nfs_client *clp;
struct rpc_cred *cred;
int err = -ENOMEM;
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
goto error_0;
clp->rpc_ops = cl_init->rpc_ops;
if (cl_init->rpc_ops->version == 4) {
if (nfs_callback_up() < 0)
goto error_2;
__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
}
atomic_set(&clp->cl_count, 1);
clp->cl_cons_state = NFS_CS_INITING;
......@@ -133,9 +130,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_addrlen = cl_init->addrlen;
if (cl_init->hostname) {
err = -ENOMEM;
clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
if (!clp->cl_hostname)
goto error_3;
goto error_cleanup;
}
INIT_LIST_HEAD(&clp->cl_superblocks);
......@@ -150,6 +148,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
clp->cl_boot_time = CURRENT_TIME;
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
clp->cl_minorversion = cl_init->minorversion;
#endif
cred = rpc_lookup_machine_cred();
if (!IS_ERR(cred))
......@@ -159,13 +158,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
return clp;
error_3:
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down();
error_2:
error_cleanup:
kfree(clp);
error_0:
return NULL;
return ERR_PTR(err);
}
static void nfs4_shutdown_client(struct nfs_client *clp)
......@@ -181,6 +177,35 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
#endif
}
/*
* Destroy the NFS4 callback service
*/
static void nfs4_destroy_callback(struct nfs_client *clp)
{
#ifdef CONFIG_NFS_V4
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down(clp->cl_minorversion);
#endif /* CONFIG_NFS_V4 */
}
/*
* Clears/puts all minor version specific parts from an nfs_client struct
* reverting it to minorversion 0.
*/
static void nfs4_clear_client_minor_version(struct nfs_client *clp)
{
#ifdef CONFIG_NFS_V4_1
if (nfs4_has_session(clp)) {
nfs4_destroy_session(clp->cl_session);
clp->cl_session = NULL;
}
clp->cl_call_sync = _nfs4_call_sync;
#endif /* CONFIG_NFS_V4_1 */
nfs4_destroy_callback(clp);
}
/*
* Destroy a shared client record
*/
......@@ -188,6 +213,7 @@ static void nfs_free_client(struct nfs_client *clp)
{
dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
nfs4_clear_client_minor_version(clp);
nfs4_shutdown_client(clp);
nfs_fscache_release_client_cookie(clp);
......@@ -196,9 +222,6 @@ static void nfs_free_client(struct nfs_client *clp)
if (!IS_ERR(clp->cl_rpcclient))
rpc_shutdown_client(clp->cl_rpcclient);
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down();
if (clp->cl_machine_cred != NULL)
put_rpccred(clp->cl_machine_cred);
......@@ -347,7 +370,8 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
/* Don't match clients that failed to initialise properly */
if (clp->cl_cons_state != NFS_CS_READY)
if (!(clp->cl_cons_state == NFS_CS_READY ||
clp->cl_cons_state == NFS_CS_SESSION_INITING))
continue;
/* Different NFS versions cannot share the same nfs_client */
......@@ -420,7 +444,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
if (clp->cl_proto != data->proto)
continue;
/* Match nfsv4 minorversion */
if (clp->cl_minorversion != data->minorversion)
continue;
/* Match the full socket address */
if (!nfs_sockaddr_cmp(sap, clap))
continue;
......@@ -456,9 +482,10 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in
spin_unlock(&nfs_client_lock);
new = nfs_alloc_client(cl_init);
} while (new);
} while (!IS_ERR(new));
return ERR_PTR(-ENOMEM);
dprintk("--> nfs_get_client() = %ld [failed]\n", PTR_ERR(new));
return new;
/* install a new client and return with it unready */
install_client:
......@@ -478,7 +505,7 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in
nfs_free_client(new);
error = wait_event_killable(nfs_client_active_wq,
clp->cl_cons_state != NFS_CS_INITING);
clp->cl_cons_state < NFS_CS_INITING);
if (error < 0) {
nfs_put_client(clp);
return ERR_PTR(-ERESTARTSYS);
......@@ -499,12 +526,28 @@ static struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_in
/*
* Mark a server as ready or failed
*/
static void nfs_mark_client_ready(struct nfs_client *clp, int state)
void nfs_mark_client_ready(struct nfs_client *clp, int state)
{
clp->cl_cons_state = state;
wake_up_all(&nfs_client_active_wq);
}
/*
* With sessions, the client is not marked ready until after a
* successful EXCHANGE_ID and CREATE_SESSION.
*
* Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
* other versions of NFS can be tried.
*/
int nfs4_check_client_ready(struct nfs_client *clp)
{
if (!nfs4_has_session(clp))
return 0;
if (clp->cl_cons_state < NFS_CS_READY)
return -EPROTONOSUPPORT;
return 0;
}
/*
* Initialise the timeout values for a connection
*/
......@@ -1049,6 +1092,61 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
}
#ifdef CONFIG_NFS_V4
/*
* Initialize the NFS4 callback service
*/
static int nfs4_init_callback(struct nfs_client *clp)
{
int error;
if (clp->rpc_ops->version == 4) {
if (nfs4_has_session(clp)) {
error = xprt_setup_backchannel(
clp->cl_rpcclient->cl_xprt,
NFS41_BC_MIN_CALLBACKS);
if (error < 0)
return error;
}
error = nfs_callback_up(clp->cl_minorversion,
clp->cl_rpcclient->cl_xprt);
if (error < 0) {
dprintk("%s: failed to start callback. Error = %d\n",
__func__, error);
return error;
}
__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
}
return 0;
}
/*
* Initialize the minor version specific parts of an NFS4 client record
*/
static int nfs4_init_client_minor_version(struct nfs_client *clp)
{
clp->cl_call_sync = _nfs4_call_sync;
#if defined(CONFIG_NFS_V4_1)
if (clp->cl_minorversion) {
struct nfs4_session *session = NULL;
/*
* Create the session and mark it expired.
* When a SEQUENCE operation encounters the expired session
* it will do session recovery to initialize it.
*/
session = nfs4_alloc_session(clp);
if (!session)
return -ENOMEM;
clp->cl_session = session;
clp->cl_call_sync = _nfs4_call_sync_session;
}
#endif /* CONFIG_NFS_V4_1 */
return nfs4_init_callback(clp);
}
/*
* Initialise an NFS4 client record
*/
......@@ -1083,7 +1181,12 @@ static int nfs4_init_client(struct nfs_client *clp,
}
__set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
nfs_mark_client_ready(clp, NFS_CS_READY);
error = nfs4_init_client_minor_version(clp);
if (error < 0)
goto error;
if (!nfs4_has_session(clp))
nfs_mark_client_ready(clp, NFS_CS_READY);
return 0;
error:
......@@ -1101,7 +1204,8 @@ static int nfs4_set_client(struct nfs_server *server,
const size_t addrlen,
const char *ip_addr,
rpc_authflavor_t authflavour,
int proto, const struct rpc_timeout *timeparms)
int proto, const struct rpc_timeout *timeparms,
u32 minorversion)
{
struct nfs_client_initdata cl_init = {
.hostname = hostname,
......@@ -1109,6 +1213,7 @@ static int nfs4_set_client(struct nfs_server *server,
.addrlen = addrlen,
.rpc_ops = &nfs_v4_clientops,
.proto = proto,
.minorversion = minorversion,
};
struct nfs_client *clp;
int error;
......@@ -1137,6 +1242,36 @@ static int nfs4_set_client(struct nfs_server *server,
return error;
}
/*
* Initialize a session.
* Note: save the mount rsize and wsize for create_server negotiation.
*/
static void nfs4_init_session(struct nfs_client *clp,
unsigned int wsize, unsigned int rsize)
{
#if defined(CONFIG_NFS_V4_1)
if (nfs4_has_session(clp)) {
clp->cl_session->fc_attrs.max_rqst_sz = wsize;
clp->cl_session->fc_attrs.max_resp_sz = rsize;
}
#endif /* CONFIG_NFS_V4_1 */
}
/*
* Session has been established, and the client marked ready.
* Set the mount rsize and wsize with negotiated fore channel
* attributes which will be bound checked in nfs_server_set_fsinfo.
*/
static void nfs4_session_set_rwsize(struct nfs_server *server)
{
#ifdef CONFIG_NFS_V4_1
if (!nfs4_has_session(server->nfs_client))
return;
server->rsize = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
server->wsize = server->nfs_client->cl_session->fc_attrs.max_rqst_sz;
#endif /* CONFIG_NFS_V4_1 */
}
/*
* Create a version 4 volume record
*/
......@@ -1164,7 +1299,8 @@ static int nfs4_init_server(struct nfs_server *server,
data->client_address,
data->auth_flavors[0],
data->nfs_server.protocol,
&timeparms);
&timeparms,
data->minorversion);
if (error < 0)
goto error;
......@@ -1214,6 +1350,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
BUG_ON(!server->nfs_client->rpc_ops);
BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
nfs4_init_session(server->nfs_client, server->wsize, server->rsize);
/* Probe the root fh to retrieve its FSID */
error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path);
if (error < 0)
......@@ -1224,6 +1362,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
(unsigned long long) server->fsid.minor);
dprintk("Mount FH: %d\n", mntfh->size);
nfs4_session_set_rwsize(server);
error = nfs_probe_fsinfo(server, mntfh, &fattr);
if (error < 0)
goto error;
......@@ -1282,7 +1422,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
parent_client->cl_ipaddr,
data->authflavor,
parent_server->client->cl_xprt->prot,
parent_server->client->cl_timeout);
parent_server->client->cl_timeout,
parent_client->cl_minorversion);
if (error < 0)
goto error;
......
......@@ -68,29 +68,26 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
{
struct inode *inode = state->inode;
struct file_lock *fl;
int status;
int status = 0;
if (inode->i_flock == NULL)
goto out;
/* Protect inode->i_flock using the BKL */
lock_kernel();
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
continue;
if (nfs_file_open_context(fl->fl_file) != ctx)
continue;
unlock_kernel();
status = nfs4_lock_delegation_recall(state, fl);
if (status >= 0)
continue;
switch (status) {
default:
printk(KERN_ERR "%s: unhandled error %d.\n",
__func__, status);
case -NFS4ERR_EXPIRED:
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
case -NFS4ERR_STALE_CLIENTID:
nfs4_schedule_state_recovery(NFS_SERVER(inode)->nfs_client);
goto out_err;
}
if (status < 0)
goto out;
lock_kernel();
}
return 0;
out_err:
unlock_kernel();
out:
return status;
}
......@@ -268,7 +265,10 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat
struct nfs_inode *nfsi = NFS_I(inode);
nfs_msync_inode(inode);
/* Guard against new delegated open calls */
/*
* Guard against new delegated open/lock/unlock calls and against
* state recovery
*/
down_write(&nfsi->rwsem);
nfs_delegation_claim_opens(inode, &delegation->stateid);
up_write(&nfsi->rwsem);
......
......@@ -259,6 +259,9 @@ static void nfs_direct_read_release(void *calldata)
}
static const struct rpc_call_ops nfs_read_direct_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_read_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_direct_read_result,
.rpc_release = nfs_direct_read_release,
};
......@@ -535,6 +538,9 @@ static void nfs_direct_commit_release(void *calldata)
}
static const struct rpc_call_ops nfs_commit_direct_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_write_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_direct_commit_result,
.rpc_release = nfs_direct_commit_release,
};
......@@ -673,6 +679,9 @@ static void nfs_direct_write_release(void *calldata)
}
static const struct rpc_call_ops nfs_write_direct_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_write_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_direct_write_result,
.rpc_release = nfs_direct_write_release,
};
......
......@@ -48,6 +48,9 @@ static ssize_t nfs_file_splice_read(struct file *filp, loff_t *ppos,
size_t count, unsigned int flags);
static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
struct file *filp, loff_t *ppos,
size_t count, unsigned int flags);
static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
static int nfs_file_flush(struct file *, fl_owner_t id);
......@@ -73,6 +76,7 @@ const struct file_operations nfs_file_operations = {
.lock = nfs_lock,
.flock = nfs_flock,
.splice_read = nfs_file_splice_read,
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
};
......@@ -587,12 +591,38 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
goto out;
}
static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
struct file *filp, loff_t *ppos,
size_t count, unsigned int flags)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
ssize_t ret;
dprintk("NFS splice_write(%s/%s, %lu@%llu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long long) *ppos);
/*
* The combination of splice and an O_APPEND destination is disallowed.
*/
nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count);
ret = generic_file_splice_write(pipe, filp, ppos, count, flags);
if (ret >= 0 && nfs_need_sync_write(filp, inode)) {
int err = nfs_do_fsync(nfs_file_open_context(filp), inode);
if (err < 0)
ret = err;
}
return ret;
}
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
{
struct inode *inode = filp->f_mapping->host;
int status = 0;
lock_kernel();
/* Try local locking first */
posix_test_lock(filp, fl);
if (fl->fl_type != F_UNLCK) {
......@@ -608,7 +638,6 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
out:
unlock_kernel();
return status;
out_noconflict:
fl->fl_type = F_UNLCK;
......@@ -650,13 +679,11 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
* If we're signalled while cleaning up locks on process exit, we
* still need to complete the unlock.
*/
lock_kernel();
/* Use local locking if mounted with "-onolock" */
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
else
status = do_vfs_lock(filp, fl);
unlock_kernel();
return status;
}
......@@ -673,13 +700,11 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
if (status != 0)
goto out;
lock_kernel();
/* Use local locking if mounted with "-onolock" */
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
else
status = do_vfs_lock(filp, fl);
unlock_kernel();
if (status < 0)
goto out;
/*
......
......@@ -2,6 +2,7 @@
* NFS internal definitions
*/
#include "nfs4_fs.h"
#include <linux/mount.h>
#include <linux/security.h>
......@@ -17,6 +18,18 @@ struct nfs_string;
*/
#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1)
/*
* Determine if sessions are in use.
*/
static inline int nfs4_has_session(const struct nfs_client *clp)
{
#ifdef CONFIG_NFS_V4_1
if (clp->cl_session)
return 1;
#endif /* CONFIG_NFS_V4_1 */
return 0;
}
struct nfs_clone_mount {
const struct super_block *sb;
const struct dentry *dentry;
......@@ -29,6 +42,12 @@ struct nfs_clone_mount {
rpc_authflavor_t authflavor;
};
/*
* Note: RFC 1813 doesn't limit the number of auth flavors that
* a server can return, so make something up.
*/
#define NFS_MAX_SECFLAVORS (12)
/*
* In-kernel mount arguments
*/
......@@ -44,6 +63,7 @@ struct nfs_parsed_mount_data {
unsigned int auth_flavor_len;
rpc_authflavor_t auth_flavors[1];
char *client_address;
unsigned int minorversion;
char *fscache_uniq;
struct {
......@@ -77,6 +97,8 @@ struct nfs_mount_request {
unsigned short protocol;
struct nfs_fh *fh;
int noresvport;
unsigned int *auth_flav_len;
rpc_authflavor_t *auth_flavs;
};
extern int nfs_mount(struct nfs_mount_request *info);
......@@ -99,6 +121,8 @@ extern void nfs_free_server(struct nfs_server *server);
extern struct nfs_server *nfs_clone_server(struct nfs_server *,
struct nfs_fh *,
struct nfs_fattr *);
extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
extern int nfs4_check_client_ready(struct nfs_client *clp);
#ifdef CONFIG_PROC_FS
extern int __init nfs_fs_proc_init(void);
extern void nfs_fs_proc_exit(void);
......@@ -146,6 +170,20 @@ extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int);
extern struct rpc_procinfo nfs3_procedures[];
extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int);
/* nfs4proc.c */
static inline void nfs4_restart_rpc(struct rpc_task *task,
const struct nfs_client *clp)
{
#ifdef CONFIG_NFS_V4_1
if (nfs4_has_session(clp) &&
test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) {
rpc_restart_call_prepare(task);
return;
}
#endif /* CONFIG_NFS_V4_1 */
rpc_restart_call(task);
}
/* nfs4xdr.c */
#ifdef CONFIG_NFS_V4
extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus);
......@@ -205,6 +243,38 @@ extern int nfs4_path_walk(struct nfs_server *server,
const char *path);
#endif
/* read.c */
extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
/* write.c */
extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
/* nfs4proc.c */
extern int _nfs4_call_sync(struct nfs_server *server,
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res,
int cache_reply);
extern int _nfs4_call_sync_session(struct nfs_server *server,
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res,
int cache_reply);
#ifdef CONFIG_NFS_V4_1
extern void nfs41_sequence_free_slot(const struct nfs_client *,
struct nfs4_sequence_res *res);
#endif /* CONFIG_NFS_V4_1 */
static inline void nfs4_sequence_free_slot(const struct nfs_client *clp,
struct nfs4_sequence_res *res)
{
#ifdef CONFIG_NFS_V4_1
if (nfs4_has_session(clp))
nfs41_sequence_free_slot(clp, res);
#endif /* CONFIG_NFS_V4_1 */
}
/*
* Determine the device name as a string
*/
......
......@@ -20,8 +20,116 @@
# define NFSDBG_FACILITY NFSDBG_MOUNT
#endif
/*
* Defined by RFC 1094, section A.3; and RFC 1813, section 5.1.4
*/
#define MNTPATHLEN (1024)
/*
* XDR data type sizes
*/
#define encode_dirpath_sz (1 + XDR_QUADLEN(MNTPATHLEN))
#define MNT_status_sz (1)
#define MNT_fhs_status_sz (1)
#define MNT_fhandle_sz XDR_QUADLEN(NFS2_FHSIZE)
#define MNT_fhandle3_sz (1 + XDR_QUADLEN(NFS3_FHSIZE))
#define MNT_authflav3_sz (1 + NFS_MAX_SECFLAVORS)
/*
* XDR argument and result sizes
*/
#define MNT_enc_dirpath_sz encode_dirpath_sz
#define MNT_dec_mountres_sz (MNT_status_sz + MNT_fhandle_sz)
#define MNT_dec_mountres3_sz (MNT_status_sz + MNT_fhandle_sz + \
MNT_authflav3_sz)
/*
* Defined by RFC 1094, section A.5
*/
enum {
MOUNTPROC_NULL = 0,
MOUNTPROC_MNT = 1,
MOUNTPROC_DUMP = 2,
MOUNTPROC_UMNT = 3,
MOUNTPROC_UMNTALL = 4,
MOUNTPROC_EXPORT = 5,
};
/*
* Defined by RFC 1813, section 5.2
*/
enum {
MOUNTPROC3_NULL = 0,
MOUNTPROC3_MNT = 1,
MOUNTPROC3_DUMP = 2,
MOUNTPROC3_UMNT = 3,
MOUNTPROC3_UMNTALL = 4,
MOUNTPROC3_EXPORT = 5,
};
static struct rpc_program mnt_program;
/*
* Defined by OpenGroup XNFS Version 3W, chapter 8
*/
enum mountstat {
MNT_OK = 0,
MNT_EPERM = 1,
MNT_ENOENT = 2,
MNT_EACCES = 13,
MNT_EINVAL = 22,
};
static struct {
u32 status;
int errno;
} mnt_errtbl[] = {
{ .status = MNT_OK, .errno = 0, },
{ .status = MNT_EPERM, .errno = -EPERM, },
{ .status = MNT_ENOENT, .errno = -ENOENT, },
{ .status = MNT_EACCES, .errno = -EACCES, },
{ .status = MNT_EINVAL, .errno = -EINVAL, },
};
/*
* Defined by RFC 1813, section 5.1.5
*/
enum mountstat3 {
MNT3_OK = 0, /* no error */
MNT3ERR_PERM = 1, /* Not owner */
MNT3ERR_NOENT = 2, /* No such file or directory */
MNT3ERR_IO = 5, /* I/O error */
MNT3ERR_ACCES = 13, /* Permission denied */
MNT3ERR_NOTDIR = 20, /* Not a directory */
MNT3ERR_INVAL = 22, /* Invalid argument */
MNT3ERR_NAMETOOLONG = 63, /* Filename too long */
MNT3ERR_NOTSUPP = 10004, /* Operation not supported */
MNT3ERR_SERVERFAULT = 10006, /* A failure on the server */
};
static struct {
u32 status;
int errno;
} mnt3_errtbl[] = {
{ .status = MNT3_OK, .errno = 0, },
{ .status = MNT3ERR_PERM, .errno = -EPERM, },
{ .status = MNT3ERR_NOENT, .errno = -ENOENT, },
{ .status = MNT3ERR_IO, .errno = -EIO, },
{ .status = MNT3ERR_ACCES, .errno = -EACCES, },
{ .status = MNT3ERR_NOTDIR, .errno = -ENOTDIR, },
{ .status = MNT3ERR_INVAL, .errno = -EINVAL, },
{ .status = MNT3ERR_NAMETOOLONG, .errno = -ENAMETOOLONG, },
{ .status = MNT3ERR_NOTSUPP, .errno = -ENOTSUPP, },
{ .status = MNT3ERR_SERVERFAULT, .errno = -ESERVERFAULT, },
};
struct mountres {
int errno;
struct nfs_fh *fh;
unsigned int *auth_count;
rpc_authflavor_t *auth_flavors;
};
struct mnt_fhstatus {
u32 status;
struct nfs_fh *fh;
......@@ -35,8 +143,10 @@ struct mnt_fhstatus {
*/
int nfs_mount(struct nfs_mount_request *info)
{
struct mnt_fhstatus result = {
.fh = info->fh
struct mountres result = {
.fh = info->fh,
.auth_count = info->auth_flav_len,
.auth_flavors = info->auth_flavs,
};
struct rpc_message msg = {
.rpc_argp = info->dirpath,
......@@ -68,14 +178,14 @@ int nfs_mount(struct nfs_mount_request *info)
if (info->version == NFS_MNT3_VERSION)
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
else
msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT];
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC_MNT];
status = rpc_call_sync(mnt_clnt, &msg, 0);
rpc_shutdown_client(mnt_clnt);
if (status < 0)
goto out_call_err;
if (result.status != 0)
if (result.errno != 0)
goto out_mnt_err;
dprintk("NFS: MNT request succeeded\n");
......@@ -86,72 +196,215 @@ int nfs_mount(struct nfs_mount_request *info)
out_clnt_err:
status = PTR_ERR(mnt_clnt);
dprintk("NFS: failed to create RPC client, status=%d\n", status);
dprintk("NFS: failed to create MNT RPC client, status=%d\n", status);
goto out;
out_call_err:
dprintk("NFS: failed to start MNT request, status=%d\n", status);
dprintk("NFS: MNT request failed, status=%d\n", status);
goto out;
out_mnt_err:
dprintk("NFS: MNT server returned result %d\n", result.status);
status = nfs_stat_to_errno(result.status);
dprintk("NFS: MNT server returned result %d\n", result.errno);
status = result.errno;
goto out;
}
/*
* XDR encode/decode functions for MOUNT
*/
static int xdr_encode_dirpath(struct rpc_rqst *req, __be32 *p,
const char *path)
static int encode_mntdirpath(struct xdr_stream *xdr, const char *pathname)
{
const u32 pathname_len = strlen(pathname);
__be32 *p;
if (unlikely(pathname_len > MNTPATHLEN))
return -EIO;
p = xdr_reserve_space(xdr, sizeof(u32) + pathname_len);
if (unlikely(p == NULL))
return -EIO;
xdr_encode_opaque(p, pathname, pathname_len);
return 0;
}
static int mnt_enc_dirpath(struct rpc_rqst *req, __be32 *p,
const char *dirpath)
{
struct xdr_stream xdr;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
return encode_mntdirpath(&xdr, dirpath);
}
/*
* RFC 1094: "A non-zero status indicates some sort of error. In this
* case, the status is a UNIX error number." This can be problematic
* if the server and client use different errno values for the same
* error.
*
* However, the OpenGroup XNFS spec provides a simple mapping that is
* independent of local errno values on the server and the client.
*/
static int decode_status(struct xdr_stream *xdr, struct mountres *res)
{
p = xdr_encode_string(p, path);
unsigned int i;
u32 status;
__be32 *p;
p = xdr_inline_decode(xdr, sizeof(status));
if (unlikely(p == NULL))
return -EIO;
status = ntohl(*p);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
for (i = 0; i <= ARRAY_SIZE(mnt_errtbl); i++) {
if (mnt_errtbl[i].status == status) {
res->errno = mnt_errtbl[i].errno;
return 0;
}
}
dprintk("NFS: unrecognized MNT status code: %u\n", status);
res->errno = -EACCES;
return 0;
}
static int xdr_decode_fhstatus(struct rpc_rqst *req, __be32 *p,
struct mnt_fhstatus *res)
static int decode_fhandle(struct xdr_stream *xdr, struct mountres *res)
{
struct nfs_fh *fh = res->fh;
__be32 *p;
p = xdr_inline_decode(xdr, NFS2_FHSIZE);
if (unlikely(p == NULL))
return -EIO;
fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE);
return 0;
}
static int mnt_dec_mountres(struct rpc_rqst *req, __be32 *p,
struct mountres *res)
{
struct xdr_stream xdr;
int status;
xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
status = decode_status(&xdr, res);
if (unlikely(status != 0 || res->errno != 0))
return status;
return decode_fhandle(&xdr, res);
}
static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res)
{
unsigned int i;
u32 status;
__be32 *p;
if ((res->status = ntohl(*p++)) == 0) {
fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE);
p = xdr_inline_decode(xdr, sizeof(status));
if (unlikely(p == NULL))
return -EIO;
status = ntohl(*p);
for (i = 0; i <= ARRAY_SIZE(mnt3_errtbl); i++) {
if (mnt3_errtbl[i].status == status) {
res->errno = mnt3_errtbl[i].errno;
return 0;
}
}
dprintk("NFS: unrecognized MNT3 status code: %u\n", status);
res->errno = -EACCES;
return 0;
}
static int xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p,
struct mnt_fhstatus *res)
static int decode_fhandle3(struct xdr_stream *xdr, struct mountres *res)
{
struct nfs_fh *fh = res->fh;
unsigned size;
if ((res->status = ntohl(*p++)) == 0) {
size = ntohl(*p++);
if (size <= NFS3_FHSIZE && size != 0) {
fh->size = size;
memcpy(fh->data, p, size);
} else
res->status = -EBADHANDLE;
u32 size;
__be32 *p;
p = xdr_inline_decode(xdr, sizeof(size));
if (unlikely(p == NULL))
return -EIO;
size = ntohl(*p++);
if (size > NFS3_FHSIZE || size == 0)
return -EIO;
p = xdr_inline_decode(xdr, size);
if (unlikely(p == NULL))
return -EIO;
fh->size = size;
memcpy(fh->data, p, size);
return 0;
}
static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)
{
rpc_authflavor_t *flavors = res->auth_flavors;
unsigned int *count = res->auth_count;
u32 entries, i;
__be32 *p;
if (*count == 0)
return 0;
p = xdr_inline_decode(xdr, sizeof(entries));
if (unlikely(p == NULL))
return -EIO;
entries = ntohl(*p);
dprintk("NFS: received %u auth flavors\n", entries);
if (entries > NFS_MAX_SECFLAVORS)
entries = NFS_MAX_SECFLAVORS;
p = xdr_inline_decode(xdr, sizeof(u32) * entries);
if (unlikely(p == NULL))
return -EIO;
if (entries > *count)
entries = *count;
for (i = 0; i < entries; i++) {
flavors[i] = ntohl(*p++);
dprintk("NFS:\tflavor %u: %d\n", i, flavors[i]);
}
*count = i;
return 0;
}
#define MNT_dirpath_sz (1 + 256)
#define MNT_fhstatus_sz (1 + 8)
#define MNT_fhstatus3_sz (1 + 16)
static int mnt_dec_mountres3(struct rpc_rqst *req, __be32 *p,
struct mountres *res)
{
struct xdr_stream xdr;
int status;
xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
status = decode_fhs_status(&xdr, res);
if (unlikely(status != 0 || res->errno != 0))
return status;
status = decode_fhandle3(&xdr, res);
if (unlikely(status != 0)) {
res->errno = -EBADHANDLE;
return 0;
}
return decode_auth_flavors(&xdr, res);
}
static struct rpc_procinfo mnt_procedures[] = {
[MNTPROC_MNT] = {
.p_proc = MNTPROC_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus,
.p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus_sz,
.p_statidx = MNTPROC_MNT,
[MOUNTPROC_MNT] = {
.p_proc = MOUNTPROC_MNT,
.p_encode = (kxdrproc_t)mnt_enc_dirpath,
.p_decode = (kxdrproc_t)mnt_dec_mountres,
.p_arglen = MNT_enc_dirpath_sz,
.p_replen = MNT_dec_mountres_sz,
.p_statidx = MOUNTPROC_MNT,
.p_name = "MOUNT",
},
};
......@@ -159,10 +412,10 @@ static struct rpc_procinfo mnt_procedures[] = {
static struct rpc_procinfo mnt3_procedures[] = {
[MOUNTPROC3_MNT] = {
.p_proc = MOUNTPROC3_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus3,
.p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus3_sz,
.p_encode = (kxdrproc_t)mnt_enc_dirpath,
.p_decode = (kxdrproc_t)mnt_dec_mountres3,
.p_arglen = MNT_enc_dirpath_sz,
.p_replen = MNT_dec_mountres3_sz,
.p_statidx = MOUNTPROC3_MNT,
.p_name = "MOUNT",
},
......
......@@ -207,8 +207,6 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
status = nfs_revalidate_inode(server, inode);
if (status < 0)
return ERR_PTR(status);
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
nfs_zap_acl_cache(inode);
acl = nfs3_get_cached_acl(inode, type);
if (acl != ERR_PTR(-EAGAIN))
return acl;
......
......@@ -44,6 +44,7 @@ enum nfs4_client_state {
NFS4CLNT_RECLAIM_REBOOT,
NFS4CLNT_RECLAIM_NOGRACE,
NFS4CLNT_DELEGRETURN,
NFS4CLNT_SESSION_SETUP,
};
/*
......@@ -177,6 +178,14 @@ struct nfs4_state_recovery_ops {
int state_flag_bit;
int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
int (*recover_lock)(struct nfs4_state *, struct file_lock *);
int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
};
struct nfs4_state_maintenance_ops {
int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *);
struct rpc_cred * (*get_state_renewal_cred_locked)(struct nfs_client *);
int (*renew_lease)(struct nfs_client *, struct rpc_cred *);
};
extern const struct dentry_operations nfs4_dentry_operations;
......@@ -193,6 +202,7 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *);
extern int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait);
extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
......@@ -200,8 +210,26 @@ extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fh
extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops;
extern struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[];
extern struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[];
#if defined(CONFIG_NFS_V4_1)
extern int nfs4_setup_sequence(struct nfs_client *clp,
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
int cache_reply, struct rpc_task *task);
extern void nfs4_destroy_session(struct nfs4_session *session);
extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp);
extern int nfs4_proc_create_session(struct nfs_client *, int reset);
extern int nfs4_proc_destroy_session(struct nfs4_session *);
#else /* CONFIG_NFS_v4_1 */
static inline int nfs4_setup_sequence(struct nfs_client *clp,
struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
int cache_reply, struct rpc_task *task)
{
return 0;
}
#endif /* CONFIG_NFS_V4_1 */
extern struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[];
extern const u32 nfs4_fattr_bitmap[2];
extern const u32 nfs4_statfs_bitmap[2];
......@@ -216,7 +244,12 @@ extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *);
/* nfs4state.c */
struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp);
struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
#if defined(CONFIG_NFS_V4_1)
struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp);
struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp);
#endif /* CONFIG_NFS_V4_1 */
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
extern void nfs4_put_state_owner(struct nfs4_state_owner *);
......
此差异已折叠。
......@@ -59,12 +59,14 @@
void
nfs4_renew_state(struct work_struct *work)
{
struct nfs4_state_maintenance_ops *ops;
struct nfs_client *clp =
container_of(work, struct nfs_client, cl_renewd.work);
struct rpc_cred *cred;
long lease, timeout;
unsigned long last, now;
ops = nfs4_state_renewal_ops[clp->cl_minorversion];
dprintk("%s: start\n", __func__);
/* Are there any active superblocks? */
if (list_empty(&clp->cl_superblocks))
......@@ -76,7 +78,7 @@ nfs4_renew_state(struct work_struct *work)
timeout = (2 * lease) / 3 + (long)last - (long)now;
/* Are we close to a lease timeout? */
if (time_after(now, last + lease/3)) {
cred = nfs4_get_renew_cred_locked(clp);
cred = ops->get_state_renewal_cred_locked(clp);
spin_unlock(&clp->cl_lock);
if (cred == NULL) {
if (list_empty(&clp->cl_delegations)) {
......@@ -86,7 +88,7 @@ nfs4_renew_state(struct work_struct *work)
nfs_expire_all_delegations(clp);
} else {
/* Queue an asynchronous RENEW. */
nfs4_proc_async_renew(clp, cred);
ops->sched_state_renewal(clp, cred);
put_rpccred(cred);
}
timeout = (2 * lease) / 3;
......
......@@ -60,7 +60,7 @@ const nfs4_stateid zero_stateid;
static LIST_HEAD(nfs4_clientid_list);
static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{
unsigned short port;
int status;
......@@ -77,7 +77,7 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
return status;
}
static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
{
struct rpc_cred *cred = NULL;
......@@ -114,17 +114,21 @@ struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
return cred;
}
static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
#if defined(CONFIG_NFS_V4_1)
struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
{
struct rpc_cred *cred;
spin_lock(&clp->cl_lock);
cred = nfs4_get_renew_cred_locked(clp);
cred = nfs4_get_machine_cred_locked(clp);
spin_unlock(&clp->cl_lock);
return cred;
}
static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
#endif /* CONFIG_NFS_V4_1 */
struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
{
struct nfs4_state_owner *sp;
struct rb_node *pos;
......@@ -738,12 +742,14 @@ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
{
if (status == -NFS4ERR_BAD_SEQID) {
struct nfs4_state_owner *sp = container_of(seqid->sequence,
struct nfs4_state_owner, so_seqid);
struct nfs4_state_owner *sp = container_of(seqid->sequence,
struct nfs4_state_owner, so_seqid);
struct nfs_server *server = sp->so_server;
if (status == -NFS4ERR_BAD_SEQID)
nfs4_drop_state_owner(sp);
}
nfs_increment_seqid(status, seqid);
if (!nfs4_has_session(server->nfs_client))
nfs_increment_seqid(status, seqid);
}
/*
......@@ -847,32 +853,45 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_
struct file_lock *fl;
int status = 0;
if (inode->i_flock == NULL)
return 0;
/* Guard against delegation returns and new lock/unlock calls */
down_write(&nfsi->rwsem);
/* Protect inode->i_flock using the BKL */
lock_kernel();
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
continue;
if (nfs_file_open_context(fl->fl_file)->state != state)
continue;
unlock_kernel();
status = ops->recover_lock(state, fl);
if (status >= 0)
continue;
switch (status) {
case 0:
break;
case -ESTALE:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE:
case -NFS4ERR_STALE_CLIENTID:
goto out;
default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
__func__, status);
case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE:
case -ENOMEM:
case -NFS4ERR_DENIED:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
break;
case -NFS4ERR_STALE_CLIENTID:
goto out_err;
status = 0;
}
lock_kernel();
}
up_write(&nfsi->rwsem);
return 0;
out_err:
unlock_kernel();
out:
up_write(&nfsi->rwsem);
return status;
}
......@@ -918,6 +937,7 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
__func__, status);
case -ENOENT:
case -ENOMEM:
case -ESTALE:
/*
* Open state on this file cannot be recovered
......@@ -928,6 +948,9 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
/* Mark the file as being 'closed' */
state->state = 0;
break;
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
nfs4_state_mark_reclaim_nograce(sp->so_client, state);
......@@ -1042,6 +1065,14 @@ static void nfs4_recovery_handle_error(struct nfs_client *clp, int error)
case -NFS4ERR_EXPIRED:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
case -NFS4ERR_BADSESSION:
case -NFS4ERR_BADSLOT:
case -NFS4ERR_BAD_HIGH_SLOT:
case -NFS4ERR_DEADSESSION:
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
case -NFS4ERR_SEQ_FALSE_RETRY:
case -NFS4ERR_SEQ_MISORDERED:
set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
}
}
......@@ -1075,18 +1106,22 @@ static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recov
static int nfs4_check_lease(struct nfs_client *clp)
{
struct rpc_cred *cred;
struct nfs4_state_maintenance_ops *ops =
nfs4_state_renewal_ops[clp->cl_minorversion];
int status = -NFS4ERR_EXPIRED;
/* Is the client already known to have an expired lease? */
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
return 0;
cred = nfs4_get_renew_cred(clp);
spin_lock(&clp->cl_lock);
cred = ops->get_state_renewal_cred_locked(clp);
spin_unlock(&clp->cl_lock);
if (cred == NULL) {
cred = nfs4_get_setclientid_cred(clp);
if (cred == NULL)
goto out;
}
status = nfs4_proc_renew(clp, cred);
status = ops->renew_lease(clp, cred);
put_rpccred(cred);
out:
nfs4_recovery_handle_error(clp, status);
......@@ -1096,21 +1131,98 @@ static int nfs4_check_lease(struct nfs_client *clp)
static int nfs4_reclaim_lease(struct nfs_client *clp)
{
struct rpc_cred *cred;
struct nfs4_state_recovery_ops *ops =
nfs4_reboot_recovery_ops[clp->cl_minorversion];
int status = -ENOENT;
cred = nfs4_get_setclientid_cred(clp);
cred = ops->get_clid_cred(clp);
if (cred != NULL) {
status = nfs4_init_client(clp, cred);
status = ops->establish_clid(clp, cred);
put_rpccred(cred);
/* Handle case where the user hasn't set up machine creds */
if (status == -EACCES && cred == clp->cl_machine_cred) {
nfs4_clear_machine_cred(clp);
status = -EAGAIN;
}
if (status == -NFS4ERR_MINOR_VERS_MISMATCH)
status = -EPROTONOSUPPORT;
}
return status;
}
#ifdef CONFIG_NFS_V4_1
static void nfs4_session_recovery_handle_error(struct nfs_client *clp, int err)
{
switch (err) {
case -NFS4ERR_STALE_CLIENTID:
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
}
}
static int nfs4_reset_session(struct nfs_client *clp)
{
int status;
status = nfs4_proc_destroy_session(clp->cl_session);
if (status && status != -NFS4ERR_BADSESSION &&
status != -NFS4ERR_DEADSESSION) {
nfs4_session_recovery_handle_error(clp, status);
goto out;
}
memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
status = nfs4_proc_create_session(clp, 1);
if (status)
nfs4_session_recovery_handle_error(clp, status);
/* fall through*/
out:
/* Wake up the next rpc task even on error */
rpc_wake_up_next(&clp->cl_session->fc_slot_table.slot_tbl_waitq);
return status;
}
static int nfs4_initialize_session(struct nfs_client *clp)
{
int status;
status = nfs4_proc_create_session(clp, 0);
if (!status) {
nfs_mark_client_ready(clp, NFS_CS_READY);
} else if (status == -NFS4ERR_STALE_CLIENTID) {
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
} else {
nfs_mark_client_ready(clp, status);
}
return status;
}
#else /* CONFIG_NFS_V4_1 */
static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
static int nfs4_initialize_session(struct nfs_client *clp) { return 0; }
#endif /* CONFIG_NFS_V4_1 */
/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors
* on EXCHANGE_ID for v4.1
*/
static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
{
if (nfs4_has_session(clp)) {
switch (status) {
case -NFS4ERR_DELAY:
case -NFS4ERR_CLID_INUSE:
case -EAGAIN:
break;
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
* in nfs4_exchange_id */
default:
return;
}
}
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
}
static void nfs4_state_manager(struct nfs_client *clp)
{
int status = 0;
......@@ -1121,9 +1233,12 @@ static void nfs4_state_manager(struct nfs_client *clp)
/* We're going to have to re-establish a clientid */
status = nfs4_reclaim_lease(clp);
if (status) {
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_set_lease_expired(clp, status);
if (status == -EAGAIN)
continue;
if (clp->cl_cons_state ==
NFS_CS_SESSION_INITING)
nfs_mark_client_ready(clp, status);
goto out_error;
}
clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
......@@ -1134,25 +1249,44 @@ static void nfs4_state_manager(struct nfs_client *clp)
if (status != 0)
continue;
}
/* Initialize or reset the session */
if (nfs4_has_session(clp) &&
test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) {
if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
status = nfs4_initialize_session(clp);
else
status = nfs4_reset_session(clp);
if (status) {
if (status == -NFS4ERR_STALE_CLIENTID)
continue;
goto out_error;
}
}
/* First recover reboot state... */
if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops);
status = nfs4_do_reclaim(clp,
nfs4_reboot_recovery_ops[clp->cl_minorversion]);
if (status == -NFS4ERR_STALE_CLIENTID)
continue;
if (test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state))
continue;
nfs4_state_end_reclaim_reboot(clp);
continue;
}
/* Now recover expired state... */
if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops);
status = nfs4_do_reclaim(clp,
nfs4_nograce_recovery_ops[clp->cl_minorversion]);
if (status < 0) {
set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
if (status == -NFS4ERR_STALE_CLIENTID)
continue;
if (status == -NFS4ERR_EXPIRED)
continue;
if (test_bit(NFS4CLNT_SESSION_SETUP,
&clp->cl_state))
continue;
goto out_error;
} else
nfs4_state_end_reclaim_nograce(clp);
......
此差异已折叠。
......@@ -92,6 +92,9 @@
#undef NFSROOT_DEBUG
#define NFSDBG_FACILITY NFSDBG_ROOT
/* Default port to use if server is not running a portmapper */
#define NFS_MNT_PORT 627
/* Default path we try to mount. "%s" gets replaced by our IP address */
#define NFS_ROOT "/tftpboot/%s"
......@@ -487,6 +490,7 @@ static int __init root_nfs_get_handle(void)
{
struct nfs_fh fh;
struct sockaddr_in sin;
unsigned int auth_flav_len = 0;
struct nfs_mount_request request = {
.sap = (struct sockaddr *)&sin,
.salen = sizeof(sin),
......@@ -496,6 +500,7 @@ static int __init root_nfs_get_handle(void)
.protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP,
.fh = &fh,
.auth_flav_len = &auth_flav_len,
};
int status;
......
......@@ -22,6 +22,7 @@
#include <asm/system.h>
#include "nfs4_fs.h"
#include "internal.h"
#include "iostat.h"
#include "fscache.h"
......@@ -46,6 +47,7 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
p->npages = pagecount;
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array;
else {
......@@ -357,19 +359,25 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data
struct nfs_readres *resp = &data->res;
if (resp->eof || resp->count == argp->count)
return;
goto out;
/* This is a short read! */
nfs_inc_stats(data->inode, NFSIOS_SHORTREAD);
/* Has the server at least made some progress? */
if (resp->count == 0)
return;
goto out;
/* Yes, so retry the read at the end of the data */
argp->offset += resp->count;
argp->pgbase += resp->count;
argp->count -= resp->count;
rpc_restart_call(task);
nfs4_restart_rpc(task, NFS_SERVER(data->inode)->nfs_client);
return;
out:
nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client,
&data->res.seq_res);
return;
}
/*
......@@ -406,7 +414,23 @@ static void nfs_readpage_release_partial(void *calldata)
nfs_readdata_release(calldata);
}
#if defined(CONFIG_NFS_V4_1)
void nfs_read_prepare(struct rpc_task *task, void *calldata)
{
struct nfs_read_data *data = calldata;
if (nfs4_setup_sequence(NFS_SERVER(data->inode)->nfs_client,
&data->args.seq_args, &data->res.seq_res,
0, task))
return;
rpc_call_start(task);
}
#endif /* CONFIG_NFS_V4_1 */
static const struct rpc_call_ops nfs_read_partial_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_read_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_readpage_result_partial,
.rpc_release = nfs_readpage_release_partial,
};
......@@ -470,6 +494,9 @@ static void nfs_readpage_release_full(void *calldata)
}
static const struct rpc_call_ops nfs_read_full_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_read_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_readpage_result_full,
.rpc_release = nfs_readpage_release_full,
};
......
......@@ -90,6 +90,7 @@ enum {
Opt_mountport,
Opt_mountvers,
Opt_nfsvers,
Opt_minorversion,
/* Mount options that take string arguments */
Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
......@@ -139,22 +140,23 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_fscache_uniq, "fsc=%s" },
{ Opt_nofscache, "nofsc" },
{ Opt_port, "port=%u" },
{ Opt_rsize, "rsize=%u" },
{ Opt_wsize, "wsize=%u" },
{ Opt_bsize, "bsize=%u" },
{ Opt_timeo, "timeo=%u" },
{ Opt_retrans, "retrans=%u" },
{ Opt_acregmin, "acregmin=%u" },
{ Opt_acregmax, "acregmax=%u" },
{ Opt_acdirmin, "acdirmin=%u" },
{ Opt_acdirmax, "acdirmax=%u" },
{ Opt_actimeo, "actimeo=%u" },
{ Opt_namelen, "namlen=%u" },
{ Opt_mountport, "mountport=%u" },
{ Opt_mountvers, "mountvers=%u" },
{ Opt_nfsvers, "nfsvers=%u" },
{ Opt_nfsvers, "vers=%u" },
{ Opt_port, "port=%s" },
{ Opt_rsize, "rsize=%s" },
{ Opt_wsize, "wsize=%s" },
{ Opt_bsize, "bsize=%s" },
{ Opt_timeo, "timeo=%s" },
{ Opt_retrans, "retrans=%s" },
{ Opt_acregmin, "acregmin=%s" },
{ Opt_acregmax, "acregmax=%s" },
{ Opt_acdirmin, "acdirmin=%s" },
{ Opt_acdirmax, "acdirmax=%s" },
{ Opt_actimeo, "actimeo=%s" },
{ Opt_namelen, "namlen=%s" },
{ Opt_mountport, "mountport=%s" },
{ Opt_mountvers, "mountvers=%s" },
{ Opt_nfsvers, "nfsvers=%s" },
{ Opt_nfsvers, "vers=%s" },
{ Opt_minorversion, "minorversion=%u" },
{ Opt_sec, "sec=%s" },
{ Opt_proto, "proto=%s" },
......@@ -514,7 +516,6 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
const char *nostr;
} nfs_info[] = {
{ NFS_MOUNT_SOFT, ",soft", ",hard" },
{ NFS_MOUNT_INTR, ",intr", ",nointr" },
{ NFS_MOUNT_POSIX, ",posix", "" },
{ NFS_MOUNT_NOCTO, ",nocto", "" },
{ NFS_MOUNT_NOAC, ",noac", "" },
......@@ -943,11 +944,6 @@ static int nfs_parse_security_flavors(char *value,
return 1;
}
static void nfs_parse_invalid_value(const char *option)
{
dfprintk(MOUNT, "NFS: bad value specified for %s option\n", option);
}
/*
* Error-check and convert a string of mount options from user space into
* a data structure. The whole mount string is processed; bad options are
......@@ -958,7 +954,7 @@ static int nfs_parse_mount_options(char *raw,
struct nfs_parsed_mount_data *mnt)
{
char *p, *string, *secdata;
int rc, sloppy = 0, errors = 0;
int rc, sloppy = 0, invalid_option = 0;
if (!raw) {
dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
......@@ -982,7 +978,9 @@ static int nfs_parse_mount_options(char *raw,
while ((p = strsep(&raw, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int option, token;
unsigned long option;
int int_option;
int token;
if (!*p)
continue;
......@@ -1091,114 +1089,156 @@ static int nfs_parse_mount_options(char *raw,
* options that take numeric values
*/
case Opt_port:
if (match_int(args, &option) ||
option < 0 || option > USHORT_MAX) {
errors++;
nfs_parse_invalid_value("port");
} else
mnt->nfs_server.port = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0 || option > USHORT_MAX)
goto out_invalid_value;
mnt->nfs_server.port = option;
break;
case Opt_rsize:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("rsize");
} else
mnt->rsize = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->rsize = option;
break;
case Opt_wsize:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("wsize");
} else
mnt->wsize = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->wsize = option;
break;
case Opt_bsize:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("bsize");
} else
mnt->bsize = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->bsize = option;
break;
case Opt_timeo:
if (match_int(args, &option) || option <= 0) {
errors++;
nfs_parse_invalid_value("timeo");
} else
mnt->timeo = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0 || option == 0)
goto out_invalid_value;
mnt->timeo = option;
break;
case Opt_retrans:
if (match_int(args, &option) || option <= 0) {
errors++;
nfs_parse_invalid_value("retrans");
} else
mnt->retrans = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0 || option == 0)
goto out_invalid_value;
mnt->retrans = option;
break;
case Opt_acregmin:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("acregmin");
} else
mnt->acregmin = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->acregmin = option;
break;
case Opt_acregmax:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("acregmax");
} else
mnt->acregmax = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->acregmax = option;
break;
case Opt_acdirmin:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("acdirmin");
} else
mnt->acdirmin = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->acdirmin = option;
break;
case Opt_acdirmax:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("acdirmax");
} else
mnt->acdirmax = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->acdirmax = option;
break;
case Opt_actimeo:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("actimeo");
} else
mnt->acregmin = mnt->acregmax =
mnt->acdirmin = mnt->acdirmax = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->acregmin = mnt->acregmax =
mnt->acdirmin = mnt->acdirmax = option;
break;
case Opt_namelen:
if (match_int(args, &option) || option < 0) {
errors++;
nfs_parse_invalid_value("namlen");
} else
mnt->namlen = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
mnt->namlen = option;
break;
case Opt_mountport:
if (match_int(args, &option) ||
option < 0 || option > USHORT_MAX) {
errors++;
nfs_parse_invalid_value("mountport");
} else
mnt->mount_server.port = option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0 || option > USHORT_MAX)
goto out_invalid_value;
mnt->mount_server.port = option;
break;
case Opt_mountvers:
if (match_int(args, &option) ||
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0 ||
option < NFS_MNT_VERSION ||
option > NFS_MNT3_VERSION) {
errors++;
nfs_parse_invalid_value("mountvers");
} else
mnt->mount_server.version = option;
option > NFS_MNT3_VERSION)
goto out_invalid_value;
mnt->mount_server.version = option;
break;
case Opt_nfsvers:
if (match_int(args, &option)) {
errors++;
nfs_parse_invalid_value("nfsvers");
break;
}
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
switch (option) {
case NFS2_VERSION:
mnt->flags &= ~NFS_MOUNT_VER3;
......@@ -1207,10 +1247,16 @@ static int nfs_parse_mount_options(char *raw,
mnt->flags |= NFS_MOUNT_VER3;
break;
default:
errors++;
nfs_parse_invalid_value("nfsvers");
goto out_invalid_value;
}
break;
case Opt_minorversion:
if (match_int(args, &int_option))
return 0;
if (int_option < 0 || int_option > NFS4_MAX_MINOR_VERSION)
return 0;
mnt->minorversion = int_option;
break;
/*
* options that take text values
......@@ -1222,9 +1268,9 @@ static int nfs_parse_mount_options(char *raw,
rc = nfs_parse_security_flavors(string, mnt);
kfree(string);
if (!rc) {
errors++;
dfprintk(MOUNT, "NFS: unrecognized "
"security flavor\n");
return 0;
}
break;
case Opt_proto:
......@@ -1238,23 +1284,25 @@ static int nfs_parse_mount_options(char *raw,
case Opt_xprt_udp:
mnt->flags &= ~NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
kfree(string);
break;
case Opt_xprt_tcp:
mnt->flags |= NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
kfree(string);
break;
case Opt_xprt_rdma:
/* vector side protocols to TCP */
mnt->flags |= NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
xprt_load_transport(string);
kfree(string);
break;
default:
errors++;
dfprintk(MOUNT, "NFS: unrecognized "
"transport protocol\n");
return 0;
}
kfree(string);
break;
case Opt_mountproto:
string = match_strdup(args);
......@@ -1273,9 +1321,9 @@ static int nfs_parse_mount_options(char *raw,
break;
case Opt_xprt_rdma: /* not used for side protocols */
default:
errors++;
dfprintk(MOUNT, "NFS: unrecognized "
"transport protocol\n");
return 0;
}
break;
case Opt_addr:
......@@ -1331,9 +1379,9 @@ static int nfs_parse_mount_options(char *raw,
mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
break;
default:
errors++;
dfprintk(MOUNT, "NFS: invalid "
"lookupcache argument\n");
return 0;
};
break;
......@@ -1351,20 +1399,20 @@ static int nfs_parse_mount_options(char *raw,
break;
default:
errors++;
invalid_option = 1;
dfprintk(MOUNT, "NFS: unrecognized mount option "
"'%s'\n", p);
}
}
if (errors > 0) {
dfprintk(MOUNT, "NFS: parsing encountered %d error%s\n",
errors, (errors == 1 ? "" : "s"));
if (!sloppy)
return 0;
}
if (!sloppy && invalid_option)
return 0;
return 1;
out_invalid_value:
printk(KERN_INFO "NFS: bad mount option value specified: %s \n", p);
return 0;
out_nomem:
printk(KERN_INFO "NFS: not enough memory to parse option\n");
return 0;
......@@ -1381,6 +1429,7 @@ static int nfs_parse_mount_options(char *raw,
static int nfs_try_mount(struct nfs_parsed_mount_data *args,
struct nfs_fh *root_fh)
{
unsigned int auth_flavor_len = 0;
struct nfs_mount_request request = {
.sap = (struct sockaddr *)
&args->mount_server.address,
......@@ -1388,6 +1437,7 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
.protocol = args->mount_server.protocol,
.fh = root_fh,
.noresvport = args->flags & NFS_MOUNT_NORESVPORT,
.auth_flav_len = &auth_flavor_len,
};
int status;
......@@ -2240,6 +2290,11 @@ static void nfs4_fill_super(struct super_block *sb)
nfs_initialise_sb(sb);
}
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
{
args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3);
}
/*
* Validate NFSv4 mount options
*/
......@@ -2263,6 +2318,7 @@ static int nfs4_validate_mount_data(void *options,
args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */
args->auth_flavors[0] = RPC_AUTH_UNIX;
args->auth_flavor_len = 0;
args->minorversion = 0;
switch (data->version) {
case 1:
......@@ -2336,6 +2392,8 @@ static int nfs4_validate_mount_data(void *options,
nfs_validate_transport_protocol(args);
nfs4_validate_mount_flags(args);
if (args->auth_flavor_len > 1)
goto out_inval_auth;
......@@ -2477,12 +2535,13 @@ static void nfs4_kill_super(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
dprintk("--> %s\n", __func__);
nfs_super_return_all_delegations(sb);
kill_anon_super(sb);
nfs4_renewd_prepare_shutdown(server);
nfs_fscache_release_super_cookie(sb);
nfs_free_server(server);
dprintk("<-- %s\n", __func__);
}
/*
......
......@@ -15,6 +15,7 @@
#include <linux/wait.h>
#include "internal.h"
#include "nfs4_fs.h"
struct nfs_unlinkdata {
struct hlist_node list;
......@@ -82,7 +83,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
struct inode *dir = data->dir;
if (!NFS_PROTO(dir)->unlink_done(task, dir))
rpc_restart_call(task);
nfs4_restart_rpc(task, NFS_SERVER(dir)->nfs_client);
}
/**
......@@ -102,9 +103,25 @@ static void nfs_async_unlink_release(void *calldata)
nfs_sb_deactive(sb);
}
#if defined(CONFIG_NFS_V4_1)
void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
{
struct nfs_unlinkdata *data = calldata;
struct nfs_server *server = NFS_SERVER(data->dir);
if (nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
&data->res.seq_res, 1, task))
return;
rpc_call_start(task);
}
#endif /* CONFIG_NFS_V4_1 */
static const struct rpc_call_ops nfs_unlink_ops = {
.rpc_call_done = nfs_async_unlink_done,
.rpc_release = nfs_async_unlink_release,
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_unlink_prepare,
#endif /* CONFIG_NFS_V4_1 */
};
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
......@@ -241,6 +258,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
status = PTR_ERR(data->cred);
goto out_free;
}
data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
status = -EBUSY;
spin_lock(&dentry->d_lock);
......
......@@ -25,6 +25,7 @@
#include "delegation.h"
#include "internal.h"
#include "iostat.h"
#include "nfs4_fs.h"
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
......@@ -52,6 +53,7 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
}
return p;
}
......@@ -71,6 +73,7 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
p->npages = pagecount;
p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
if (pagecount <= ARRAY_SIZE(p->page_array))
p->pagevec = p->page_array;
else {
......@@ -1048,7 +1051,23 @@ static void nfs_writeback_release_partial(void *calldata)
nfs_writedata_release(calldata);
}
#if defined(CONFIG_NFS_V4_1)
void nfs_write_prepare(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
struct nfs_client *clp = (NFS_SERVER(data->inode))->nfs_client;
if (nfs4_setup_sequence(clp, &data->args.seq_args,
&data->res.seq_res, 1, task))
return;
rpc_call_start(task);
}
#endif /* CONFIG_NFS_V4_1 */
static const struct rpc_call_ops nfs_write_partial_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_write_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_writeback_done_partial,
.rpc_release = nfs_writeback_release_partial,
};
......@@ -1111,6 +1130,9 @@ static void nfs_writeback_release_full(void *calldata)
}
static const struct rpc_call_ops nfs_write_full_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_write_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_writeback_done_full,
.rpc_release = nfs_writeback_release_full,
};
......@@ -1123,6 +1145,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
{
struct nfs_writeargs *argp = &data->args;
struct nfs_writeres *resp = &data->res;
struct nfs_server *server = NFS_SERVER(data->inode);
int status;
dprintk("NFS: %5u nfs_writeback_done (status %d)\n",
......@@ -1155,7 +1178,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
if (time_before(complain, jiffies)) {
dprintk("NFS: faulty NFS server %s:"
" (committed = %d) != (stable = %d)\n",
NFS_SERVER(data->inode)->nfs_client->cl_hostname,
server->nfs_client->cl_hostname,
resp->verf->committed, argp->stable);
complain = jiffies + 300 * HZ;
}
......@@ -1181,7 +1204,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
*/
argp->stable = NFS_FILE_SYNC;
}
rpc_restart_call(task);
nfs4_restart_rpc(task, server->nfs_client);
return -EAGAIN;
}
if (time_before(complain, jiffies)) {
......@@ -1193,6 +1216,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
/* Can't do anything about it except throw an error. */
task->tk_status = -EIO;
}
nfs4_sequence_free_slot(server->nfs_client, &data->res.seq_res);
return 0;
}
......@@ -1349,6 +1373,9 @@ static void nfs_commit_release(void *calldata)
}
static const struct rpc_call_ops nfs_commit_ops = {
#if defined(CONFIG_NFS_V4_1)
.rpc_call_prepare = nfs_write_prepare,
#endif /* CONFIG_NFS_V4_1 */
.rpc_call_done = nfs_commit_done,
.rpc_release = nfs_commit_release,
};
......
......@@ -195,7 +195,7 @@ extern struct svc_procedure nlmsvc_procedures4[];
extern int nlmsvc_grace_period;
extern unsigned long nlmsvc_timeout;
extern int nsm_use_hostnames;
extern int nsm_local_state;
extern u32 nsm_local_state;
/*
* Lockd client functions
......
......@@ -25,8 +25,9 @@
#define NFSMODE_SOCK 0140000
#define NFSMODE_FIFO 0010000
#define NFS_MNT_PROGRAM 100005
#define NFS_MNT_PORT 627
#define NFS_MNT_PROGRAM 100005
#define NFS_MNT_VERSION 1
#define NFS_MNT3_VERSION 3
/*
* NFS stats. The good thing with these values is that NFSv3 errors are
......
......@@ -64,11 +64,4 @@ struct nfs2_fh {
#define NFSPROC_READDIR 16
#define NFSPROC_STATFS 17
#define NFS_MNT_PROGRAM 100005
#define NFS_MNT_VERSION 1
#define MNTPROC_NULL 0
#define MNTPROC_MNT 1
#define MNTPROC_UMNT 3
#define MNTPROC_UMNTALL 4
#endif /* _LINUX_NFS2_H */
......@@ -88,12 +88,7 @@ struct nfs3_fh {
#define NFS3PROC_PATHCONF 20
#define NFS3PROC_COMMIT 21
#define NFS_MNT3_PROGRAM 100005
#define NFS_MNT3_VERSION 3
#define MOUNTPROC3_NULL 0
#define MOUNTPROC3_MNT 1
#define MOUNTPROC3_UMNT 3
#define MOUNTPROC3_UMNTALL 4
#if defined(__KERNEL__)
......
......@@ -21,6 +21,7 @@
#define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_OPAQUE_LIMIT 1024
#define NFS4_MAX_SESSIONID_LEN 16
#define NFS4_ACCESS_READ 0x0001
......@@ -130,6 +131,16 @@
#define NFS4_MAX_UINT64 (~(u64)0)
/* An NFS4 sessions server must support at least NFS4_MAX_OPS operations.
* If a compound requires more operations, adjust NFS4_MAX_OPS accordingly.
*/
#define NFS4_MAX_OPS 8
/* Our NFS4 client back channel server only wants the cb_sequene and the
* actual operation per compound
*/
#define NFS4_MAX_BACK_CHANNEL_OPS 2
enum nfs4_acl_whotype {
NFS4_ACL_WHO_NAMED = 0,
NFS4_ACL_WHO_OWNER,
......@@ -462,6 +473,13 @@ enum lock_type4 {
#define NFSPROC4_NULL 0
#define NFSPROC4_COMPOUND 1
#define NFS4_MINOR_VERSION 0
#if defined(CONFIG_NFS_V4_1)
#define NFS4_MAX_MINOR_VERSION 1
#else
#define NFS4_MAX_MINOR_VERSION 0
#endif /* CONFIG_NFS_V4_1 */
#define NFS4_DEBUG 1
/* Index of predefined Linux client operations */
......
此差异已折叠。
此差异已折叠。
......@@ -41,7 +41,6 @@
#include <linux/kref.h>
#include <linux/sunrpc/clnt.h>
#define NFS4_OPAQUE_LIMIT 1024
typedef struct {
u32 cl_boot;
u32 cl_id;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册