summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig179
-rw-r--r--fs/lockd/clntproc.c184
-rw-r--r--fs/lockd/host.c93
-rw-r--r--fs/lockd/mon.c113
-rw-r--r--fs/lockd/svc.c162
-rw-r--r--fs/lockd/svclock.c6
-rw-r--r--fs/lockd/svcshare.c3
-rw-r--r--fs/nfs/Makefile3
-rw-r--r--fs/nfs/callback.c93
-rw-r--r--fs/nfs/client.c23
-rw-r--r--fs/nfs/dir.c2
-rw-r--r--fs/nfs/direct.c88
-rw-r--r--fs/nfs/file.c18
-rw-r--r--fs/nfs/inode.c45
-rw-r--r--fs/nfs/internal.h13
-rw-r--r--fs/nfs/namespace.c2
-rw-r--r--fs/nfs/nfs2xdr.c113
-rw-r--r--fs/nfs/nfs3xdr.c71
-rw-r--r--fs/nfs/nfs4proc.c39
-rw-r--r--fs/nfs/nfs4state.c49
-rw-r--r--fs/nfs/nfs4xdr.c147
-rw-r--r--fs/nfs/read.c94
-rw-r--r--fs/nfs/super.c147
-rw-r--r--fs/nfs/symlink.c1
-rw-r--r--fs/nfs/unlink.c2
-rw-r--r--fs/nfs/write.c207
-rw-r--r--fs/nfsd/auth.c1
-rw-r--r--fs/nfsd/export.c9
-rw-r--r--fs/nfsd/nfs4callback.c28
-rw-r--r--fs/nfsd/nfs4idmap.c2
-rw-r--r--fs/nfsd/nfs4state.c71
-rw-r--r--fs/nfsd/nfs4xdr.c13
-rw-r--r--fs/nfsd/nfsctl.c22
-rw-r--r--fs/nfsd/nfsfh.c228
-rw-r--r--fs/nfsd/nfssvc.c2
-rw-r--r--fs/nfsd/vfs.c35
36 files changed, 1376 insertions, 932 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index 8b18a8758677..2e43d46f65d6 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -411,7 +411,7 @@ config JFS_STATISTICS
to be made available to the user in the /proc/fs/jfs/ directory.
config FS_POSIX_ACL
-# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs)
+# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/nfs4)
#
# NOTE: you can implement Posix ACLs without these helpers (XFS does).
# Never use this symbol for ifdefs.
@@ -1664,105 +1664,86 @@ config NFS_V4
If unsure, say N.
-config NFS_DIRECTIO
- bool "Allow direct I/O on NFS files"
- depends on NFS_FS
- help
- This option enables applications to perform uncached I/O on files
- in NFS file systems using the O_DIRECT open() flag. When O_DIRECT
- is set for a file, its data is not cached in the system's page
- cache. Data is moved to and from user-level application buffers
- directly. Unlike local disk-based file systems, NFS O_DIRECT has
- no alignment restrictions.
-
- Unless your program is designed to use O_DIRECT properly, you are
- much better off allowing the NFS client to manage data caching for
- you. Misusing O_DIRECT can cause poor server performance or network
- storms. This kernel build option defaults OFF to avoid exposing
- system administrators unwittingly to a potentially hazardous
- feature.
-
- For more details on NFS O_DIRECT, see fs/nfs/direct.c.
-
- If unsure, say N. This reduces the size of the NFS client, and
- causes open() to return EINVAL if a file residing in NFS is
- opened with the O_DIRECT flag.
-
config NFSD
tristate "NFS server support"
depends on INET
select LOCKD
select SUNRPC
select EXPORTFS
- select NFSD_V2_ACL if NFSD_V3_ACL
select NFS_ACL_SUPPORT if NFSD_V2_ACL
- select NFSD_TCP if NFSD_V4
- select CRYPTO_MD5 if NFSD_V4
- select CRYPTO if NFSD_V4
- select FS_POSIX_ACL if NFSD_V4
- select PROC_FS if NFSD_V4
- select PROC_FS if SUNRPC_GSS
- help
- If you want your Linux box to act as an NFS *server*, so that other
- computers on your local network which support NFS can access certain
- directories on your box transparently, you have two options: you can
- use the self-contained user space program nfsd, in which case you
- should say N here, or you can say Y and use the kernel based NFS
- server. The advantage of the kernel based solution is that it is
- faster.
-
- In either case, you will need support software; the respective
- locations are given in the file <file:Documentation/Changes> in the
- NFS section.
-
- If you say Y here, you will get support for version 2 of the NFS
- protocol (NFSv2). If you also want NFSv3, say Y to the next question
- as well.
-
- Please read the NFS-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile the NFS server support as a module, choose M here: the
- module will be called nfsd. If unsure, say N.
+ help
+ Choose Y here if you want to allow other computers to access
+ files residing on this system using Sun's Network File System
+ protocol. To compile the NFS server support as a module,
+ choose M here: the module will be called nfsd.
+
+ You may choose to use a user-space NFS server instead, in which
+ case you can choose N here.
+
+ To export local file systems using NFS, you also need to install
+ user space programs which can be found in the Linux nfs-utils
+ package, available from http://linux-nfs.org/. More detail about
+ the Linux NFS server implementation is available via the
+ exports(5) man page.
+
+ Below you can choose which versions of the NFS protocol are
+ available to clients mounting the NFS server on this system.
+ Support for NFS version 2 (RFC 1094) is always available when
+ CONFIG_NFSD is selected.
+
+ If unsure, say N.
config NFSD_V2_ACL
bool
depends on NFSD
config NFSD_V3
- bool "Provide NFSv3 server support"
+ bool "NFS server support for NFS version 3"
depends on NFSD
help
- If you would like to include the NFSv3 server as well as the NFSv2
- server, say Y here. If unsure, say Y.
+ This option enables support in your system's NFS server for
+ version 3 of the NFS protocol (RFC 1813).
+
+ If unsure, say Y.
config NFSD_V3_ACL
- bool "Provide server support for the NFSv3 ACL protocol extension"
+ bool "NFS server support for the NFSv3 ACL protocol extension"
depends on NFSD_V3
+ select NFSD_V2_ACL
help
- Implement the NFSv3 ACL protocol extension for manipulating POSIX
- Access Control Lists on exported file systems. NFS clients should
- be compiled with the NFSv3 ACL protocol extension; see the
- CONFIG_NFS_V3_ACL option. If unsure, say N.
+ Solaris NFS servers support an auxiliary NFSv3 ACL protocol that
+ never became an official part of the NFS version 3 protocol.
+ This protocol extension allows applications on NFS clients to
+ manipulate POSIX Access Control Lists on files residing on NFS
+ servers. NFS servers enforce POSIX ACLs on local files whether
+ this protocol is available or not.
+
+ This option enables support in your system's NFS server for the
+ NFSv3 ACL protocol extension allowing NFS clients to manipulate
+ POSIX ACLs on files exported by your system's NFS server. NFS
+ clients which support the Solaris NFSv3 ACL protocol can then
+ access and modify ACLs on your NFS server.
+
+ To store ACLs on your NFS server, you also need to enable ACL-
+ related CONFIG options for your local file systems of choice.
+
+ If unsure, say N.
config NFSD_V4
- bool "Provide NFSv4 server support (EXPERIMENTAL)"
- depends on NFSD && NFSD_V3 && EXPERIMENTAL
+ bool "NFS server support for NFS version 4 (EXPERIMENTAL)"
+ depends on NFSD && PROC_FS && EXPERIMENTAL
+ select NFSD_V3
+ select FS_POSIX_ACL
select RPCSEC_GSS_KRB5
help
- If you would like to include the NFSv4 server as well as the NFSv2
- and NFSv3 servers, say Y here. This feature is experimental, and
- should only be used if you are interested in helping to test NFSv4.
- If unsure, say N.
+ This option enables support in your system's NFS server for
+ version 4 of the NFS protocol (RFC 3530).
-config NFSD_TCP
- bool "Provide NFS server over TCP support"
- depends on NFSD
- default y
- help
- If you want your NFS server to support TCP connections, say Y here.
- TCP connections usually perform better than the default UDP when
- the network is lossy or congested. If unsure, say Y.
+ To export files using NFSv4, you need to install additional user
+ space programs which can be found in the Linux nfs-utils package,
+ available from http://linux-nfs.org/.
+
+ If unsure, say N.
config ROOT_NFS
bool "Root file system on NFS"
@@ -1808,15 +1789,33 @@ config SUNRPC_XPRT_RDMA
tristate
depends on SUNRPC && INFINIBAND && EXPERIMENTAL
default SUNRPC && INFINIBAND
+ help
+ This option enables an RPC client transport capability that
+ allows the NFS client to mount servers via an RDMA-enabled
+ transport.
+
+ To compile RPC client RDMA transport support as a module,
+ choose M here: the module will be called xprtrdma.
+
+ If unsure, say N.
config SUNRPC_BIND34
bool "Support for rpcbind versions 3 & 4 (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
+ default n
help
- Provides kernel support for querying rpcbind servers via versions 3
- and 4 of the rpcbind protocol. The kernel automatically falls back
- to version 2 if a remote rpcbind service does not support versions
- 3 or 4.
+ RPC requests over IPv6 networks require support for larger
+ addresses when performing an RPC bind. Sun added support for
+ IPv6 addressing by creating two new versions of the rpcbind
+ protocol (RFC 1833).
+
+ This option enables support in the kernel RPC client for
+ querying rpcbind servers via versions 3 and 4 of the rpcbind
+ protocol. The kernel automatically falls back to version 2
+ if a remote rpcbind service does not support versions 3 or 4.
+ By themselves, these new versions do not provide support for
+ RPC over IPv6, but the new protocol versions are necessary to
+ support it.
If unsure, say N to get traditional behavior (version 2 rpcbind
requests only).
@@ -1830,12 +1829,13 @@ config RPCSEC_GSS_KRB5
select CRYPTO_DES
select CRYPTO_CBC
help
- Provides for secure RPC calls by means of a gss-api
- mechanism based on Kerberos V5. This is required for
- NFSv4.
+ Choose Y here to enable Secure RPC using the Kerberos version 5
+ GSS-API mechanism (RFC 1964).
- Note: Requires an auxiliary userspace daemon which may be found on
- http://www.citi.umich.edu/projects/nfsv4/
+ Secure RPC calls with Kerberos require an auxiliary user-space
+ daemon which may be found in the Linux nfs-utils package
+ available from http://linux-nfs.org/. In addition, user-space
+ Kerberos support should be installed.
If unsure, say N.
@@ -1849,11 +1849,12 @@ config RPCSEC_GSS_SPKM3
select CRYPTO_CAST5
select CRYPTO_CBC
help
- Provides for secure RPC calls by means of a gss-api
- mechanism based on the SPKM3 public-key mechanism.
+ Choose Y here to enable Secure RPC using the SPKM3 public key
+ GSS-API mechansim (RFC 2025).
- Note: Requires an auxiliary userspace daemon which may be found on
- http://www.citi.umich.edu/projects/nfsv4/
+ Secure RPC calls with SPKM3 require an auxiliary userspace
+ daemon which may be found in the Linux nfs-utils package
+ available from http://linux-nfs.org/.
If unsure, say N.
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index b6b74a60e1eb..40b16f23e49a 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -155,8 +155,6 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
{
struct nlm_rqst *call;
- sigset_t oldset;
- unsigned long flags;
int status;
nlm_get_host(host);
@@ -168,22 +166,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
/* Set up the argument struct */
nlmclnt_setlockargs(call, fl);
- /* Keep the old signal mask */
- spin_lock_irqsave(&current->sighand->siglock, flags);
- oldset = current->blocked;
-
- /* If we're cleaning up locks because the process is exiting,
- * perform the RPC call asynchronously. */
- if ((IS_SETLK(cmd) || IS_SETLKW(cmd))
- && fl->fl_type == F_UNLCK
- && (current->flags & PF_EXITING)) {
- sigfillset(&current->blocked); /* Mask all signals */
- recalc_sigpending();
-
- call->a_flags = RPC_TASK_ASYNC;
- }
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
if (fl->fl_type != F_UNLCK) {
call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
@@ -198,11 +180,6 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL;
- spin_lock_irqsave(&current->sighand->siglock, flags);
- current->blocked = oldset;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
dprintk("lockd: clnt proc returns %d\n", status);
return status;
}
@@ -221,6 +198,7 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
for(;;) {
call = kzalloc(sizeof(*call), GFP_KERNEL);
if (call != NULL) {
+ atomic_set(&call->a_count, 1);
locks_init_lock(&call->a_args.lock.fl);
locks_init_lock(&call->a_res.lock.fl);
call->a_host = host;
@@ -237,6 +215,8 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
void nlm_release_call(struct nlm_rqst *call)
{
+ if (!atomic_dec_and_test(&call->a_count))
+ return;
nlm_release_host(call->a_host);
nlmclnt_release_lockargs(call);
kfree(call);
@@ -267,7 +247,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue)
* Generic NLM call
*/
static int
-nlmclnt_call(struct nlm_rqst *req, u32 proc)
+nlmclnt_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
@@ -276,6 +256,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
struct rpc_message msg = {
.rpc_argp = argp,
.rpc_resp = resp,
+ .rpc_cred = cred,
};
int status;
@@ -343,10 +324,16 @@ in_grace_period:
/*
* Generic NLM call, async version.
*/
-static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
+static struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
+ struct rpc_task_setup task_setup_data = {
+ .rpc_message = msg,
+ .callback_ops = tk_ops,
+ .callback_data = req,
+ .flags = RPC_TASK_ASYNC,
+ };
dprintk("lockd: call procedure %d on %s (async)\n",
(int)proc, host->h_name);
@@ -356,21 +343,36 @@ static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *
if (clnt == NULL)
goto out_err;
msg->rpc_proc = &clnt->cl_procinfo[proc];
+ task_setup_data.rpc_client = clnt;
/* bootstrap and kick off the async RPC call */
- return rpc_call_async(clnt, msg, RPC_TASK_ASYNC, tk_ops, req);
+ return rpc_run_task(&task_setup_data);
out_err:
tk_ops->rpc_release(req);
- return -ENOLCK;
+ return ERR_PTR(-ENOLCK);
}
+static int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
+{
+ struct rpc_task *task;
+
+ task = __nlm_async_call(req, proc, msg, tk_ops);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ rpc_put_task(task);
+ return 0;
+}
+
+/*
+ * NLM asynchronous call.
+ */
int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
{
struct rpc_message msg = {
.rpc_argp = &req->a_args,
.rpc_resp = &req->a_res,
};
- return __nlm_async_call(req, proc, &msg, tk_ops);
+ return nlm_do_async_call(req, proc, &msg, tk_ops);
}
int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
@@ -378,7 +380,33 @@ int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *t
struct rpc_message msg = {
.rpc_argp = &req->a_res,
};
- return __nlm_async_call(req, proc, &msg, tk_ops);
+ return nlm_do_async_call(req, proc, &msg, tk_ops);
+}
+
+/*
+ * NLM client asynchronous call.
+ *
+ * Note that although the calls are asynchronous, and are therefore
+ * guaranteed to complete, we still always attempt to wait for
+ * completion in order to be able to correctly track the lock
+ * state.
+ */
+static int nlmclnt_async_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
+{
+ struct rpc_message msg = {
+ .rpc_argp = &req->a_args,
+ .rpc_resp = &req->a_res,
+ .rpc_cred = cred,
+ };
+ struct rpc_task *task;
+ int err;
+
+ task = __nlm_async_call(req, proc, &msg, tk_ops);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ err = rpc_wait_for_completion_task(task);
+ rpc_put_task(task);
+ return err;
}
/*
@@ -389,7 +417,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
{
int status;
- status = nlmclnt_call(req, NLMPROC_TEST);
+ status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST);
if (status < 0)
goto out;
@@ -480,10 +508,12 @@ static int do_vfs_lock(struct file_lock *fl)
static int
nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
{
+ struct rpc_cred *cred = nfs_file_cred(fl->fl_file);
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
struct nlm_wait *block = NULL;
unsigned char fl_flags = fl->fl_flags;
+ unsigned char fl_type;
int status = -ENOLCK;
if (nsm_monitor(host) < 0) {
@@ -493,18 +523,22 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
}
fl->fl_flags |= FL_ACCESS;
status = do_vfs_lock(fl);
+ fl->fl_flags = fl_flags;
if (status < 0)
goto out;
block = nlmclnt_prepare_block(host, fl);
again:
+ /*
+ * Initialise resp->status to a valid non-zero value,
+ * since 0 == nlm_lck_granted
+ */
+ resp->status = nlm_lck_blocked;
for(;;) {
/* Reboot protection */
fl->fl_u.nfs_fl.state = host->h_state;
- status = nlmclnt_call(req, NLMPROC_LOCK);
+ status = nlmclnt_call(cred, req, NLMPROC_LOCK);
if (status < 0)
- goto out_unblock;
- if (!req->a_args.block)
break;
/* Did a reclaimer thread notify us of a server reboot? */
if (resp->status == nlm_lck_denied_grace_period)
@@ -513,15 +547,22 @@ again:
break;
/* Wait on an NLM blocking lock */
status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
- /* if we were interrupted. Send a CANCEL request to the server
- * and exit
- */
if (status < 0)
- goto out_unblock;
+ break;
if (resp->status != nlm_lck_blocked)
break;
}
+ /* if we were interrupted while blocking, then cancel the lock request
+ * and exit
+ */
+ if (resp->status == nlm_lck_blocked) {
+ if (!req->a_args.block)
+ goto out_unlock;
+ if (nlmclnt_cancel(host, req->a_args.block, fl) == 0)
+ goto out_unblock;
+ }
+
if (resp->status == nlm_granted) {
down_read(&host->h_rwsem);
/* Check whether or not the server has rebooted */
@@ -530,20 +571,34 @@ again:
goto again;
}
/* Ensure the resulting lock will get added to granted list */
- fl->fl_flags = fl_flags | FL_SLEEP;
+ fl->fl_flags |= FL_SLEEP;
if (do_vfs_lock(fl) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
up_read(&host->h_rwsem);
+ fl->fl_flags = fl_flags;
+ status = 0;
}
+ if (status < 0)
+ goto out_unlock;
status = nlm_stat_to_errno(resp->status);
out_unblock:
nlmclnt_finish_block(block);
- /* Cancel the blocked request if it is still pending */
- if (resp->status == nlm_lck_blocked)
- nlmclnt_cancel(host, req->a_args.block, fl);
out:
nlm_release_call(req);
+ return status;
+out_unlock:
+ /* Fatal error: ensure that we remove the lock altogether */
+ dprintk("lockd: lock attempt ended in fatal error.\n"
+ " Attempting to unlock.\n");
+ nlmclnt_finish_block(block);
+ fl_type = fl->fl_type;
+ fl->fl_type = F_UNLCK;
+ down_read(&host->h_rwsem);
+ do_vfs_lock(fl);
+ up_read(&host->h_rwsem);
+ fl->fl_type = fl_type;
fl->fl_flags = fl_flags;
+ nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
return status;
}
@@ -567,8 +622,8 @@ nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
nlmclnt_setlockargs(req, fl);
req->a_args.reclaim = 1;
- if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
- && req->a_res.status == nlm_granted)
+ status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK);
+ if (status >= 0 && req->a_res.status == nlm_granted)
return 0;
printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
@@ -598,7 +653,8 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
{
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
- int status = 0;
+ int status;
+ unsigned char fl_flags = fl->fl_flags;
/*
* Note: the server is supposed to either grant us the unlock
@@ -607,16 +663,17 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
*/
fl->fl_flags |= FL_EXISTS;
down_read(&host->h_rwsem);
- if (do_vfs_lock(fl) == -ENOENT) {
- up_read(&host->h_rwsem);
+ status = do_vfs_lock(fl);
+ up_read(&host->h_rwsem);
+ fl->fl_flags = fl_flags;
+ if (status == -ENOENT) {
+ status = 0;
goto out;
}
- up_read(&host->h_rwsem);
-
- if (req->a_flags & RPC_TASK_ASYNC)
- return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
- status = nlmclnt_call(req, NLMPROC_UNLOCK);
+ atomic_inc(&req->a_count);
+ status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
+ NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
if (status < 0)
goto out;
@@ -671,16 +728,10 @@ static const struct rpc_call_ops nlmclnt_unlock_ops = {
static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl)
{
struct nlm_rqst *req;
- unsigned long flags;
- sigset_t oldset;
- int status;
+ int status;
- /* Block all signals while setting up call */
- spin_lock_irqsave(&current->sighand->siglock, flags);
- oldset = current->blocked;
- sigfillset(&current->blocked);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
+ dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"
+ " Attempting to cancel lock.\n");
req = nlm_alloc_call(nlm_get_host(host));
if (!req)
@@ -690,13 +741,12 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
nlmclnt_setlockargs(req, fl);
req->a_args.block = block;
- status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- current->blocked = oldset;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
+ atomic_inc(&req->a_count);
+ status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
+ NLMPROC_CANCEL, &nlmclnt_cancel_ops);
+ if (status == 0 && req->a_res.status == nlm_lck_denied)
+ status = -ENOLCK;
+ nlm_release_call(req);
return status;
}
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index f1ef49fff118..a17664c7eacc 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -19,12 +19,11 @@
#define NLMDBG_FACILITY NLMDBG_HOSTCACHE
-#define NLM_HOST_MAX 64
#define NLM_HOST_NRHASH 32
#define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1))
#define NLM_HOST_REBIND (60 * HZ)
-#define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
-#define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ)
+#define NLM_HOST_EXPIRE (300 * HZ)
+#define NLM_HOST_COLLECT (120 * HZ)
static struct hlist_head nlm_hosts[NLM_HOST_NRHASH];
static unsigned long next_gc;
@@ -42,11 +41,12 @@ static struct nsm_handle * nsm_find(const struct sockaddr_in *sin,
/*
* Common host lookup routine for server & client
*/
-static struct nlm_host *
-nlm_lookup_host(int server, const struct sockaddr_in *sin,
- int proto, int version, const char *hostname,
- unsigned int hostname_len,
- const struct sockaddr_in *ssin)
+static struct nlm_host *nlm_lookup_host(int server,
+ const struct sockaddr_in *sin,
+ int proto, u32 version,
+ const char *hostname,
+ unsigned int hostname_len,
+ const struct sockaddr_in *ssin)
{
struct hlist_head *chain;
struct hlist_node *pos;
@@ -55,7 +55,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin,
int hash;
dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT
- ", p=%d, v=%d, my role=%s, name=%.*s)\n",
+ ", p=%d, v=%u, my role=%s, name=%.*s)\n",
NIPQUAD(ssin->sin_addr.s_addr),
NIPQUAD(sin->sin_addr.s_addr), proto, version,
server? "server" : "client",
@@ -142,9 +142,7 @@ nlm_lookup_host(int server, const struct sockaddr_in *sin,
INIT_LIST_HEAD(&host->h_granted);
INIT_LIST_HEAD(&host->h_reclaim);
- if (++nrhosts > NLM_HOST_MAX)
- next_gc = 0;
-
+ nrhosts++;
out:
mutex_unlock(&nlm_host_mutex);
return host;
@@ -175,9 +173,10 @@ nlm_destroy_host(struct nlm_host *host)
/*
* Find an NLM server handle in the cache. If there is none, create it.
*/
-struct nlm_host *
-nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version,
- const char *hostname, unsigned int hostname_len)
+struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin,
+ int proto, u32 version,
+ const char *hostname,
+ unsigned int hostname_len)
{
struct sockaddr_in ssin = {0};
@@ -460,7 +459,7 @@ nlm_gc_hosts(void)
* Manage NSM handles
*/
static LIST_HEAD(nsm_handles);
-static DEFINE_MUTEX(nsm_mutex);
+static DEFINE_SPINLOCK(nsm_lock);
static struct nsm_handle *
__nsm_find(const struct sockaddr_in *sin,
@@ -468,7 +467,7 @@ __nsm_find(const struct sockaddr_in *sin,
int create)
{
struct nsm_handle *nsm = NULL;
- struct list_head *pos;
+ struct nsm_handle *pos;
if (!sin)
return NULL;
@@ -482,38 +481,43 @@ __nsm_find(const struct sockaddr_in *sin,
return NULL;
}
- mutex_lock(&nsm_mutex);
- list_for_each(pos, &nsm_handles) {
- nsm = list_entry(pos, struct nsm_handle, sm_link);
+retry:
+ spin_lock(&nsm_lock);
+ list_for_each_entry(pos, &nsm_handles, sm_link) {
if (hostname && nsm_use_hostnames) {
- if (strlen(nsm->sm_name) != hostname_len
- || memcmp(nsm->sm_name, hostname, hostname_len))
+ if (strlen(pos->sm_name) != hostname_len
+ || memcmp(pos->sm_name, hostname, hostname_len))
continue;
- } else if (!nlm_cmp_addr(&nsm->sm_addr, sin))
+ } else if (!nlm_cmp_addr(&pos->sm_addr, sin))
continue;
- atomic_inc(&nsm->sm_count);
- goto out;
+ atomic_inc(&pos->sm_count);
+ kfree(nsm);
+ nsm = pos;
+ goto found;
}
-
- if (!create) {
- nsm = NULL;
- goto out;
+ if (nsm) {
+ list_add(&nsm->sm_link, &nsm_handles);
+ goto found;
}
+ spin_unlock(&nsm_lock);
+
+ if (!create)
+ return NULL;
nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
- if (nsm != NULL) {
- nsm->sm_addr = *sin;
- nsm->sm_name = (char *) (nsm + 1);
- memcpy(nsm->sm_name, hostname, hostname_len);
- nsm->sm_name[hostname_len] = '\0';
- atomic_set(&nsm->sm_count, 1);
+ if (nsm == NULL)
+ return NULL;
- list_add(&nsm->sm_link, &nsm_handles);
- }
+ nsm->sm_addr = *sin;
+ nsm->sm_name = (char *) (nsm + 1);
+ memcpy(nsm->sm_name, hostname, hostname_len);
+ nsm->sm_name[hostname_len] = '\0';
+ atomic_set(&nsm->sm_count, 1);
+ goto retry;
-out:
- mutex_unlock(&nsm_mutex);
+found:
+ spin_unlock(&nsm_lock);
return nsm;
}
@@ -532,12 +536,9 @@ nsm_release(struct nsm_handle *nsm)
{
if (!nsm)
return;
- if (atomic_dec_and_test(&nsm->sm_count)) {
- mutex_lock(&nsm_mutex);
- if (atomic_read(&nsm->sm_count) == 0) {
- list_del(&nsm->sm_link);
- kfree(nsm);
- }
- mutex_unlock(&nsm_mutex);
+ if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
+ list_del(&nsm->sm_link);
+ spin_unlock(&nsm_lock);
+ kfree(nsm);
}
}
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 908b23fadd05..e4d563543b11 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -18,6 +18,8 @@
#define NLMDBG_FACILITY NLMDBG_MONITOR
+#define XDR_ADDRBUF_LEN (20)
+
static struct rpc_clnt * nsm_create(void);
static struct rpc_program nsm_program;
@@ -147,28 +149,55 @@ nsm_create(void)
/*
* XDR functions for NSM.
+ *
+ * See http://www.opengroup.org/ for details on the Network
+ * Status Monitor wire protocol.
*/
-static __be32 *
-xdr_encode_common(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
+static __be32 *xdr_encode_nsm_string(__be32 *p, char *string)
{
- char buffer[20], *name;
-
- /*
- * Use the dotted-quad IP address of the remote host as
- * identifier. Linux statd always looks up the canonical
- * hostname first for whatever remote hostname it receives,
- * so this works alright.
- */
- if (nsm_use_hostnames) {
- name = argp->mon_name;
- } else {
- sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr));
+ size_t len = strlen(string);
+
+ if (len > SM_MAXSTRLEN)
+ len = SM_MAXSTRLEN;
+ return xdr_encode_opaque(p, string, len);
+}
+
+/*
+ * "mon_name" specifies the host to be monitored.
+ *
+ * Linux uses a text version of the IP address of the remote
+ * host as the host identifier (the "mon_name" argument).
+ *
+ * Linux statd always looks up the canonical hostname first for
+ * whatever remote hostname it receives, so this works alright.
+ */
+static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp)
+{
+ char buffer[XDR_ADDRBUF_LEN + 1];
+ char *name = argp->mon_name;
+
+ if (!nsm_use_hostnames) {
+ snprintf(buffer, XDR_ADDRBUF_LEN,
+ NIPQUAD_FMT, NIPQUAD(argp->addr));
name = buffer;
}
- if (!(p = xdr_encode_string(p, name))
- || !(p = xdr_encode_string(p, utsname()->nodename)))
+
+ return xdr_encode_nsm_string(p, name);
+}
+
+/*
+ * The "my_id" argument specifies the hostname and RPC procedure
+ * to be called when the status manager receives notification
+ * (via the SM_NOTIFY call) that the state of host "mon_name"
+ * has changed.
+ */
+static __be32 *xdr_encode_my_id(__be32 *p, struct nsm_args *argp)
+{
+ p = xdr_encode_nsm_string(p, utsname()->nodename);
+ if (!p)
return ERR_PTR(-EIO);
+
*p++ = htonl(argp->prog);
*p++ = htonl(argp->vers);
*p++ = htonl(argp->proc);
@@ -176,18 +205,48 @@ xdr_encode_common(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
return p;
}
-static int
-xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
+/*
+ * The "mon_id" argument specifies the non-private arguments
+ * of an SM_MON or SM_UNMON call.
+ */
+static __be32 *xdr_encode_mon_id(__be32 *p, struct nsm_args *argp)
{
- p = xdr_encode_common(rqstp, p, argp);
- if (IS_ERR(p))
- return PTR_ERR(p);
+ p = xdr_encode_mon_name(p, argp);
+ if (!p)
+ return ERR_PTR(-EIO);
- /* Surprise - there may even be room for an IPv6 address now */
+ return xdr_encode_my_id(p, argp);
+}
+
+/*
+ * The "priv" argument may contain private information required
+ * by the SM_MON call. This information will be supplied in the
+ * SM_NOTIFY call.
+ *
+ * Linux provides the raw IP address of the monitored host,
+ * left in network byte order.
+ */
+static __be32 *xdr_encode_priv(__be32 *p, struct nsm_args *argp)
+{
*p++ = argp->addr;
*p++ = 0;
*p++ = 0;
*p++ = 0;
+
+ return p;
+}
+
+static int
+xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
+{
+ p = xdr_encode_mon_id(p, argp);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ p = xdr_encode_priv(p, argp);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
return 0;
}
@@ -195,7 +254,7 @@ xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
static int
xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
{
- p = xdr_encode_common(rqstp, p, argp);
+ p = xdr_encode_mon_id(p, argp);
if (IS_ERR(p))
return PTR_ERR(p);
rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
@@ -220,9 +279,11 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
}
#define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
-#define SM_my_id_sz (3+1+SM_my_name_sz)
-#define SM_mon_id_sz (1+XDR_QUADLEN(20)+SM_my_id_sz)
-#define SM_mon_sz (SM_mon_id_sz+4)
+#define SM_my_id_sz (SM_my_name_sz+3)
+#define SM_mon_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
+#define SM_mon_id_sz (SM_mon_name_sz+SM_my_id_sz)
+#define SM_priv_sz (XDR_QUADLEN(SM_PRIV_SIZE))
+#define SM_mon_sz (SM_mon_id_sz+SM_priv_sz)
#define SM_monres_sz 2
#define SM_unmonres_sz 1
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 1ed8bd4de941..2169af4d5455 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -25,6 +25,7 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/sunrpc/types.h>
@@ -48,14 +49,11 @@ EXPORT_SYMBOL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users;
-static pid_t nlmsvc_pid;
+static struct task_struct *nlmsvc_task;
static struct svc_serv *nlmsvc_serv;
int nlmsvc_grace_period;
unsigned long nlmsvc_timeout;
-static DECLARE_COMPLETION(lockd_start_done);
-static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
-
/*
* These can be set at insmod time (useful for NFS as root filesystem),
* and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003
@@ -74,7 +72,9 @@ static const unsigned long nlm_timeout_min = 3;
static const unsigned long nlm_timeout_max = 20;
static const int nlm_port_min = 0, nlm_port_max = 65535;
+#ifdef CONFIG_SYSCTL
static struct ctl_table_header * nlm_sysctl_table;
+#endif
static unsigned long get_lockd_grace_period(void)
{
@@ -111,35 +111,30 @@ static inline void clear_grace_period(void)
/*
* This is the lockd kernel thread
*/
-static void
-lockd(struct svc_rqst *rqstp)
+static int
+lockd(void *vrqstp)
{
- int err = 0;
+ int err = 0, preverr = 0;
+ struct svc_rqst *rqstp = vrqstp;
unsigned long grace_period_expire;
- /* Lock module and set up kernel thread */
- /* lockd_up is waiting for us to startup, so will
- * be holding a reference to this module, so it
- * is safe to just claim another reference
- */
- __module_get(THIS_MODULE);
- lock_kernel();
-
- /*
- * Let our maker know we're running.
- */
- nlmsvc_pid = current->pid;
- nlmsvc_serv = rqstp->rq_server;
- complete(&lockd_start_done);
-
- daemonize("lockd");
+ /* try_to_freeze() is called from svc_recv() */
set_freezable();
- /* Process request with signals blocked, but allow SIGKILL. */
+ /* Allow SIGKILL to tell lockd to drop all of its locks */
allow_signal(SIGKILL);
dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
+ /*
+ * FIXME: it would be nice if lockd didn't spend its entire life
+ * running under the BKL. At the very least, it would be good to
+ * have someone clarify what it's intended to protect here. I've
+ * seen some handwavy posts about posix locking needing to be
+ * done under the BKL, but it's far from clear.
+ */
+ lock_kernel();
+
if (!nlm_timeout)
nlm_timeout = LOCKD_DFLT_TIMEO;
nlmsvc_timeout = nlm_timeout * HZ;
@@ -148,10 +143,9 @@ lockd(struct svc_rqst *rqstp)
/*
* The main request loop. We don't terminate until the last
- * NFS mount or NFS daemon has gone away, and we've been sent a
- * signal, or else another process has taken over our job.
+ * NFS mount or NFS daemon has gone away.
*/
- while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) {
+ while (!kthread_should_stop()) {
long timeout = MAX_SCHEDULE_TIMEOUT;
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
@@ -161,6 +155,7 @@ lockd(struct svc_rqst *rqstp)
nlmsvc_invalidate_all();
grace_period_expire = set_grace_period();
}
+ continue;
}
/*
@@ -179,14 +174,20 @@ lockd(struct svc_rqst *rqstp)
* recvfrom routine.
*/
err = svc_recv(rqstp, timeout);
- if (err == -EAGAIN || err == -EINTR)
+ if (err == -EAGAIN || err == -EINTR) {
+ preverr = err;
continue;
+ }
if (err < 0) {
- printk(KERN_WARNING
- "lockd: terminating on error %d\n",
- -err);
- break;
+ if (err != preverr) {
+ printk(KERN_WARNING "%s: unexpected error "
+ "from svc_recv (%d)\n", __func__, err);
+ preverr = err;
+ }
+ schedule_timeout_interruptible(HZ);
+ continue;
}
+ preverr = err;
dprintk("lockd: request from %s\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
@@ -195,28 +196,19 @@ lockd(struct svc_rqst *rqstp)
}
flush_signals(current);
+ if (nlmsvc_ops)
+ nlmsvc_invalidate_all();
+ nlm_shutdown_hosts();
- /*
- * Check whether there's a new lockd process before
- * shutting down the hosts and clearing the slot.
- */
- if (!nlmsvc_pid || current->pid == nlmsvc_pid) {
- if (nlmsvc_ops)
- nlmsvc_invalidate_all();
- nlm_shutdown_hosts();
- nlmsvc_pid = 0;
- nlmsvc_serv = NULL;
- } else
- printk(KERN_DEBUG
- "lockd: new process, skipping host shutdown\n");
- wake_up(&lockd_exit);
+ unlock_kernel();
+
+ nlmsvc_task = NULL;
+ nlmsvc_serv = NULL;
/* Exit the RPC thread */
svc_exit_thread(rqstp);
- /* Release module */
- unlock_kernel();
- module_put_and_exit(0);
+ return 0;
}
/*
@@ -261,14 +253,15 @@ static int make_socks(struct svc_serv *serv, int proto)
int
lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
{
- struct svc_serv * serv;
- int error = 0;
+ struct svc_serv *serv;
+ struct svc_rqst *rqstp;
+ int error = 0;
mutex_lock(&nlmsvc_mutex);
/*
* Check whether we're already up and running.
*/
- if (nlmsvc_pid) {
+ if (nlmsvc_serv) {
if (proto)
error = make_socks(nlmsvc_serv, proto);
goto out;
@@ -295,13 +288,28 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
/*
* Create the kernel thread and wait for it to start.
*/
- error = svc_create_thread(lockd, serv);
- if (error) {
+ rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
+ if (IS_ERR(rqstp)) {
+ error = PTR_ERR(rqstp);
+ printk(KERN_WARNING
+ "lockd_up: svc_rqst allocation failed, error=%d\n",
+ error);
+ goto destroy_and_out;
+ }
+
+ svc_sock_update_bufs(serv);
+ nlmsvc_serv = rqstp->rq_server;
+
+ nlmsvc_task = kthread_run(lockd, rqstp, serv->sv_name);
+ if (IS_ERR(nlmsvc_task)) {
+ error = PTR_ERR(nlmsvc_task);
+ nlmsvc_task = NULL;
+ nlmsvc_serv = NULL;
printk(KERN_WARNING
- "lockd_up: create thread failed, error=%d\n", error);
+ "lockd_up: kthread_run failed, error=%d\n", error);
+ svc_exit_thread(rqstp);
goto destroy_and_out;
}
- wait_for_completion(&lockd_start_done);
/*
* Note: svc_serv structures have an initial use count of 1,
@@ -323,42 +331,28 @@ EXPORT_SYMBOL(lockd_up);
void
lockd_down(void)
{
- static int warned;
-
mutex_lock(&nlmsvc_mutex);
if (nlmsvc_users) {
if (--nlmsvc_users)
goto out;
- } else
- printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid);
-
- if (!nlmsvc_pid) {
- if (warned++ == 0)
- printk(KERN_WARNING "lockd_down: no lockd running.\n");
- goto out;
+ } else {
+ printk(KERN_ERR "lockd_down: no users! task=%p\n",
+ nlmsvc_task);
+ BUG();
}
- warned = 0;
- kill_proc(nlmsvc_pid, SIGKILL, 1);
- /*
- * Wait for the lockd process to exit, but since we're holding
- * the lockd semaphore, we can't wait around forever ...
- */
- clear_thread_flag(TIF_SIGPENDING);
- interruptible_sleep_on_timeout(&lockd_exit, HZ);
- if (nlmsvc_pid) {
- printk(KERN_WARNING
- "lockd_down: lockd failed to exit, clearing pid\n");
- nlmsvc_pid = 0;
+ if (!nlmsvc_task) {
+ printk(KERN_ERR "lockd_down: no lockd running.\n");
+ BUG();
}
- spin_lock_irq(&current->sighand->siglock);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ kthread_stop(nlmsvc_task);
out:
mutex_unlock(&nlmsvc_mutex);
}
EXPORT_SYMBOL(lockd_down);
+#ifdef CONFIG_SYSCTL
+
/*
* Sysctl parameters (same as module parameters, different interface).
*/
@@ -443,6 +437,8 @@ static ctl_table nlm_sysctl_root[] = {
{ .ctl_name = 0 }
};
+#endif /* CONFIG_SYSCTL */
+
/*
* Module (and sysfs) parameters.
*/
@@ -516,15 +512,21 @@ module_param(nsm_use_hostnames, bool, 0644);
static int __init init_nlm(void)
{
+#ifdef CONFIG_SYSCTL
nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
return nlm_sysctl_table ? 0 : -ENOMEM;
+#else
+ return 0;
+#endif
}
static void __exit exit_nlm(void)
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts();
+#ifdef CONFIG_SYSCTL
unregister_sysctl_table(nlm_sysctl_table);
+#endif
}
module_init(init_nlm);
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index fe9bdb4a220c..1f122c1940af 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -29,6 +29,7 @@
#include <linux/sunrpc/svc.h>
#include <linux/lockd/nlm.h>
#include <linux/lockd/lockd.h>
+#include <linux/kthread.h>
#define NLMDBG_FACILITY NLMDBG_SVCLOCK
@@ -226,8 +227,7 @@ failed:
}
/*
- * Delete a block. If the lock was cancelled or the grant callback
- * failed, unlock is set to 1.
+ * Delete a block.
* It is the caller's responsibility to check whether the file
* can be closed hereafter.
*/
@@ -887,7 +887,7 @@ nlmsvc_retry_blocked(void)
unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
struct nlm_block *block;
- while (!list_empty(&nlm_blocked)) {
+ while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
if (block->b_when == NLM_NEVER)
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c
index 068886de4dda..b0ae07008700 100644
--- a/fs/lockd/svcshare.c
+++ b/fs/lockd/svcshare.c
@@ -71,7 +71,8 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct nlm_share *share, **shpp;
struct xdr_netobj *oh = &argp->lock.oh;
- for (shpp = &file->f_shares; (share = *shpp) != 0; shpp = &share->s_next) {
+ for (shpp = &file->f_shares; (share = *shpp) != NULL;
+ shpp = &share->s_next) {
if (share->s_host == host && nlm_cmp_owner(share, oh)) {
*shpp = share->s_next;
kfree(share);
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index df0f41e09885..ac6170c594a3 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_NFS_FS) += nfs.o
nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
- pagelist.o proc.o read.o symlink.o unlink.o \
+ direct.o pagelist.o proc.o read.o symlink.o unlink.o \
write.o namespace.o mount_clnt.o
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
@@ -14,5 +14,4 @@ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o \
nfs4namespace.o
-nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
nfs-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 66648dd92d97..5606ae3d72d3 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -15,6 +15,7 @@
#include <linux/nfs_fs.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/kthread.h>
#include <net/inet_sock.h>
@@ -27,9 +28,7 @@
struct nfs_callback_data {
unsigned int users;
struct svc_serv *serv;
- pid_t pid;
- struct completion started;
- struct completion stopped;
+ struct task_struct *task;
};
static struct nfs_callback_data nfs_callback_info;
@@ -57,48 +56,44 @@ module_param_call(callback_tcpport, param_set_port, param_get_int,
/*
* This is the callback kernel thread.
*/
-static void nfs_callback_svc(struct svc_rqst *rqstp)
+static int
+nfs_callback_svc(void *vrqstp)
{
- int err;
+ int err, preverr = 0;
+ struct svc_rqst *rqstp = vrqstp;
- __module_get(THIS_MODULE);
- lock_kernel();
-
- nfs_callback_info.pid = current->pid;
- daemonize("nfsv4-svc");
- /* Process request with signals blocked, but allow SIGKILL. */
- allow_signal(SIGKILL);
set_freezable();
- complete(&nfs_callback_info.started);
-
- for(;;) {
- if (signalled()) {
- if (nfs_callback_info.users == 0)
- break;
- flush_signals(current);
- }
+ /*
+ * 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()) {
/*
* Listen for a request on the socket
*/
err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
- if (err == -EAGAIN || err == -EINTR)
+ if (err == -EAGAIN || err == -EINTR) {
+ preverr = err;
continue;
+ }
if (err < 0) {
- printk(KERN_WARNING
- "%s: terminating on error %d\n",
- __FUNCTION__, -err);
- break;
+ if (err != preverr) {
+ printk(KERN_WARNING "%s: unexpected error "
+ "from svc_recv (%d)\n", __func__, err);
+ preverr = err;
+ }
+ schedule_timeout_uninterruptible(HZ);
+ continue;
}
+ preverr = err;
svc_process(rqstp);
}
-
- flush_signals(current);
- svc_exit_thread(rqstp);
- nfs_callback_info.pid = 0;
- complete(&nfs_callback_info.stopped);
unlock_kernel();
- module_put_and_exit(0);
+ nfs_callback_info.task = NULL;
+ svc_exit_thread(rqstp);
+ return 0;
}
/*
@@ -107,14 +102,13 @@ static void nfs_callback_svc(struct svc_rqst *rqstp)
int nfs_callback_up(void)
{
struct svc_serv *serv = NULL;
+ struct svc_rqst *rqstp;
int ret = 0;
lock_kernel();
mutex_lock(&nfs_callback_mutex);
- if (nfs_callback_info.users++ || nfs_callback_info.pid != 0)
+ if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
goto out;
- init_completion(&nfs_callback_info.started);
- init_completion(&nfs_callback_info.stopped);
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
ret = -ENOMEM;
if (!serv)
@@ -127,15 +121,28 @@ int nfs_callback_up(void)
nfs_callback_tcpport = ret;
dprintk("Callback port = 0x%x\n", nfs_callback_tcpport);
- ret = svc_create_thread(nfs_callback_svc, serv);
- if (ret < 0)
+ rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
+ if (IS_ERR(rqstp)) {
+ ret = PTR_ERR(rqstp);
goto out_err;
+ }
+
+ svc_sock_update_bufs(serv);
nfs_callback_info.serv = serv;
- wait_for_completion(&nfs_callback_info.started);
+
+ nfs_callback_info.task = kthread_run(nfs_callback_svc, rqstp,
+ "nfsv4-svc");
+ if (IS_ERR(nfs_callback_info.task)) {
+ ret = PTR_ERR(nfs_callback_info.task);
+ nfs_callback_info.serv = NULL;
+ nfs_callback_info.task = NULL;
+ svc_exit_thread(rqstp);
+ goto out_err;
+ }
out:
/*
* svc_create creates the svc_serv with sv_nrthreads == 1, and then
- * svc_create_thread increments that. So we need to call svc_destroy
+ * svc_prepare_thread increments that. So we need to call svc_destroy
* on both success and failure so that the refcount is 1 when the
* thread exits.
*/
@@ -152,19 +159,15 @@ out_err:
}
/*
- * Kill the server process if it is not already up.
+ * Kill the server process if it is not already down.
*/
void nfs_callback_down(void)
{
lock_kernel();
mutex_lock(&nfs_callback_mutex);
nfs_callback_info.users--;
- do {
- if (nfs_callback_info.users != 0 || nfs_callback_info.pid == 0)
- break;
- if (kill_proc(nfs_callback_info.pid, SIGKILL, 1) < 0)
- break;
- } while (wait_for_completion_timeout(&nfs_callback_info.stopped, 5*HZ) == 0);
+ if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL)
+ kthread_stop(nfs_callback_info.task);
mutex_unlock(&nfs_callback_mutex);
unlock_kernel();
}
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index c5c0175898f6..f2f3b284e6dd 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -112,6 +112,7 @@ struct nfs_client_initdata {
static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
{
struct nfs_client *clp;
+ struct rpc_cred *cred;
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
goto error_0;
@@ -150,6 +151,9 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_boot_time = CURRENT_TIME;
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
#endif
+ cred = rpc_lookup_machine_cred();
+ if (!IS_ERR(cred))
+ clp->cl_machine_cred = cred;
return clp;
@@ -170,6 +174,8 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
BUG_ON(!RB_EMPTY_ROOT(&clp->cl_state_owners));
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp);
+
+ rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
#endif
}
@@ -189,6 +195,9 @@ static void nfs_free_client(struct nfs_client *clp)
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);
+
kfree(clp->cl_hostname);
kfree(clp);
@@ -680,10 +689,22 @@ static int nfs_init_server(struct nfs_server *server,
if (error < 0)
goto error;
+ server->port = data->nfs_server.port;
+
error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
if (error < 0)
goto error;
+ /* Preserve the values of mount_server-related mount options */
+ if (data->mount_server.addrlen) {
+ memcpy(&server->mountd_address, &data->mount_server.address,
+ data->mount_server.addrlen);
+ server->mountd_addrlen = data->mount_server.addrlen;
+ }
+ server->mountd_version = data->mount_server.version;
+ server->mountd_port = data->mount_server.port;
+ server->mountd_protocol = data->mount_server.protocol;
+
server->namelen = data->namlen;
/* Create a client RPC handle for the NFSv3 ACL management interface */
nfs_init_server_aclclient(server);
@@ -1062,6 +1083,8 @@ static int nfs4_init_server(struct nfs_server *server,
server->acdirmin = data->acdirmin * HZ;
server->acdirmax = data->acdirmax * HZ;
+ server->port = data->nfs_server.port;
+
error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
error:
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index d9e30ac2798d..f288b3ecab4a 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1967,7 +1967,7 @@ force_lookup:
if (!NFS_PROTO(inode)->access)
goto out_notsup;
- cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+ cred = rpc_lookup_cred();
if (!IS_ERR(cred)) {
res = nfs_do_access(inode, cred, mask);
put_rpccred(cred);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 16844f98f50e..4757a2b326a1 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -229,14 +229,20 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
{
struct nfs_read_data *data = calldata;
- struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
- if (nfs_readpage_result(task, data) != 0)
- return;
+ nfs_readpage_result(task, data);
+}
+
+static void nfs_direct_read_release(void *calldata)
+{
+
+ struct nfs_read_data *data = calldata;
+ struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+ int status = data->task.tk_status;
spin_lock(&dreq->lock);
- if (unlikely(task->tk_status < 0)) {
- dreq->error = task->tk_status;
+ if (unlikely(status < 0)) {
+ dreq->error = status;
spin_unlock(&dreq->lock);
} else {
dreq->count += data->res.count;
@@ -249,11 +255,12 @@ static void nfs_direct_read_result(struct rpc_task *task, void *calldata)
if (put_dreq(dreq))
nfs_direct_complete(dreq);
+ nfs_readdata_release(calldata);
}
static const struct rpc_call_ops nfs_read_direct_ops = {
.rpc_call_done = nfs_direct_read_result,
- .rpc_release = nfs_readdata_release,
+ .rpc_release = nfs_direct_read_release,
};
/*
@@ -280,6 +287,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
.rpc_client = NFS_CLIENT(inode),
.rpc_message = &msg,
.callback_ops = &nfs_read_direct_ops,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
unsigned int pgbase;
@@ -323,7 +331,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
data->inode = inode;
data->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode);
- data->args.context = ctx;
+ data->args.context = get_nfs_open_context(ctx);
data->args.offset = pos;
data->args.pgbase = pgbase;
data->args.pages = data->pagevec;
@@ -339,8 +347,9 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
NFS_PROTO(inode)->read_setup(data, &msg);
task = rpc_run_task(&task_setup_data);
- if (!IS_ERR(task))
- rpc_put_task(task);
+ if (IS_ERR(task))
+ break;
+ rpc_put_task(task);
dprintk("NFS: %5u initiated direct read call "
"(req %s/%Ld, %zu bytes @ offset %Lu)\n",
@@ -446,6 +455,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
struct rpc_task_setup task_setup_data = {
.rpc_client = NFS_CLIENT(inode),
.callback_ops = &nfs_write_direct_ops,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
@@ -499,27 +509,34 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
static void nfs_direct_commit_result(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
- struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
/* Call the NFS version-specific code */
- if (NFS_PROTO(data->inode)->commit_done(task, data) != 0)
- return;
- if (unlikely(task->tk_status < 0)) {
+ NFS_PROTO(data->inode)->commit_done(task, data);
+}
+
+static void nfs_direct_commit_release(void *calldata)
+{
+ struct nfs_write_data *data = calldata;
+ struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+ int status = data->task.tk_status;
+
+ if (status < 0) {
dprintk("NFS: %5u commit failed with error %d.\n",
- task->tk_pid, task->tk_status);
+ data->task.tk_pid, status);
dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
} else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) {
- dprintk("NFS: %5u commit verify failed\n", task->tk_pid);
+ dprintk("NFS: %5u commit verify failed\n", data->task.tk_pid);
dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
}
- dprintk("NFS: %5u commit returned %d\n", task->tk_pid, task->tk_status);
+ dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status);
nfs_direct_write_complete(dreq, data->inode);
+ nfs_commitdata_release(calldata);
}
static const struct rpc_call_ops nfs_commit_direct_ops = {
.rpc_call_done = nfs_direct_commit_result,
- .rpc_release = nfs_commit_release,
+ .rpc_release = nfs_direct_commit_release,
};
static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
@@ -537,6 +554,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
.rpc_message = &msg,
.callback_ops = &nfs_commit_direct_ops,
.callback_data = data,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
@@ -546,6 +564,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
data->args.fh = NFS_FH(data->inode);
data->args.offset = 0;
data->args.count = 0;
+ data->args.context = get_nfs_open_context(dreq->ctx);
data->res.count = 0;
data->res.fattr = &data->fattr;
data->res.verf = &data->verf;
@@ -585,7 +604,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
static void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
{
- dreq->commit_data = nfs_commit_alloc();
+ dreq->commit_data = nfs_commitdata_alloc();
if (dreq->commit_data != NULL)
dreq->commit_data->req = (struct nfs_page *) dreq;
}
@@ -606,11 +625,20 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
- struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
- int status = task->tk_status;
if (nfs_writeback_done(task, data) != 0)
return;
+}
+
+/*
+ * NB: Return the value of the first error return code. Subsequent
+ * errors after the first one are ignored.
+ */
+static void nfs_direct_write_release(void *calldata)
+{
+ struct nfs_write_data *data = calldata;
+ struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+ int status = data->task.tk_status;
spin_lock(&dreq->lock);
@@ -632,23 +660,13 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
break;
case NFS_ODIRECT_DO_COMMIT:
if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) {
- dprintk("NFS: %5u write verify failed\n", task->tk_pid);
+ dprintk("NFS: %5u write verify failed\n", data->task.tk_pid);
dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
}
}
}
out_unlock:
spin_unlock(&dreq->lock);
-}
-
-/*
- * NB: Return the value of the first error return code. Subsequent
- * errors after the first one are ignored.
- */
-static void nfs_direct_write_release(void *calldata)
-{
- struct nfs_write_data *data = calldata;
- struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
if (put_dreq(dreq))
nfs_direct_write_complete(dreq, data->inode);
@@ -682,6 +700,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
.rpc_client = NFS_CLIENT(inode),
.rpc_message = &msg,
.callback_ops = &nfs_write_direct_ops,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
size_t wsize = NFS_SERVER(inode)->wsize;
@@ -728,7 +747,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->inode = inode;
data->cred = msg.rpc_cred;
data->args.fh = NFS_FH(inode);
- data->args.context = ctx;
+ data->args.context = get_nfs_open_context(ctx);
data->args.offset = pos;
data->args.pgbase = pgbase;
data->args.pages = data->pagevec;
@@ -745,8 +764,9 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
NFS_PROTO(inode)->write_setup(data, &msg);
task = rpc_run_task(&task_setup_data);
- if (!IS_ERR(task))
- rpc_put_task(task);
+ if (IS_ERR(task))
+ break;
+ rpc_put_task(task);
dprintk("NFS: %5u initiated direct write call "
"(req %s/%Ld, %zu bytes @ offset %Lu)\n",
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 5d2e9d9a4e28..3536b01164f9 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -238,10 +238,8 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
ssize_t result;
size_t count = iov_length(iov, nr_segs);
-#ifdef CONFIG_NFS_DIRECTIO
if (iocb->ki_filp->f_flags & O_DIRECT)
return nfs_file_direct_read(iocb, iov, nr_segs, pos);
-#endif
dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -387,9 +385,7 @@ const struct address_space_operations nfs_file_aops = {
.write_end = nfs_write_end,
.invalidatepage = nfs_invalidate_page,
.releasepage = nfs_release_page,
-#ifdef CONFIG_NFS_DIRECTIO
.direct_IO = nfs_direct_IO,
-#endif
.launder_page = nfs_launder_page,
};
@@ -447,10 +443,8 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
ssize_t result;
size_t count = iov_length(iov, nr_segs);
-#ifdef CONFIG_NFS_DIRECTIO
if (iocb->ki_filp->f_flags & O_DIRECT)
return nfs_file_direct_write(iocb, iov, nr_segs, pos);
-#endif
dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -576,17 +570,9 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
lock_kernel();
/* Use local locking if mounted with "-onolock" */
- if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) {
+ if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
- /* If we were signalled we still need to ensure that
- * we clean up any state on the server. We therefore
- * record the lock call as having succeeded in order to
- * ensure that locks_remove_posix() cleans it out when
- * the process exits.
- */
- if (status == -EINTR || status == -ERESTARTSYS)
- do_vfs_lock(filp, fl);
- } else
+ else
status = do_vfs_lock(filp, fl);
unlock_kernel();
if (status < 0)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 6f88d7c77ac9..5cb3345eb694 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -523,8 +523,12 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
static void __put_nfs_open_context(struct nfs_open_context *ctx, int wait)
{
- struct inode *inode = ctx->path.dentry->d_inode;
+ struct inode *inode;
+ if (ctx == NULL)
+ return;
+
+ inode = ctx->path.dentry->d_inode;
if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock))
return;
list_del(&ctx->list);
@@ -610,7 +614,7 @@ int nfs_open(struct inode *inode, struct file *filp)
struct nfs_open_context *ctx;
struct rpc_cred *cred;
- cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+ cred = rpc_lookup_cred();
if (IS_ERR(cred))
return PTR_ERR(cred);
ctx = alloc_nfs_open_context(filp->f_path.mnt, filp->f_path.dentry, cred);
@@ -1218,6 +1222,36 @@ static void nfs_destroy_inodecache(void)
kmem_cache_destroy(nfs_inode_cachep);
}
+struct workqueue_struct *nfsiod_workqueue;
+
+/*
+ * start up the nfsiod workqueue
+ */
+static int nfsiod_start(void)
+{
+ struct workqueue_struct *wq;
+ dprintk("RPC: creating workqueue nfsiod\n");
+ wq = create_singlethread_workqueue("nfsiod");
+ if (wq == NULL)
+ return -ENOMEM;
+ nfsiod_workqueue = wq;
+ return 0;
+}
+
+/*
+ * Destroy the nfsiod workqueue
+ */
+static void nfsiod_stop(void)
+{
+ struct workqueue_struct *wq;
+
+ wq = nfsiod_workqueue;
+ if (wq == NULL)
+ return;
+ nfsiod_workqueue = NULL;
+ destroy_workqueue(wq);
+}
+
/*
* Initialize NFS
*/
@@ -1225,6 +1259,10 @@ static int __init init_nfs_fs(void)
{
int err;
+ err = nfsiod_start();
+ if (err)
+ goto out6;
+
err = nfs_fs_proc_init();
if (err)
goto out5;
@@ -1271,6 +1309,8 @@ out3:
out4:
nfs_fs_proc_exit();
out5:
+ nfsiod_stop();
+out6:
return err;
}
@@ -1286,6 +1326,7 @@ static void __exit exit_nfs_fs(void)
#endif
unregister_nfs_fs();
nfs_fs_proc_exit();
+ nfsiod_stop();
}
/* Not quite true; I just maintain it */
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 931992763e68..04ae867dddba 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -46,9 +46,9 @@ struct nfs_parsed_mount_data {
struct sockaddr_storage address;
size_t addrlen;
char *hostname;
- unsigned int version;
+ u32 version;
unsigned short port;
- int protocol;
+ unsigned short protocol;
} mount_server;
struct {
@@ -56,7 +56,8 @@ struct nfs_parsed_mount_data {
size_t addrlen;
char *hostname;
char *export_path;
- int protocol;
+ unsigned short port;
+ unsigned short protocol;
} nfs_server;
struct security_mnt_opts lsm_opts;
@@ -115,13 +116,8 @@ extern void nfs_destroy_readpagecache(void);
extern int __init nfs_init_writepagecache(void);
extern void nfs_destroy_writepagecache(void);
-#ifdef CONFIG_NFS_DIRECTIO
extern int __init nfs_init_directcache(void);
extern void nfs_destroy_directcache(void);
-#else
-#define nfs_init_directcache() (0)
-#define nfs_destroy_directcache() do {} while(0)
-#endif
/* nfs2xdr.c */
extern int nfs_stat_to_errno(int);
@@ -146,6 +142,7 @@ extern struct rpc_procinfo nfs4_procedures[];
extern int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask);
/* inode.c */
+extern struct workqueue_struct *nfsiod_workqueue;
extern struct inode *nfs_alloc_inode(struct super_block *sb);
extern void nfs_destroy_inode(struct inode *);
extern int nfs_write_inode(struct inode *,int);
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 607f6eb9cdb5..af4d0f1e402c 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -20,7 +20,7 @@
static void nfs_expire_automounts(struct work_struct *work);
-LIST_HEAD(nfs_automount_list);
+static LIST_HEAD(nfs_automount_list);
static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
int nfs_mountpoint_expiry_timeout = 500 * HZ;
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 1f7ea675e0c5..28bab67d1519 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -267,7 +267,7 @@ nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
int status;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
p = xdr_decode_fattr(p, res->fattr);
count = ntohl(*p++);
@@ -428,11 +428,11 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
size_t hdrlen;
unsigned int pglen, recvd;
u32 len;
- int status, nr;
+ int status, nr = 0;
__be32 *end, *entry, *kaddr;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
if (iov->iov_len < hdrlen) {
@@ -452,7 +452,12 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
kaddr = p = kmap_atomic(*page, KM_USER0);
end = (__be32 *)((char *)p + pglen);
entry = p;
- for (nr = 0; *p++; nr++) {
+
+ /* Make sure the packet actually has a value_follows and EOF entry */
+ if ((entry + 1) > end)
+ goto short_pkt;
+
+ for (; *p++; nr++) {
if (p + 2 > end)
goto short_pkt;
p++; /* fileid */
@@ -467,18 +472,32 @@ nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
goto short_pkt;
entry = p;
}
- if (!nr && (entry[0] != 0 || entry[1] == 0))
- goto short_pkt;
+
+ /*
+ * Apparently some server sends responses that are a valid size, but
+ * contain no entries, and have value_follows==0 and EOF==0. For
+ * those, just set the EOF marker.
+ */
+ if (!nr && entry[1] == 0) {
+ dprintk("NFS: readdir reply truncated!\n");
+ entry[1] = 1;
+ }
out:
kunmap_atomic(kaddr, KM_USER0);
return nr;
short_pkt:
+ /*
+ * When we get a short packet there are 2 possibilities. We can
+ * return an error, or fix up the response to look like a valid
+ * response and return what we have so far. If there are no
+ * entries and the packet was short, then return -EIO. If there
+ * are valid entries in the response, return them and pretend that
+ * the call was successful, but incomplete. The caller can retry the
+ * readdir starting at the last cookie.
+ */
entry[0] = entry[1] = 0;
- /* truncate listing ? */
- if (!nr) {
- dprintk("NFS: readdir reply truncated!\n");
- entry[1] = 1;
- }
+ if (!nr)
+ nr = -errno_NFSERR_IO;
goto out;
err_unmap:
nr = -errno_NFSERR_IO;
@@ -518,7 +537,7 @@ nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
int status;
if ((status = ntohl(*p++)) != 0)
- status = -nfs_stat_to_errno(status);
+ status = nfs_stat_to_errno(status);
return status;
}
@@ -532,7 +551,7 @@ nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
int status;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
xdr_decode_fattr(p, fattr);
return 0;
}
@@ -547,7 +566,7 @@ nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
int status;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
p = xdr_decode_fhandle(p, res->fh);
xdr_decode_fattr(p, res->fattr);
return 0;
@@ -585,7 +604,7 @@ nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
int status;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
/* Convert length of symlink */
len = ntohl(*p++);
if (len >= rcvbuf->page_len) {
@@ -634,7 +653,7 @@ nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
int status;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
res->tsize = ntohl(*p++);
res->bsize = ntohl(*p++);
@@ -653,39 +672,39 @@ static struct {
int errno;
} nfs_errtbl[] = {
{ NFS_OK, 0 },
- { NFSERR_PERM, EPERM },
- { NFSERR_NOENT, ENOENT },
- { NFSERR_IO, errno_NFSERR_IO },
- { NFSERR_NXIO, ENXIO },
-/* { NFSERR_EAGAIN, EAGAIN }, */
- { NFSERR_ACCES, EACCES },
- { NFSERR_EXIST, EEXIST },
- { NFSERR_XDEV, EXDEV },
- { NFSERR_NODEV, ENODEV },
- { NFSERR_NOTDIR, ENOTDIR },
- { NFSERR_ISDIR, EISDIR },
- { NFSERR_INVAL, EINVAL },
- { NFSERR_FBIG, EFBIG },
- { NFSERR_NOSPC, ENOSPC },
- { NFSERR_ROFS, EROFS },
- { NFSERR_MLINK, EMLINK },
- { NFSERR_NAMETOOLONG, ENAMETOOLONG },
- { NFSERR_NOTEMPTY, ENOTEMPTY },
- { NFSERR_DQUOT, EDQUOT },
- { NFSERR_STALE, ESTALE },
- { NFSERR_REMOTE, EREMOTE },
+ { NFSERR_PERM, -EPERM },
+ { NFSERR_NOENT, -ENOENT },
+ { NFSERR_IO, -errno_NFSERR_IO},
+ { NFSERR_NXIO, -ENXIO },
+/* { NFSERR_EAGAIN, -EAGAIN }, */
+ { NFSERR_ACCES, -EACCES },
+ { NFSERR_EXIST, -EEXIST },
+ { NFSERR_XDEV, -EXDEV },
+ { NFSERR_NODEV, -ENODEV },
+ { NFSERR_NOTDIR, -ENOTDIR },
+ { NFSERR_ISDIR, -EISDIR },
+ { NFSERR_INVAL, -EINVAL },
+ { NFSERR_FBIG, -EFBIG },
+ { NFSERR_NOSPC, -ENOSPC },
+ { NFSERR_ROFS, -EROFS },
+ { NFSERR_MLINK, -EMLINK },
+ { NFSERR_NAMETOOLONG, -ENAMETOOLONG },
+ { NFSERR_NOTEMPTY, -ENOTEMPTY },
+ { NFSERR_DQUOT, -EDQUOT },
+ { NFSERR_STALE, -ESTALE },
+ { NFSERR_REMOTE, -EREMOTE },
#ifdef EWFLUSH
- { NFSERR_WFLUSH, EWFLUSH },
+ { NFSERR_WFLUSH, -EWFLUSH },
#endif
- { NFSERR_BADHANDLE, EBADHANDLE },
- { NFSERR_NOT_SYNC, ENOTSYNC },
- { NFSERR_BAD_COOKIE, EBADCOOKIE },
- { NFSERR_NOTSUPP, ENOTSUPP },
- { NFSERR_TOOSMALL, ETOOSMALL },
- { NFSERR_SERVERFAULT, ESERVERFAULT },
- { NFSERR_BADTYPE, EBADTYPE },
- { NFSERR_JUKEBOX, EJUKEBOX },
- { -1, EIO }
+ { NFSERR_BADHANDLE, -EBADHANDLE },
+ { NFSERR_NOT_SYNC, -ENOTSYNC },
+ { NFSERR_BAD_COOKIE, -EBADCOOKIE },
+ { NFSERR_NOTSUPP, -ENOTSUPP },
+ { NFSERR_TOOSMALL, -ETOOSMALL },
+ { NFSERR_SERVERFAULT, -ESERVERFAULT },
+ { NFSERR_BADTYPE, -EBADTYPE },
+ { NFSERR_JUKEBOX, -EJUKEBOX },
+ { -1, -EIO }
};
/*
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 3917e2fa4e40..11cdddec1432 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -508,14 +508,14 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
struct page **page;
size_t hdrlen;
u32 len, recvd, pglen;
- int status, nr;
+ int status, nr = 0;
__be32 *entry, *end, *kaddr;
status = ntohl(*p++);
/* Decode post_op_attrs */
p = xdr_decode_post_op_attr(p, res->dir_attr);
if (status)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
/* Decode verifier cookie */
if (res->verf) {
res->verf[0] = *p++;
@@ -542,7 +542,12 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
kaddr = p = kmap_atomic(*page, KM_USER0);
end = (__be32 *)((char *)p + pglen);
entry = p;
- for (nr = 0; *p++; nr++) {
+
+ /* Make sure the packet actually has a value_follows and EOF entry */
+ if ((entry + 1) > end)
+ goto short_pkt;
+
+ for (; *p++; nr++) {
if (p + 3 > end)
goto short_pkt;
p += 2; /* inode # */
@@ -581,18 +586,32 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res
goto short_pkt;
entry = p;
}
- if (!nr && (entry[0] != 0 || entry[1] == 0))
- goto short_pkt;
+
+ /*
+ * Apparently some server sends responses that are a valid size, but
+ * contain no entries, and have value_follows==0 and EOF==0. For
+ * those, just set the EOF marker.
+ */
+ if (!nr && entry[1] == 0) {
+ dprintk("NFS: readdir reply truncated!\n");
+ entry[1] = 1;
+ }
out:
kunmap_atomic(kaddr, KM_USER0);
return nr;
short_pkt:
+ /*
+ * When we get a short packet there are 2 possibilities. We can
+ * return an error, or fix up the response to look like a valid
+ * response and return what we have so far. If there are no
+ * entries and the packet was short, then return -EIO. If there
+ * are valid entries in the response, return them and pretend that
+ * the call was successful, but incomplete. The caller can retry the
+ * readdir starting at the last cookie.
+ */
entry[0] = entry[1] = 0;
- /* truncate listing ? */
- if (!nr) {
- dprintk("NFS: readdir reply truncated!\n");
- entry[1] = 1;
- }
+ if (!nr)
+ nr = -errno_NFSERR_IO;
goto out;
err_unmap:
nr = -errno_NFSERR_IO;
@@ -732,7 +751,7 @@ nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
int status;
if ((status = ntohl(*p++)))
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
xdr_decode_fattr(p, fattr);
return 0;
}
@@ -747,7 +766,7 @@ nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
int status;
if ((status = ntohl(*p++)))
- status = -nfs_stat_to_errno(status);
+ status = nfs_stat_to_errno(status);
xdr_decode_wcc_data(p, fattr);
return status;
}
@@ -767,7 +786,7 @@ nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
int status;
if ((status = ntohl(*p++))) {
- status = -nfs_stat_to_errno(status);
+ status = nfs_stat_to_errno(status);
} else {
if (!(p = xdr_decode_fhandle(p, res->fh)))
return -errno_NFSERR_IO;
@@ -787,7 +806,7 @@ nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
p = xdr_decode_post_op_attr(p, res->fattr);
if (status)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
res->access = ntohl(*p++);
return 0;
}
@@ -824,7 +843,7 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
p = xdr_decode_post_op_attr(p, fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
/* Convert length of symlink */
len = ntohl(*p++);
@@ -872,7 +891,7 @@ nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
p = xdr_decode_post_op_attr(p, res->fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
/* Decode reply count and EOF flag. NFSv3 is somewhat redundant
* in that it puts the count both in the res struct and in the
@@ -922,7 +941,7 @@ nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
p = xdr_decode_wcc_data(p, res->fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
res->count = ntohl(*p++);
res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
@@ -953,7 +972,7 @@ nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
res->fattr->valid = 0;
}
} else {
- status = -nfs_stat_to_errno(status);
+ status = nfs_stat_to_errno(status);
}
p = xdr_decode_wcc_data(p, res->dir_attr);
return status;
@@ -968,7 +987,7 @@ nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
int status;
if ((status = ntohl(*p++)) != 0)
- status = -nfs_stat_to_errno(status);
+ status = nfs_stat_to_errno(status);
p = xdr_decode_wcc_data(p, res->fromattr);
p = xdr_decode_wcc_data(p, res->toattr);
return status;
@@ -983,7 +1002,7 @@ nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
int status;
if ((status = ntohl(*p++)) != 0)
- status = -nfs_stat_to_errno(status);
+ status = nfs_stat_to_errno(status);
p = xdr_decode_post_op_attr(p, res->fattr);
p = xdr_decode_wcc_data(p, res->dir_attr);
return status;
@@ -1001,7 +1020,7 @@ nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
p = xdr_decode_post_op_attr(p, res->fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
p = xdr_decode_hyper(p, &res->tbytes);
p = xdr_decode_hyper(p, &res->fbytes);
@@ -1026,7 +1045,7 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
p = xdr_decode_post_op_attr(p, res->fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
res->rtmax = ntohl(*p++);
res->rtpref = ntohl(*p++);
@@ -1054,7 +1073,7 @@ nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
p = xdr_decode_post_op_attr(p, res->fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
res->max_link = ntohl(*p++);
res->max_namelen = ntohl(*p++);
@@ -1073,7 +1092,7 @@ nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
status = ntohl(*p++);
p = xdr_decode_wcc_data(p, res->fattr);
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
res->verf->verifier[0] = *p++;
res->verf->verifier[1] = *p++;
@@ -1095,7 +1114,7 @@ nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
int err, base;
if (status != 0)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
p = xdr_decode_post_op_attr(p, res->fattr);
res->mask = ntohl(*p++);
if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
@@ -1122,7 +1141,7 @@ nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
int status = ntohl(*p++);
if (status)
- return -nfs_stat_to_errno(status);
+ return nfs_stat_to_errno(status);
xdr_decode_post_op_attr(p, fattr);
return 0;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 7ce07862c2fb..dbc09271af02 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -51,6 +51,7 @@
#include "nfs4_fs.h"
#include "delegation.h"
+#include "internal.h"
#include "iostat.h"
#define NFSDBG_FACILITY NFSDBG_PROC
@@ -239,6 +240,8 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
{
p->o_res.f_attr = &p->f_attr;
p->o_res.dir_attr = &p->dir_attr;
+ p->o_res.seqid = p->o_arg.seqid;
+ p->c_res.seqid = p->c_arg.seqid;
p->o_res.server = p->o_arg.server;
nfs_fattr_init(&p->f_attr);
nfs_fattr_init(&p->dir_attr);
@@ -729,7 +732,6 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
renew_lease(data->o_res.server, data->timestamp);
data->rpc_done = 1;
}
- nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid);
}
static void nfs4_open_confirm_release(void *calldata)
@@ -773,6 +775,7 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
.rpc_message = &msg,
.callback_ops = &nfs4_open_confirm_ops,
.callback_data = data,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
int status;
@@ -858,7 +861,6 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
if (!(data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM))
nfs_confirm_seqid(&data->owner->so_seqid, 0);
}
- nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid);
data->rpc_done = 1;
}
@@ -910,6 +912,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
.rpc_message = &msg,
.callback_ops = &nfs4_open_ops,
.callback_data = data,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
int status;
@@ -979,11 +982,8 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
if (IS_ERR(opendata))
return PTR_ERR(opendata);
ret = nfs4_open_recover(opendata, state);
- if (ret == -ESTALE) {
- /* Invalidate the state owner so we don't ever use it again */
- nfs4_drop_state_owner(state->owner);
+ if (ret == -ESTALE)
d_drop(ctx->path.dentry);
- }
nfs4_opendata_put(opendata);
return ret;
}
@@ -1226,7 +1226,6 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
/* hmm. we are done with the inode, and in the process of freeing
* the state_owner. we keep this around to process errors
*/
- nfs_increment_open_seqid(task->tk_status, calldata->arg.seqid);
switch (task->tk_status) {
case 0:
nfs_set_open_stateid(state, &calldata->res.stateid, 0);
@@ -1315,6 +1314,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
.rpc_client = server->client,
.rpc_message = &msg,
.callback_ops = &nfs4_close_ops,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
int status = -ENOMEM;
@@ -1332,6 +1332,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
goto out_free_calldata;
calldata->arg.bitmask = server->attr_bitmask;
calldata->res.fattr = &calldata->fattr;
+ calldata->res.seqid = calldata->arg.seqid;
calldata->res.server = server;
calldata->path.mnt = mntget(path->mnt);
calldata->path.dentry = dget(path->dentry);
@@ -1404,7 +1405,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
BUG_ON(nd->intent.open.flags & O_CREAT);
}
- cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ cred = rpc_lookup_cred();
if (IS_ERR(cred))
return (struct dentry *)cred;
parent = dentry->d_parent;
@@ -1439,7 +1440,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
struct rpc_cred *cred;
struct nfs4_state *state;
- cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ cred = rpc_lookup_cred();
if (IS_ERR(cred))
return PTR_ERR(cred);
state = nfs4_do_open(dir, &path, openflags, NULL, cred);
@@ -1656,7 +1657,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
nfs_fattr_init(fattr);
- cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+ cred = rpc_lookup_cred();
if (IS_ERR(cred))
return PTR_ERR(cred);
@@ -1892,7 +1893,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
struct rpc_cred *cred;
int status = 0;
- cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ cred = rpc_lookup_cred();
if (IS_ERR(cred)) {
status = PTR_ERR(cred);
goto out;
@@ -2761,10 +2762,10 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL);
+ rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
nfs4_schedule_state_recovery(clp);
if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
- rpc_wake_up_task(task);
+ rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
task->tk_status = 0;
return -EAGAIN;
case -NFS4ERR_DELAY:
@@ -2884,7 +2885,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short po
RPC_DISPLAY_ADDR),
rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_PROTO),
- cred->cr_ops->cr_name,
+ clp->cl_rpcclient->cl_auth->au_ops->au_name,
clp->cl_id_uniquifier);
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid),
@@ -3158,6 +3159,7 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
p->arg.fh = NFS_FH(inode);
p->arg.fl = &p->fl;
p->arg.seqid = seqid;
+ p->res.seqid = seqid;
p->arg.stateid = &lsp->ls_stateid;
p->lsp = lsp;
atomic_inc(&lsp->ls_count);
@@ -3183,7 +3185,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
if (RPC_ASSASSINATED(task))
return;
- nfs_increment_lock_seqid(task->tk_status, calldata->arg.seqid);
switch (task->tk_status) {
case 0:
memcpy(calldata->lsp->ls_stateid.data,
@@ -3235,6 +3236,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
.rpc_client = NFS_CLIENT(lsp->ls_state->inode),
.rpc_message = &msg,
.callback_ops = &nfs4_locku_ops,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
@@ -3261,6 +3263,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
struct nfs4_lock_state *lsp;
struct rpc_task *task;
int status = 0;
+ unsigned char fl_flags = request->fl_flags;
status = nfs4_set_lock_state(state, request);
/* Unlock _before_ we do the RPC call */
@@ -3284,6 +3287,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
status = nfs4_wait_for_completion_rpc_task(task);
rpc_put_task(task);
out:
+ request->fl_flags = fl_flags;
return status;
}
@@ -3320,6 +3324,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
p->arg.lock_stateid = &lsp->ls_stateid;
p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
p->arg.lock_owner.id = lsp->ls_id.id;
+ p->res.lock_seqid = p->arg.lock_seqid;
p->lsp = lsp;
atomic_inc(&lsp->ls_count);
p->ctx = get_nfs_open_context(ctx);
@@ -3346,6 +3351,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
return;
data->arg.open_stateid = &state->stateid;
data->arg.new_lock_owner = 1;
+ data->res.open_seqid = data->arg.open_seqid;
} else
data->arg.new_lock_owner = 0;
data->timestamp = jiffies;
@@ -3363,7 +3369,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
if (RPC_ASSASSINATED(task))
goto out;
if (data->arg.new_lock_owner != 0) {
- nfs_increment_open_seqid(data->rpc_status, data->arg.open_seqid);
if (data->rpc_status == 0)
nfs_confirm_seqid(&data->lsp->ls_seqid, 0);
else
@@ -3375,7 +3380,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
data->lsp->ls_flags |= NFS_LOCK_INITIALIZED;
renew_lease(NFS_SERVER(data->ctx->path.dentry->d_inode), data->timestamp);
}
- nfs_increment_lock_seqid(data->rpc_status, data->arg.lock_seqid);
out:
dprintk("%s: done, ret = %d!\n", __FUNCTION__, data->rpc_status);
}
@@ -3419,6 +3423,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
.rpc_client = NFS_CLIENT(state->inode),
.rpc_message = &msg,
.callback_ops = &nfs4_lock_ops,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
int ret;
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index b962397004c1..46eb624e4f16 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -71,6 +71,29 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
return status;
}
+static struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp)
+{
+ struct rpc_cred *cred = NULL;
+
+ spin_lock(&clp->cl_lock);
+ if (clp->cl_machine_cred != NULL)
+ cred = get_rpccred(clp->cl_machine_cred);
+ spin_unlock(&clp->cl_lock);
+ return cred;
+}
+
+static void nfs4_clear_machine_cred(struct nfs_client *clp)
+{
+ struct rpc_cred *cred;
+
+ spin_lock(&clp->cl_lock);
+ cred = clp->cl_machine_cred;
+ clp->cl_machine_cred = NULL;
+ spin_unlock(&clp->cl_lock);
+ if (cred != NULL)
+ put_rpccred(cred);
+}
+
struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
{
struct nfs4_state_owner *sp;
@@ -91,13 +114,18 @@ static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
{
struct nfs4_state_owner *sp;
struct rb_node *pos;
+ struct rpc_cred *cred;
+ cred = nfs4_get_machine_cred(clp);
+ if (cred != NULL)
+ goto out;
pos = rb_first(&clp->cl_state_owners);
if (pos != NULL) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
- return get_rpccred(sp->so_cred);
+ cred = get_rpccred(sp->so_cred);
}
- return NULL;
+out:
+ return cred;
}
static void nfs_alloc_unique_id(struct rb_root *root, struct nfs_unique_id *new,
@@ -292,8 +320,10 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
spin_unlock(&clp->cl_lock);
if (sp == new)
get_rpccred(cred);
- else
+ else {
+ rpc_destroy_wait_queue(&new->so_sequence.wait);
kfree(new);
+ }
return sp;
}
@@ -310,6 +340,7 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp)
return;
nfs4_remove_state_owner(clp, sp);
spin_unlock(&clp->cl_lock);
+ rpc_destroy_wait_queue(&sp->so_sequence.wait);
put_rpccred(cred);
kfree(sp);
}
@@ -529,6 +560,7 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
spin_lock(&clp->cl_lock);
nfs_free_unique_id(&clp->cl_lockowner_id, &lsp->ls_id);
spin_unlock(&clp->cl_lock);
+ rpc_destroy_wait_queue(&lsp->ls_sequence.wait);
kfree(lsp);
}
@@ -731,7 +763,7 @@ int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task)
list_add_tail(&seqid->list, &sequence->list);
if (list_first_entry(&sequence->list, struct nfs_seqid, list) == seqid)
goto unlock;
- rpc_sleep_on(&sequence->wait, task, NULL, NULL);
+ rpc_sleep_on(&sequence->wait, task, NULL);
status = -EAGAIN;
unlock:
spin_unlock(&sequence->lock);
@@ -920,10 +952,10 @@ restart_loop:
if (cred != NULL) {
/* Yes there are: try to renew the old lease */
status = nfs4_proc_renew(clp, cred);
+ put_rpccred(cred);
switch (status) {
case 0:
case -NFS4ERR_CB_PATH_DOWN:
- put_rpccred(cred);
goto out;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_LEASE_MOVED:
@@ -932,14 +964,19 @@ restart_loop:
} else {
/* "reboot" to ensure we clear all state on the server */
clp->cl_boot_time = CURRENT_TIME;
- cred = nfs4_get_setclientid_cred(clp);
}
/* We're going to have to re-establish a clientid */
nfs4_state_mark_reclaim(clp);
status = -ENOENT;
+ cred = nfs4_get_setclientid_cred(clp);
if (cred != NULL) {
status = nfs4_init_client(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);
+ goto restart_loop;
+ }
}
if (status)
goto out_error;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index db1ed9c46ede..5a2d64927b35 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -110,7 +110,7 @@ static int nfs4_stat_to_errno(int);
#define decode_savefh_maxsz (op_decode_hdr_maxsz)
#define encode_restorefh_maxsz (op_encode_hdr_maxsz)
#define decode_restorefh_maxsz (op_decode_hdr_maxsz)
-#define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2)
+#define encode_fsinfo_maxsz (encode_getattr_maxsz)
#define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 11)
#define encode_renew_maxsz (op_encode_hdr_maxsz + 3)
#define decode_renew_maxsz (op_decode_hdr_maxsz)
@@ -1191,8 +1191,8 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
WRITE32(attrs[0] & readdir->bitmask[0]);
WRITE32(attrs[1] & readdir->bitmask[1]);
- dprintk("%s: cookie = %Lu, verifier = 0x%x%x, bitmap = 0x%x%x\n",
- __FUNCTION__,
+ dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
+ __func__,
(unsigned long long)readdir->cookie,
((u32 *)readdir->verifier.data)[0],
((u32 *)readdir->verifier.data)[1],
@@ -2241,7 +2241,7 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
}
READ32(nfserr);
if (nfserr != NFS_OK)
- return -nfs4_stat_to_errno(nfserr);
+ return nfs4_stat_to_errno(nfserr);
return 0;
}
@@ -2291,7 +2291,7 @@ static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint3
bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS;
} else
bitmask[0] = bitmask[1] = 0;
- dprintk("%s: bitmask=0x%x%x\n", __FUNCTION__, bitmask[0], bitmask[1]);
+ dprintk("%s: bitmask=%08x:%08x\n", __func__, bitmask[0], bitmask[1]);
return 0;
}
@@ -3005,6 +3005,8 @@ static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res)
int status;
status = decode_op_hdr(xdr, OP_CLOSE);
+ if (status != -EIO)
+ nfs_increment_open_seqid(status, res->seqid);
if (status)
return status;
READ_BUF(NFS4_STATEID_SIZE);
@@ -3296,11 +3298,17 @@ static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res)
int status;
status = decode_op_hdr(xdr, OP_LOCK);
+ if (status == -EIO)
+ goto out;
if (status == 0) {
READ_BUF(NFS4_STATEID_SIZE);
COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
} else if (status == -NFS4ERR_DENIED)
- return decode_lock_denied(xdr, NULL);
+ status = decode_lock_denied(xdr, NULL);
+ if (res->open_seqid != NULL)
+ nfs_increment_open_seqid(status, res->open_seqid);
+ nfs_increment_lock_seqid(status, res->lock_seqid);
+out:
return status;
}
@@ -3319,6 +3327,8 @@ static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res)
int status;
status = decode_op_hdr(xdr, OP_LOCKU);
+ if (status != -EIO)
+ nfs_increment_lock_seqid(status, res->seqid);
if (status == 0) {
READ_BUF(NFS4_STATEID_SIZE);
COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
@@ -3384,6 +3394,8 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
int status;
status = decode_op_hdr(xdr, OP_OPEN);
+ if (status != -EIO)
+ nfs_increment_open_seqid(status, res->seqid);
if (status)
return status;
READ_BUF(NFS4_STATEID_SIZE);
@@ -3416,6 +3428,8 @@ static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmre
int status;
status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
+ if (status != -EIO)
+ nfs_increment_open_seqid(status, res->seqid);
if (status)
return status;
READ_BUF(NFS4_STATEID_SIZE);
@@ -3429,6 +3443,8 @@ static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *re
int status;
status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE);
+ if (status != -EIO)
+ nfs_increment_open_seqid(status, res->seqid);
if (status)
return status;
READ_BUF(NFS4_STATEID_SIZE);
@@ -3481,7 +3497,7 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
size_t hdrlen;
u32 recvd, pglen = rcvbuf->page_len;
__be32 *end, *entry, *p, *kaddr;
- unsigned int nr;
+ unsigned int nr = 0;
int status;
status = decode_op_hdr(xdr, OP_READDIR);
@@ -3489,8 +3505,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
return status;
READ_BUF(8);
COPYMEM(readdir->verifier.data, 8);
- dprintk("%s: verifier = 0x%x%x\n",
- __FUNCTION__,
+ dprintk("%s: verifier = %08x:%08x\n",
+ __func__,
((u32 *)readdir->verifier.data)[0],
((u32 *)readdir->verifier.data)[1]);
@@ -3505,7 +3521,12 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
kaddr = p = kmap_atomic(page, KM_USER0);
end = p + ((pglen + readdir->pgbase) >> 2);
entry = p;
- for (nr = 0; *p++; nr++) {
+
+ /* Make sure the packet actually has a value_follows and EOF entry */
+ if ((entry + 1) > end)
+ goto short_pkt;
+
+ for (; *p++; nr++) {
u32 len, attrlen, xlen;
if (end - p < 3)
goto short_pkt;
@@ -3532,20 +3553,32 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
p += attrlen; /* attributes */
entry = p;
}
- if (!nr && (entry[0] != 0 || entry[1] == 0))
- goto short_pkt;
+ /*
+ * Apparently some server sends responses that are a valid size, but
+ * contain no entries, and have value_follows==0 and EOF==0. For
+ * those, just set the EOF marker.
+ */
+ if (!nr && entry[1] == 0) {
+ dprintk("NFS: readdir reply truncated!\n");
+ entry[1] = 1;
+ }
out:
kunmap_atomic(kaddr, KM_USER0);
return 0;
short_pkt:
+ /*
+ * When we get a short packet there are 2 possibilities. We can
+ * return an error, or fix up the response to look like a valid
+ * response and return what we have so far. If there are no
+ * entries and the packet was short, then return -EIO. If there
+ * are valid entries in the response, return them and pretend that
+ * the call was successful, but incomplete. The caller can retry the
+ * readdir starting at the last cookie.
+ */
dprintk("%s: short packet at entry %d\n", __FUNCTION__, nr);
entry[0] = entry[1] = 0;
- /* truncate listing ? */
- if (!nr) {
- dprintk("NFS: readdir reply truncated!\n");
- entry[1] = 1;
- }
- goto out;
+ if (nr)
+ goto out;
err_unmap:
kunmap_atomic(kaddr, KM_USER0);
return -errno_NFSERR_IO;
@@ -3727,7 +3760,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
READ_BUF(len);
return -NFSERR_CLID_INUSE;
} else
- return -nfs4_stat_to_errno(nfserr);
+ return nfs4_stat_to_errno(nfserr);
return 0;
}
@@ -4389,7 +4422,7 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf
if (!status)
status = decode_fsinfo(&xdr, fsinfo);
if (!status)
- status = -nfs4_stat_to_errno(hdr.status);
+ status = nfs4_stat_to_errno(hdr.status);
return status;
}
@@ -4479,7 +4512,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p,
if (!status)
status = decode_setclientid(&xdr, clp);
if (!status)
- status = -nfs4_stat_to_errno(hdr.status);
+ status = nfs4_stat_to_errno(hdr.status);
return status;
}
@@ -4501,7 +4534,7 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
if (!status)
status = decode_fsinfo(&xdr, fsinfo);
if (!status)
- status = -nfs4_stat_to_errno(hdr.status);
+ status = nfs4_stat_to_errno(hdr.status);
return status;
}
@@ -4611,42 +4644,42 @@ static struct {
int errno;
} nfs_errtbl[] = {
{ NFS4_OK, 0 },
- { NFS4ERR_PERM, EPERM },
- { NFS4ERR_NOENT, ENOENT },
- { NFS4ERR_IO, errno_NFSERR_IO },
- { NFS4ERR_NXIO, ENXIO },
- { NFS4ERR_ACCESS, EACCES },
- { NFS4ERR_EXIST, EEXIST },
- { NFS4ERR_XDEV, EXDEV },
- { NFS4ERR_NOTDIR, ENOTDIR },
- { NFS4ERR_ISDIR, EISDIR },
- { NFS4ERR_INVAL, EINVAL },
- { NFS4ERR_FBIG, EFBIG },
- { NFS4ERR_NOSPC, ENOSPC },
- { NFS4ERR_ROFS, EROFS },
- { NFS4ERR_MLINK, EMLINK },
- { NFS4ERR_NAMETOOLONG, ENAMETOOLONG },
- { NFS4ERR_NOTEMPTY, ENOTEMPTY },
- { NFS4ERR_DQUOT, EDQUOT },
- { NFS4ERR_STALE, ESTALE },
- { NFS4ERR_BADHANDLE, EBADHANDLE },
- { NFS4ERR_BADOWNER, EINVAL },
- { NFS4ERR_BADNAME, EINVAL },
- { NFS4ERR_BAD_COOKIE, EBADCOOKIE },
- { NFS4ERR_NOTSUPP, ENOTSUPP },
- { NFS4ERR_TOOSMALL, ETOOSMALL },
- { NFS4ERR_SERVERFAULT, ESERVERFAULT },
- { NFS4ERR_BADTYPE, EBADTYPE },
- { NFS4ERR_LOCKED, EAGAIN },
- { NFS4ERR_RESOURCE, EREMOTEIO },
- { NFS4ERR_SYMLINK, ELOOP },
- { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP },
- { NFS4ERR_DEADLOCK, EDEADLK },
- { NFS4ERR_WRONGSEC, EPERM }, /* FIXME: this needs
+ { NFS4ERR_PERM, -EPERM },
+ { NFS4ERR_NOENT, -ENOENT },
+ { NFS4ERR_IO, -errno_NFSERR_IO},
+ { NFS4ERR_NXIO, -ENXIO },
+ { NFS4ERR_ACCESS, -EACCES },
+ { NFS4ERR_EXIST, -EEXIST },
+ { NFS4ERR_XDEV, -EXDEV },
+ { NFS4ERR_NOTDIR, -ENOTDIR },
+ { NFS4ERR_ISDIR, -EISDIR },
+ { NFS4ERR_INVAL, -EINVAL },
+ { NFS4ERR_FBIG, -EFBIG },
+ { NFS4ERR_NOSPC, -ENOSPC },
+ { NFS4ERR_ROFS, -EROFS },
+ { NFS4ERR_MLINK, -EMLINK },
+ { NFS4ERR_NAMETOOLONG, -ENAMETOOLONG },
+ { NFS4ERR_NOTEMPTY, -ENOTEMPTY },
+ { NFS4ERR_DQUOT, -EDQUOT },
+ { NFS4ERR_STALE, -ESTALE },
+ { NFS4ERR_BADHANDLE, -EBADHANDLE },
+ { NFS4ERR_BADOWNER, -EINVAL },
+ { NFS4ERR_BADNAME, -EINVAL },
+ { NFS4ERR_BAD_COOKIE, -EBADCOOKIE },
+ { NFS4ERR_NOTSUPP, -ENOTSUPP },
+ { NFS4ERR_TOOSMALL, -ETOOSMALL },
+ { NFS4ERR_SERVERFAULT, -ESERVERFAULT },
+ { NFS4ERR_BADTYPE, -EBADTYPE },
+ { NFS4ERR_LOCKED, -EAGAIN },
+ { NFS4ERR_RESOURCE, -EREMOTEIO },
+ { NFS4ERR_SYMLINK, -ELOOP },
+ { NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP },
+ { NFS4ERR_DEADLOCK, -EDEADLK },
+ { NFS4ERR_WRONGSEC, -EPERM }, /* FIXME: this needs
* to be handled by a
* middle-layer.
*/
- { -1, EIO }
+ { -1, -EIO }
};
/*
@@ -4663,14 +4696,14 @@ nfs4_stat_to_errno(int stat)
}
if (stat <= 10000 || stat > 10100) {
/* The server is looney tunes. */
- return ESERVERFAULT;
+ return -ESERVERFAULT;
}
/* If we cannot translate the error, the recovery routines should
* handle it.
* Note: remaining NFSv4 error codes have values > 10000, so should
* not conflict with native Linux error codes.
*/
- return stat;
+ return -stat;
}
#define PROC(proc, argtype, restype) \
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 5a70be589bbe..16f57e0af999 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -58,22 +58,19 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
return p;
}
-static void nfs_readdata_rcu_free(struct rcu_head *head)
+static void nfs_readdata_free(struct nfs_read_data *p)
{
- struct nfs_read_data *p = container_of(head, struct nfs_read_data, task.u.tk_rcu);
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_rdata_mempool);
}
-static void nfs_readdata_free(struct nfs_read_data *rdata)
-{
- call_rcu_bh(&rdata->task.u.tk_rcu, nfs_readdata_rcu_free);
-}
-
void nfs_readdata_release(void *data)
{
- nfs_readdata_free(data);
+ struct nfs_read_data *rdata = data;
+
+ put_nfs_open_context(rdata->args.context);
+ nfs_readdata_free(rdata);
}
static
@@ -156,7 +153,7 @@ static void nfs_readpage_release(struct nfs_page *req)
/*
* Set up the NFS read request struct
*/
-static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
+static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
const struct rpc_call_ops *call_ops,
unsigned int count, unsigned int offset)
{
@@ -174,6 +171,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
.rpc_message = &msg,
.callback_ops = call_ops,
.callback_data = data,
+ .workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC | swap_flags,
};
@@ -186,7 +184,7 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
data->args.pgbase = req->wb_pgbase + offset;
data->args.pages = data->pagevec;
data->args.count = count;
- data->args.context = req->wb_context;
+ data->args.context = get_nfs_open_context(req->wb_context);
data->res.fattr = &data->fattr;
data->res.count = count;
@@ -204,8 +202,10 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
(unsigned long long)data->args.offset);
task = rpc_run_task(&task_setup_data);
- if (!IS_ERR(task))
- rpc_put_task(task);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ rpc_put_task(task);
+ return 0;
}
static void
@@ -242,6 +242,7 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne
size_t rsize = NFS_SERVER(inode)->rsize, nbytes;
unsigned int offset;
int requests = 0;
+ int ret = 0;
LIST_HEAD(list);
nfs_list_remove_request(req);
@@ -253,7 +254,6 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne
data = nfs_readdata_alloc(1);
if (!data)
goto out_bad;
- INIT_LIST_HEAD(&data->pages);
list_add(&data->pages, &list);
requests++;
nbytes -= len;
@@ -264,6 +264,8 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne
offset = 0;
nbytes = count;
do {
+ int ret2;
+
data = list_entry(list.next, struct nfs_read_data, pages);
list_del_init(&data->pages);
@@ -271,13 +273,15 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne
if (nbytes < rsize)
rsize = nbytes;
- nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
+ ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
rsize, offset);
+ if (ret == 0)
+ ret = ret2;
offset += rsize;
nbytes -= rsize;
} while (nbytes != 0);
- return 0;
+ return ret;
out_bad:
while (!list_empty(&list)) {
@@ -295,12 +299,12 @@ static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned
struct nfs_page *req;
struct page **pages;
struct nfs_read_data *data;
+ int ret = -ENOMEM;
data = nfs_readdata_alloc(npages);
if (!data)
goto out_bad;
- INIT_LIST_HEAD(&data->pages);
pages = data->pagevec;
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
@@ -311,11 +315,10 @@ static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned
}
req = nfs_list_entry(data->pages.next);
- nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0);
- return 0;
+ return nfs_read_rpcsetup(req, data, &nfs_read_full_ops, count, 0);
out_bad:
nfs_async_read_error(head);
- return -ENOMEM;
+ return ret;
}
/*
@@ -342,26 +345,25 @@ int nfs_readpage_result(struct rpc_task *task, struct nfs_read_data *data)
return 0;
}
-static int nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data)
+static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data)
{
struct nfs_readargs *argp = &data->args;
struct nfs_readres *resp = &data->res;
if (resp->eof || resp->count == argp->count)
- return 0;
+ return;
/* 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 0;
+ return;
/* 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);
- return -EAGAIN;
}
/*
@@ -370,29 +372,37 @@ static int nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data)
static void nfs_readpage_result_partial(struct rpc_task *task, void *calldata)
{
struct nfs_read_data *data = calldata;
- struct nfs_page *req = data->req;
- struct page *page = req->wb_page;
if (nfs_readpage_result(task, data) != 0)
return;
+ if (task->tk_status < 0)
+ return;
- if (likely(task->tk_status >= 0)) {
- nfs_readpage_truncate_uninitialised_page(data);
- if (nfs_readpage_retry(task, data) != 0)
- return;
- }
- if (unlikely(task->tk_status < 0))
+ nfs_readpage_truncate_uninitialised_page(data);
+ nfs_readpage_retry(task, data);
+}
+
+static void nfs_readpage_release_partial(void *calldata)
+{
+ struct nfs_read_data *data = calldata;
+ struct nfs_page *req = data->req;
+ struct page *page = req->wb_page;
+ int status = data->task.tk_status;
+
+ if (status < 0)
SetPageError(page);
+
if (atomic_dec_and_test(&req->wb_complete)) {
if (!PageError(page))
SetPageUptodate(page);
nfs_readpage_release(req);
}
+ nfs_readdata_release(calldata);
}
static const struct rpc_call_ops nfs_read_partial_ops = {
.rpc_call_done = nfs_readpage_result_partial,
- .rpc_release = nfs_readdata_release,
+ .rpc_release = nfs_readpage_release_partial,
};
static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data)
@@ -427,29 +437,35 @@ static void nfs_readpage_result_full(struct rpc_task *task, void *calldata)
if (nfs_readpage_result(task, data) != 0)
return;
+ if (task->tk_status < 0)
+ return;
/*
* Note: nfs_readpage_retry may change the values of
* data->args. In the multi-page case, we therefore need
* to ensure that we call nfs_readpage_set_pages_uptodate()
* first.
*/
- if (likely(task->tk_status >= 0)) {
- nfs_readpage_truncate_uninitialised_page(data);
- nfs_readpage_set_pages_uptodate(data);
- if (nfs_readpage_retry(task, data) != 0)
- return;
- }
+ nfs_readpage_truncate_uninitialised_page(data);
+ nfs_readpage_set_pages_uptodate(data);
+ nfs_readpage_retry(task, data);
+}
+
+static void nfs_readpage_release_full(void *calldata)
+{
+ struct nfs_read_data *data = calldata;
+
while (!list_empty(&data->pages)) {
struct nfs_page *req = nfs_list_entry(data->pages.next);
nfs_list_remove_request(req);
nfs_readpage_release(req);
}
+ nfs_readdata_release(calldata);
}
static const struct rpc_call_ops nfs_read_full_ops = {
.rpc_call_done = nfs_readpage_result_full,
- .rpc_release = nfs_readdata_release,
+ .rpc_release = nfs_readpage_release_full,
};
/*
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index f9219024f31a..20a1cb1810fe 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -441,10 +441,52 @@ static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour)
return sec_flavours[i].str;
}
+static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss,
+ int showdefaults)
+{
+ struct sockaddr *sap = (struct sockaddr *)&nfss->mountd_address;
+
+ switch (sap->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+ seq_printf(m, ",mountaddr=" NIPQUAD_FMT,
+ NIPQUAD(sin->sin_addr.s_addr));
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+ seq_printf(m, ",mountaddr=" NIP6_FMT,
+ NIP6(sin6->sin6_addr));
+ break;
+ }
+ default:
+ if (showdefaults)
+ seq_printf(m, ",mountaddr=unspecified");
+ }
+
+ if (nfss->mountd_version || showdefaults)
+ seq_printf(m, ",mountvers=%u", nfss->mountd_version);
+ if (nfss->mountd_port || showdefaults)
+ seq_printf(m, ",mountport=%u", nfss->mountd_port);
+
+ switch (nfss->mountd_protocol) {
+ case IPPROTO_UDP:
+ seq_printf(m, ",mountproto=udp");
+ break;
+ case IPPROTO_TCP:
+ seq_printf(m, ",mountproto=tcp");
+ break;
+ default:
+ if (showdefaults)
+ seq_printf(m, ",mountproto=auto");
+ }
+}
+
/*
* Describe the mount options in force on this server representation
*/
-static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults)
+static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
+ int showdefaults)
{
static const struct proc_nfs_info {
int flag;
@@ -452,6 +494,8 @@ 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", "" },
{ NFS_MOUNT_NONLM, ",nolock", "" },
@@ -462,18 +506,22 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
};
const struct proc_nfs_info *nfs_infop;
struct nfs_client *clp = nfss->nfs_client;
-
- seq_printf(m, ",vers=%d", clp->rpc_ops->version);
- seq_printf(m, ",rsize=%d", nfss->rsize);
- seq_printf(m, ",wsize=%d", nfss->wsize);
+ u32 version = clp->rpc_ops->version;
+
+ seq_printf(m, ",vers=%u", version);
+ seq_printf(m, ",rsize=%u", nfss->rsize);
+ seq_printf(m, ",wsize=%u", nfss->wsize);
+ if (nfss->bsize != 0)
+ seq_printf(m, ",bsize=%u", nfss->bsize);
+ seq_printf(m, ",namlen=%u", nfss->namelen);
if (nfss->acregmin != 3*HZ || showdefaults)
- seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ);
+ seq_printf(m, ",acregmin=%u", nfss->acregmin/HZ);
if (nfss->acregmax != 60*HZ || showdefaults)
- seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ);
+ seq_printf(m, ",acregmax=%u", nfss->acregmax/HZ);
if (nfss->acdirmin != 30*HZ || showdefaults)
- seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ);
+ seq_printf(m, ",acdirmin=%u", nfss->acdirmin/HZ);
if (nfss->acdirmax != 60*HZ || showdefaults)
- seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ);
+ seq_printf(m, ",acdirmax=%u", nfss->acdirmax/HZ);
for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
if (nfss->flags & nfs_infop->flag)
seq_puts(m, nfs_infop->str);
@@ -482,9 +530,24 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
}
seq_printf(m, ",proto=%s",
rpc_peeraddr2str(nfss->client, RPC_DISPLAY_PROTO));
+ if (version == 4) {
+ if (nfss->port != NFS_PORT)
+ seq_printf(m, ",port=%u", nfss->port);
+ } else
+ if (nfss->port)
+ seq_printf(m, ",port=%u", nfss->port);
+
seq_printf(m, ",timeo=%lu", 10U * nfss->client->cl_timeout->to_initval / HZ);
seq_printf(m, ",retrans=%u", nfss->client->cl_timeout->to_retries);
seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor));
+
+ if (version != 4)
+ nfs_show_mountd_options(m, nfss, showdefaults);
+
+#ifdef CONFIG_NFS_V4
+ if (clp->rpc_ops->version == 4)
+ seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr);
+#endif
}
/*
@@ -529,10 +592,10 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
seq_printf(m, "\n\tcaps:\t");
seq_printf(m, "caps=0x%x", nfss->caps);
- seq_printf(m, ",wtmult=%d", nfss->wtmult);
- seq_printf(m, ",dtsize=%d", nfss->dtsize);
- seq_printf(m, ",bsize=%d", nfss->bsize);
- seq_printf(m, ",namelen=%d", nfss->namelen);
+ seq_printf(m, ",wtmult=%u", nfss->wtmult);
+ seq_printf(m, ",dtsize=%u", nfss->dtsize);
+ seq_printf(m, ",bsize=%u", nfss->bsize);
+ seq_printf(m, ",namlen=%u", nfss->namelen);
#ifdef CONFIG_NFS_V4
if (nfss->nfs_client->rpc_ops->version == 4) {
@@ -546,9 +609,9 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
/*
* Display security flavor in effect for this mount
*/
- seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor);
+ seq_printf(m, "\n\tsec:\tflavor=%u", auth->au_ops->au_flavor);
if (auth->au_flavor)
- seq_printf(m, ",pseudoflavor=%d", auth->au_flavor);
+ seq_printf(m, ",pseudoflavor=%u", auth->au_flavor);
/*
* Display superblock I/O counters
@@ -683,7 +746,6 @@ static int nfs_parse_mount_options(char *raw,
struct nfs_parsed_mount_data *mnt)
{
char *p, *string, *secdata;
- unsigned short port = 0;
int rc;
if (!raw) {
@@ -798,7 +860,7 @@ static int nfs_parse_mount_options(char *raw,
return 0;
if (option < 0 || option > 65535)
return 0;
- port = option;
+ mnt->nfs_server.port = option;
break;
case Opt_rsize:
if (match_int(args, &mnt->rsize))
@@ -1048,7 +1110,8 @@ static int nfs_parse_mount_options(char *raw,
}
}
- nfs_set_port((struct sockaddr *)&mnt->nfs_server.address, port);
+ nfs_set_port((struct sockaddr *)&mnt->nfs_server.address,
+ mnt->nfs_server.port);
return 1;
@@ -1169,7 +1232,9 @@ static int nfs_validate_mount_data(void *options,
args->acregmax = 60;
args->acdirmin = 30;
args->acdirmax = 60;
+ args->mount_server.port = 0; /* autobind unless user sets port */
args->mount_server.protocol = XPRT_TRANSPORT_UDP;
+ args->nfs_server.port = 0; /* autobind unless user sets port */
args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
switch (data->version) {
@@ -1208,7 +1273,6 @@ static int nfs_validate_mount_data(void *options,
args->flags = data->flags;
args->rsize = data->rsize;
args->wsize = data->wsize;
- args->flags = data->flags;
args->timeo = data->timeo;
args->retrans = data->retrans;
args->acregmin = data->acregmin;
@@ -1230,6 +1294,8 @@ static int nfs_validate_mount_data(void *options,
args->namlen = data->namlen;
args->bsize = data->bsize;
args->auth_flavors[0] = data->pseudoflavor;
+ if (!args->nfs_server.hostname)
+ goto out_nomem;
/*
* The legacy version 6 binary mount data from userspace has a
@@ -1276,6 +1342,8 @@ static int nfs_validate_mount_data(void *options,
len = c - dev_name;
/* N.B. caller will free nfs_server.hostname in all cases */
args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL);
+ if (!args->nfs_server.hostname)
+ goto out_nomem;
c++;
if (strlen(c) > NFS_MAXPATHLEN)
@@ -1319,6 +1387,10 @@ out_v3_not_compiled:
return -EPROTONOSUPPORT;
#endif /* !CONFIG_NFS_V3 */
+out_nomem:
+ dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n");
+ return -ENOMEM;
+
out_no_address:
dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n");
return -EINVAL;
@@ -1706,28 +1778,6 @@ static void nfs4_fill_super(struct super_block *sb)
}
/*
- * If the user didn't specify a port, set the port number to
- * the NFS version 4 default port.
- */
-static void nfs4_default_port(struct sockaddr *sap)
-{
- switch (sap->sa_family) {
- case AF_INET: {
- struct sockaddr_in *ap = (struct sockaddr_in *)sap;
- if (ap->sin_port == 0)
- ap->sin_port = htons(NFS_PORT);
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
- if (ap->sin6_port == 0)
- ap->sin6_port = htons(NFS_PORT);
- break;
- }
- }
-}
-
-/*
* Validate NFSv4 mount options
*/
static int nfs4_validate_mount_data(void *options,
@@ -1751,6 +1801,7 @@ static int nfs4_validate_mount_data(void *options,
args->acregmax = 60;
args->acdirmin = 30;
args->acdirmax = 60;
+ args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */
args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
switch (data->version) {
@@ -1767,9 +1818,6 @@ static int nfs4_validate_mount_data(void *options,
&args->nfs_server.address))
goto out_no_address;
- nfs4_default_port((struct sockaddr *)
- &args->nfs_server.address);
-
switch (data->auth_flavourlen) {
case 0:
args->auth_flavors[0] = RPC_AUTH_UNIX;
@@ -1827,9 +1875,6 @@ static int nfs4_validate_mount_data(void *options,
&args->nfs_server.address))
return -EINVAL;
- nfs4_default_port((struct sockaddr *)
- &args->nfs_server.address);
-
switch (args->auth_flavor_len) {
case 0:
args->auth_flavors[0] = RPC_AUTH_UNIX;
@@ -1852,12 +1897,16 @@ static int nfs4_validate_mount_data(void *options,
return -ENAMETOOLONG;
/* N.B. caller will free nfs_server.hostname in all cases */
args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL);
+ if (!args->nfs_server.hostname)
+ goto out_nomem;
c++; /* step over the ':' */
len = strlen(c);
if (len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
args->nfs_server.export_path = kstrndup(c, len, GFP_KERNEL);
+ if (!args->nfs_server.export_path)
+ goto out_nomem;
dprintk("NFS: MNTPATH: '%s'\n", args->nfs_server.export_path);
@@ -1879,6 +1928,10 @@ out_inval_auth:
data->auth_flavourlen);
return -EINVAL;
+out_nomem:
+ dfprintk(MOUNT, "NFS4: not enough memory to handle mount options\n");
+ return -ENOMEM;
+
out_no_address:
dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
return -EINVAL;
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 83e865a16ad1..412738dbfbc7 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -10,7 +10,6 @@
* nfs symlink handling code
*/
-#define NFS_NEED_XDR_TYPES
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/sunrpc/clnt.h>
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
index 757415363422..3adf8b266461 100644
--- a/fs/nfs/unlink.c
+++ b/fs/nfs/unlink.c
@@ -234,7 +234,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
if (data == NULL)
goto out;
- data->cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ data->cred = rpc_lookup_cred();
if (IS_ERR(data->cred)) {
status = PTR_ERR(data->cred);
goto out_free;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index bed63416a55b..1ade11d1ba07 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -48,7 +48,7 @@ static struct kmem_cache *nfs_wdata_cachep;
static mempool_t *nfs_wdata_mempool;
static mempool_t *nfs_commit_mempool;
-struct nfs_write_data *nfs_commit_alloc(void)
+struct nfs_write_data *nfs_commitdata_alloc(void)
{
struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
@@ -59,19 +59,13 @@ struct nfs_write_data *nfs_commit_alloc(void)
return p;
}
-static void nfs_commit_rcu_free(struct rcu_head *head)
+void nfs_commit_free(struct nfs_write_data *p)
{
- struct nfs_write_data *p = container_of(head, struct nfs_write_data, task.u.tk_rcu);
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_commit_mempool);
}
-void nfs_commit_free(struct nfs_write_data *wdata)
-{
- call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free);
-}
-
struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
{
struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
@@ -93,21 +87,18 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
return p;
}
-static void nfs_writedata_rcu_free(struct rcu_head *head)
+static void nfs_writedata_free(struct nfs_write_data *p)
{
- struct nfs_write_data *p = container_of(head, struct nfs_write_data, task.u.tk_rcu);
if (p && (p->pagevec != &p->page_array[0]))
kfree(p->pagevec);
mempool_free(p, nfs_wdata_mempool);
}
-static void nfs_writedata_free(struct nfs_write_data *wdata)
+void nfs_writedata_release(void *data)
{
- call_rcu_bh(&wdata->task.u.tk_rcu, nfs_writedata_rcu_free);
-}
+ struct nfs_write_data *wdata = data;
-void nfs_writedata_release(void *wdata)
-{
+ put_nfs_open_context(wdata->args.context);
nfs_writedata_free(wdata);
}
@@ -291,8 +282,6 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
spin_unlock(&inode->i_lock);
if (!nfs_pageio_add_request(pgio, req)) {
nfs_redirty_request(req);
- nfs_end_page_writeback(page);
- nfs_clear_page_tag_locked(req);
return pgio->pg_error;
}
return 0;
@@ -366,15 +355,13 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
/*
* Insert a write request into an inode
*/
-static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
+static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
{
struct nfs_inode *nfsi = NFS_I(inode);
int error;
error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req);
- BUG_ON(error == -EEXIST);
- if (error)
- return error;
+ BUG_ON(error);
if (!nfsi->npages) {
igrab(inode);
if (nfs_have_delegation(inode, FMODE_WRITE))
@@ -384,8 +371,8 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
set_page_private(req->wb_page, (unsigned long)req);
nfsi->npages++;
kref_get(&req->wb_kref);
- radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
- return 0;
+ radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
+ NFS_PAGE_TAG_LOCKED);
}
/*
@@ -413,7 +400,7 @@ static void nfs_inode_remove_request(struct nfs_page *req)
}
static void
-nfs_redirty_request(struct nfs_page *req)
+nfs_mark_request_dirty(struct nfs_page *req)
{
__set_page_dirty_nobuffers(req->wb_page);
}
@@ -467,7 +454,7 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
return 1;
}
if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) {
- nfs_redirty_request(req);
+ nfs_mark_request_dirty(req);
return 1;
}
return 0;
@@ -597,6 +584,13 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
/* Loop over all inode entries and see if we find
* A request for the page we wish to update
*/
+ if (new) {
+ if (radix_tree_preload(GFP_NOFS)) {
+ nfs_release_request(new);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
spin_lock(&inode->i_lock);
req = nfs_page_find_request_locked(page);
if (req) {
@@ -607,28 +601,27 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0) {
- if (new)
+ if (new) {
+ radix_tree_preload_end();
nfs_release_request(new);
+ }
return ERR_PTR(error);
}
continue;
}
spin_unlock(&inode->i_lock);
- if (new)
+ if (new) {
+ radix_tree_preload_end();
nfs_release_request(new);
+ }
break;
}
if (new) {
- int error;
nfs_lock_request_dontget(new);
- error = nfs_inode_add_request(inode, new);
- if (error) {
- spin_unlock(&inode->i_lock);
- nfs_unlock_request(new);
- return ERR_PTR(error);
- }
+ nfs_inode_add_request(inode, new);
spin_unlock(&inode->i_lock);
+ radix_tree_preload_end();
req = new;
goto zero_page;
}
@@ -785,7 +778,7 @@ static int flush_task_priority(int how)
/*
* Set up the argument/result storage required for the RPC call.
*/
-static void nfs_write_rpcsetup(struct nfs_page *req,
+static int nfs_write_rpcsetup(struct nfs_page *req,
struct nfs_write_data *data,
const struct rpc_call_ops *call_ops,
unsigned int count, unsigned int offset,
@@ -806,6 +799,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
.rpc_message = &msg,
.callback_ops = call_ops,
.callback_data = data,
+ .workqueue = nfsiod_workqueue,
.flags = flags,
.priority = priority,
};
@@ -822,7 +816,7 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
data->args.pgbase = req->wb_pgbase + offset;
data->args.pages = data->pagevec;
data->args.count = count;
- data->args.context = req->wb_context;
+ data->args.context = get_nfs_open_context(req->wb_context);
data->args.stable = NFS_UNSTABLE;
if (how & FLUSH_STABLE) {
data->args.stable = NFS_DATA_SYNC;
@@ -847,8 +841,21 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
(unsigned long long)data->args.offset);
task = rpc_run_task(&task_setup_data);
- if (!IS_ERR(task))
- rpc_put_task(task);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ rpc_put_task(task);
+ return 0;
+}
+
+/* If a nfs_flush_* function fails, it should remove reqs from @head and
+ * call this on each, which will prepare them to be retried on next
+ * writeback using standard nfs.
+ */
+static void nfs_redirty_request(struct nfs_page *req)
+{
+ nfs_mark_request_dirty(req);
+ nfs_end_page_writeback(req->wb_page);
+ nfs_clear_page_tag_locked(req);
}
/*
@@ -863,6 +870,7 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned
size_t wsize = NFS_SERVER(inode)->wsize, nbytes;
unsigned int offset;
int requests = 0;
+ int ret = 0;
LIST_HEAD(list);
nfs_list_remove_request(req);
@@ -884,6 +892,8 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned
offset = 0;
nbytes = count;
do {
+ int ret2;
+
data = list_entry(list.next, struct nfs_write_data, pages);
list_del_init(&data->pages);
@@ -891,13 +901,15 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned
if (nbytes < wsize)
wsize = nbytes;
- nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
+ ret2 = nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
wsize, offset, how);
+ if (ret == 0)
+ ret = ret2;
offset += wsize;
nbytes -= wsize;
} while (nbytes != 0);
- return 0;
+ return ret;
out_bad:
while (!list_empty(&list)) {
@@ -906,8 +918,6 @@ out_bad:
nfs_writedata_release(data);
}
nfs_redirty_request(req);
- nfs_end_page_writeback(req->wb_page);
- nfs_clear_page_tag_locked(req);
return -ENOMEM;
}
@@ -940,16 +950,12 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned i
req = nfs_list_entry(data->pages.next);
/* Set up the argument struct */
- nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how);
-
- return 0;
+ return nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how);
out_bad:
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_redirty_request(req);
- nfs_end_page_writeback(req->wb_page);
- nfs_clear_page_tag_locked(req);
}
return -ENOMEM;
}
@@ -972,7 +978,6 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
struct nfs_page *req = data->req;
- struct page *page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)",
req->wb_context->path.dentry->d_inode->i_sb->s_id,
@@ -980,13 +985,20 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
req->wb_bytes,
(long long)req_offset(req));
- if (nfs_writeback_done(task, data) != 0)
- return;
+ nfs_writeback_done(task, data);
+}
- if (task->tk_status < 0) {
+static void nfs_writeback_release_partial(void *calldata)
+{
+ struct nfs_write_data *data = calldata;
+ struct nfs_page *req = data->req;
+ struct page *page = req->wb_page;
+ int status = data->task.tk_status;
+
+ if (status < 0) {
nfs_set_pageerror(page);
- nfs_context_set_write_error(req->wb_context, task->tk_status);
- dprintk(", error = %d\n", task->tk_status);
+ nfs_context_set_write_error(req->wb_context, status);
+ dprintk(", error = %d\n", status);
goto out;
}
@@ -1011,11 +1023,12 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
out:
if (atomic_dec_and_test(&req->wb_complete))
nfs_writepage_release(req);
+ nfs_writedata_release(calldata);
}
static const struct rpc_call_ops nfs_write_partial_ops = {
.rpc_call_done = nfs_writeback_done_partial,
- .rpc_release = nfs_writedata_release,
+ .rpc_release = nfs_writeback_release_partial,
};
/*
@@ -1028,17 +1041,21 @@ static const struct rpc_call_ops nfs_write_partial_ops = {
static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
- struct nfs_page *req;
- struct page *page;
- if (nfs_writeback_done(task, data) != 0)
- return;
+ nfs_writeback_done(task, data);
+}
+
+static void nfs_writeback_release_full(void *calldata)
+{
+ struct nfs_write_data *data = calldata;
+ int status = data->task.tk_status;
/* Update attributes as result of writeback. */
while (!list_empty(&data->pages)) {
- req = nfs_list_entry(data->pages.next);
+ struct nfs_page *req = nfs_list_entry(data->pages.next);
+ struct page *page = req->wb_page;
+
nfs_list_remove_request(req);
- page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)",
req->wb_context->path.dentry->d_inode->i_sb->s_id,
@@ -1046,10 +1063,10 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
req->wb_bytes,
(long long)req_offset(req));
- if (task->tk_status < 0) {
+ if (status < 0) {
nfs_set_pageerror(page);
- nfs_context_set_write_error(req->wb_context, task->tk_status);
- dprintk(", error = %d\n", task->tk_status);
+ nfs_context_set_write_error(req->wb_context, status);
+ dprintk(", error = %d\n", status);
goto remove_request;
}
@@ -1069,11 +1086,12 @@ remove_request:
next:
nfs_clear_page_tag_locked(req);
}
+ nfs_writedata_release(calldata);
}
static const struct rpc_call_ops nfs_write_full_ops = {
.rpc_call_done = nfs_writeback_done_full,
- .rpc_release = nfs_writedata_release,
+ .rpc_release = nfs_writeback_release_full,
};
@@ -1159,15 +1177,18 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
-void nfs_commit_release(void *wdata)
+void nfs_commitdata_release(void *data)
{
+ struct nfs_write_data *wdata = data;
+
+ put_nfs_open_context(wdata->args.context);
nfs_commit_free(wdata);
}
/*
* Set up the argument/result storage required for the RPC call.
*/
-static void nfs_commit_rpcsetup(struct list_head *head,
+static int nfs_commit_rpcsetup(struct list_head *head,
struct nfs_write_data *data,
int how)
{
@@ -1187,6 +1208,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
.rpc_message = &msg,
.callback_ops = &nfs_commit_ops,
.callback_data = data,
+ .workqueue = nfsiod_workqueue,
.flags = flags,
.priority = priority,
};
@@ -1203,6 +1225,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
/* Note: we always request a commit of the entire inode */
data->args.offset = 0;
data->args.count = 0;
+ data->args.context = get_nfs_open_context(first->wb_context);
data->res.count = 0;
data->res.fattr = &data->fattr;
data->res.verf = &data->verf;
@@ -1214,8 +1237,10 @@ static void nfs_commit_rpcsetup(struct list_head *head,
dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid);
task = rpc_run_task(&task_setup_data);
- if (!IS_ERR(task))
- rpc_put_task(task);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ rpc_put_task(task);
+ return 0;
}
/*
@@ -1227,15 +1252,13 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
struct nfs_write_data *data;
struct nfs_page *req;
- data = nfs_commit_alloc();
+ data = nfs_commitdata_alloc();
if (!data)
goto out_bad;
/* Set up the argument struct */
- nfs_commit_rpcsetup(head, data, how);
-
- return 0;
+ return nfs_commit_rpcsetup(head, data, how);
out_bad:
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
@@ -1255,7 +1278,6 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
static void nfs_commit_done(struct rpc_task *task, void *calldata)
{
struct nfs_write_data *data = calldata;
- struct nfs_page *req;
dprintk("NFS: %5u nfs_commit_done (status %d)\n",
task->tk_pid, task->tk_status);
@@ -1263,6 +1285,13 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
/* Call the NFS version-specific code */
if (NFS_PROTO(data->inode)->commit_done(task, data) != 0)
return;
+}
+
+static void nfs_commit_release(void *calldata)
+{
+ struct nfs_write_data *data = calldata;
+ struct nfs_page *req;
+ int status = data->task.tk_status;
while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next);
@@ -1277,10 +1306,10 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
- if (task->tk_status < 0) {
- nfs_context_set_write_error(req->wb_context, task->tk_status);
+ if (status < 0) {
+ nfs_context_set_write_error(req->wb_context, status);
nfs_inode_remove_request(req);
- dprintk(", error = %d\n", task->tk_status);
+ dprintk(", error = %d\n", status);
goto next;
}
@@ -1297,10 +1326,11 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
}
/* We have a mismatch. Write the page again */
dprintk(" mismatch\n");
- nfs_redirty_request(req);
+ nfs_mark_request_dirty(req);
next:
nfs_clear_page_tag_locked(req);
}
+ nfs_commitdata_release(calldata);
}
static const struct rpc_call_ops nfs_commit_ops = {
@@ -1487,18 +1517,19 @@ static int nfs_wb_page_priority(struct inode *inode, struct page *page,
};
int ret;
- BUG_ON(!PageLocked(page));
- if (clear_page_dirty_for_io(page)) {
- ret = nfs_writepage_locked(page, &wbc);
+ do {
+ if (clear_page_dirty_for_io(page)) {
+ ret = nfs_writepage_locked(page, &wbc);
+ if (ret < 0)
+ goto out_error;
+ } else if (!PagePrivate(page))
+ break;
+ ret = nfs_sync_mapping_wait(page->mapping, &wbc, how);
if (ret < 0)
- goto out;
- }
- if (!PagePrivate(page))
- return 0;
- ret = nfs_sync_mapping_wait(page->mapping, &wbc, how);
- if (ret >= 0)
- return 0;
-out:
+ goto out_error;
+ } while (PagePrivate(page));
+ return 0;
+out_error:
__mark_inode_dirty(inode, I_DIRTY_PAGES);
return ret;
}
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index d13403e33622..294992e9bf69 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -10,6 +10,7 @@
#include <linux/sunrpc/svcauth.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/export.h>
+#include "auth.h"
int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
{
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 8a6f7c924c75..33bfcf09db46 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -35,6 +35,7 @@
#include <linux/lockd/bind.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/gss_api.h>
+#include <net/ipv6.h>
#define NFSDDBG_FACILITY NFSDDBG_EXPORT
@@ -1548,6 +1549,7 @@ exp_addclient(struct nfsctl_client *ncp)
{
struct auth_domain *dom;
int i, err;
+ struct in6_addr addr6;
/* First, consistency check. */
err = -EINVAL;
@@ -1566,9 +1568,10 @@ exp_addclient(struct nfsctl_client *ncp)
goto out_unlock;
/* Insert client into hashtable. */
- for (i = 0; i < ncp->cl_naddr; i++)
- auth_unix_add_addr(ncp->cl_addrlist[i], dom);
-
+ for (i = 0; i < ncp->cl_naddr; i++) {
+ ipv6_addr_set_v4mapped(ncp->cl_addrlist[i].s_addr, &addr6);
+ auth_unix_add_addr(&addr6, dom);
+ }
auth_unix_forget_old(dom);
auth_domain_put(dom);
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index aae2b29ae2c9..562abf3380d0 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -344,6 +344,21 @@ static struct rpc_version * nfs_cb_version[] = {
&nfs_cb_version4,
};
+static struct rpc_program cb_program;
+
+static struct rpc_stat cb_stats = {
+ .program = &cb_program
+};
+
+#define NFS4_CALLBACK 0x40000000
+static struct rpc_program cb_program = {
+ .name = "nfs4_cb",
+ .number = NFS4_CALLBACK,
+ .nrvers = ARRAY_SIZE(nfs_cb_version),
+ .version = nfs_cb_version,
+ .stats = &cb_stats,
+};
+
/* Reference counting, callback cleanup, etc., all look racy as heck.
* And why is cb_set an atomic? */
@@ -358,13 +373,12 @@ static int do_probe_callback(void *data)
.to_maxval = (NFSD_LEASE_TIME/2) * HZ,
.to_exponential = 1,
};
- struct rpc_program * program = &cb->cb_program;
struct rpc_create_args args = {
.protocol = IPPROTO_TCP,
.address = (struct sockaddr *)&addr,
.addrsize = sizeof(addr),
.timeout = &timeparms,
- .program = program,
+ .program = &cb_program,
.version = nfs_cb_version[1]->number,
.authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
.flags = (RPC_CLNT_CREATE_NOPING),
@@ -382,16 +396,8 @@ static int do_probe_callback(void *data)
addr.sin_port = htons(cb->cb_port);
addr.sin_addr.s_addr = htonl(cb->cb_addr);
- /* Initialize rpc_program */
- program->name = "nfs4_cb";
- program->number = cb->cb_prog;
- program->nrvers = ARRAY_SIZE(nfs_cb_version);
- program->version = nfs_cb_version;
- program->stats = &cb->cb_stat;
-
/* Initialize rpc_stat */
- memset(program->stats, 0, sizeof(cb->cb_stat));
- program->stats->program = program;
+ memset(args.program->stats, 0, sizeof(struct rpc_stat));
/* Create RPC client */
client = rpc_create(&args);
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c
index 996bd88b75ba..5b398421b051 100644
--- a/fs/nfsd/nfs4idmap.c
+++ b/fs/nfsd/nfs4idmap.c
@@ -202,7 +202,7 @@ static struct cache_detail idtoname_cache = {
.alloc = ent_alloc,
};
-int
+static int
idtoname_parse(struct cache_detail *cd, char *buf, int buflen)
{
struct ent ent, *res;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 81a75f3081f4..55dfdd71f1b0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1639,6 +1639,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
locks_init_lock(&fl);
fl.fl_lmops = &nfsd_lease_mng_ops;
fl.fl_flags = FL_LEASE;
+ fl.fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
fl.fl_end = OFFSET_MAX;
fl.fl_owner = (fl_owner_t)dp;
fl.fl_file = stp->st_vfs_file;
@@ -1647,8 +1648,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
/* vfs_setlease checks to see if delegation should be handed out.
* the lock_manager callbacks fl_mylease and fl_change are used
*/
- if ((status = vfs_setlease(stp->st_vfs_file,
- flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK, &flp))) {
+ if ((status = vfs_setlease(stp->st_vfs_file, fl.fl_type, &flp))) {
dprintk("NFSD: setlease failed [%d], no delegation\n", status);
unhash_delegation(dp);
flag = NFS4_OPEN_DELEGATE_NONE;
@@ -1763,10 +1763,6 @@ out:
return status;
}
-static struct workqueue_struct *laundry_wq;
-static void laundromat_main(struct work_struct *);
-static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main);
-
__be32
nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
clientid_t *clid)
@@ -1874,7 +1870,11 @@ nfs4_laundromat(void)
return clientid_val;
}
-void
+static struct workqueue_struct *laundry_wq;
+static void laundromat_main(struct work_struct *);
+static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main);
+
+static void
laundromat_main(struct work_struct *not_used)
{
time_t t;
@@ -1975,6 +1975,26 @@ io_during_grace_disallowed(struct inode *inode, int flags)
&& mandatory_lock(inode);
}
+static int check_stateid_generation(stateid_t *in, stateid_t *ref)
+{
+ /* If the client sends us a stateid from the future, it's buggy: */
+ if (in->si_generation > ref->si_generation)
+ return nfserr_bad_stateid;
+ /*
+ * The following, however, can happen. For example, if the
+ * client sends an open and some IO at the same time, the open
+ * may bump si_generation while the IO is still in flight.
+ * Thanks to hard links and renames, the client never knows what
+ * file an open will affect. So it could avoid that situation
+ * only by serializing all opens and IO from the same open
+ * owner. To recover from the old_stateid error, the client
+ * will just have to retry the IO:
+ */
+ if (in->si_generation < ref->si_generation)
+ return nfserr_old_stateid;
+ return nfs_ok;
+}
+
/*
* Checks for stateid operations
*/
@@ -2023,12 +2043,8 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
goto out;
stidp = &stp->st_stateid;
}
- if (stateid->si_generation > stidp->si_generation)
- goto out;
-
- /* OLD STATEID */
- status = nfserr_old_stateid;
- if (stateid->si_generation < stidp->si_generation)
+ status = check_stateid_generation(stateid, stidp);
+ if (status)
goto out;
if (stp) {
if ((status = nfs4_check_openmode(stp,flags)))
@@ -2036,7 +2052,7 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
renew_client(stp->st_stateowner->so_client);
if (filpp)
*filpp = stp->st_vfs_file;
- } else if (dp) {
+ } else {
if ((status = nfs4_check_delegmode(dp, flags)))
goto out;
renew_client(dp->dl_client);
@@ -2065,6 +2081,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
{
struct nfs4_stateid *stp;
struct nfs4_stateowner *sop;
+ __be32 status;
dprintk("NFSD: preprocess_seqid_op: seqid=%d "
"stateid = (%08x/%08x/%08x/%08x)\n", seqid,
@@ -2127,7 +2144,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
}
}
- if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) {
+ if (nfs4_check_fh(current_fh, stp)) {
dprintk("NFSD: preprocess_seqid_op: fh-stateid mismatch!\n");
return nfserr_bad_stateid;
}
@@ -2150,15 +2167,9 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
" confirmed yet!\n");
return nfserr_bad_stateid;
}
- if (stateid->si_generation > stp->st_stateid.si_generation) {
- dprintk("NFSD: preprocess_seqid_op: future stateid?!\n");
- return nfserr_bad_stateid;
- }
-
- if (stateid->si_generation < stp->st_stateid.si_generation) {
- dprintk("NFSD: preprocess_seqid_op: old stateid!\n");
- return nfserr_old_stateid;
- }
+ status = check_stateid_generation(stateid, &stp->st_stateid);
+ if (status)
+ return status;
renew_client(sop->so_client);
return nfs_ok;
@@ -2194,7 +2205,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
oc->oc_seqid, &oc->oc_req_stateid,
- CHECK_FH | CONFIRM | OPEN_STATE,
+ CONFIRM | OPEN_STATE,
&oc->oc_stateowner, &stp, NULL)))
goto out;
@@ -2265,7 +2276,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
od->od_seqid,
&od->od_stateid,
- CHECK_FH | OPEN_STATE,
+ OPEN_STATE,
&od->od_stateowner, &stp, NULL)))
goto out;
@@ -2318,7 +2329,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
close->cl_seqid,
&close->cl_stateid,
- CHECK_FH | OPEN_STATE | CLOSE_STATE,
+ OPEN_STATE | CLOSE_STATE,
&close->cl_stateowner, &stp, NULL)))
goto out;
status = nfs_ok;
@@ -2623,7 +2634,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_seqid_op(&cstate->current_fh,
lock->lk_new_open_seqid,
&lock->lk_new_open_stateid,
- CHECK_FH | OPEN_STATE,
+ OPEN_STATE,
&lock->lk_replay_owner, &open_stp,
lock);
if (status)
@@ -2650,7 +2661,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_seqid_op(&cstate->current_fh,
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
- CHECK_FH | LOCK_STATE,
+ LOCK_STATE,
&lock->lk_replay_owner, &lock_stp, lock);
if (status)
goto out;
@@ -2847,7 +2858,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
locku->lu_seqid,
&locku->lu_stateid,
- CHECK_FH | LOCK_STATE,
+ LOCK_STATE,
&locku->lu_stateowner, &stp, NULL)))
goto out;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 0e6a179eccaf..1ba7ad981935 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1867,6 +1867,15 @@ out_serverfault:
goto out;
}
+static inline int attributes_need_mount(u32 *bmval)
+{
+ if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
+ return 1;
+ if (bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID)
+ return 1;
+ return 0;
+}
+
static __be32
nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
const char *name, int namlen, __be32 *p, int *buflen)
@@ -1888,9 +1897,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
* we will not follow the cross mount and will fill the attribtutes
* directly from the mountpoint dentry.
*/
- if (d_mountpoint(dentry) &&
- (cd->rd_bmval[0] & ~FATTR4_WORD0_RDATTR_ERROR) == 0 &&
- (cd->rd_bmval[1] & ~FATTR4_WORD1_MOUNTED_ON_FILEID) == 0)
+ if (d_mountpoint(dentry) && !attributes_need_mount(cd->rd_bmval))
ignore_crossmnt = 1;
else if (d_mountpoint(dentry)) {
int err;
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 8516137cdbb0..613bcb8171a5 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -37,6 +37,7 @@
#include <linux/nfsd/syscall.h>
#include <asm/uaccess.h>
+#include <net/ipv6.h>
/*
* We have a single directory with 9 nodes in it.
@@ -149,7 +150,6 @@ static const struct file_operations transaction_ops = {
.release = simple_transaction_release,
};
-extern struct seq_operations nfs_exports_op;
static int exports_open(struct inode *inode, struct file *file)
{
return seq_open(file, &nfs_exports_op);
@@ -222,6 +222,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
struct auth_domain *clp;
int err = 0;
struct knfsd_fh *res;
+ struct in6_addr in6;
if (size < sizeof(*data))
return -EINVAL;
@@ -236,7 +237,11 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
res = (struct knfsd_fh*)buf;
exp_readlock();
- if (!(clp = auth_unix_lookup(sin->sin_addr)))
+
+ ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
+
+ clp = auth_unix_lookup(&in6);
+ if (!clp)
err = -EPERM;
else {
err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen);
@@ -257,6 +262,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
int err = 0;
struct knfsd_fh fh;
char *res;
+ struct in6_addr in6;
if (size < sizeof(*data))
return -EINVAL;
@@ -271,7 +277,11 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
res = buf;
sin = (struct sockaddr_in *)&data->gd_addr;
exp_readlock();
- if (!(clp = auth_unix_lookup(sin->sin_addr)))
+
+ ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
+
+ clp = auth_unix_lookup(&in6);
+ if (!clp)
err = -EPERM;
else {
err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE);
@@ -347,8 +357,6 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
return mesg - buf;
}
-extern int nfsd_nrthreads(void);
-
static ssize_t write_threads(struct file *file, char *buf, size_t size)
{
/* if size > 0, look for a number of threads and call nfsd_svc
@@ -371,10 +379,6 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return strlen(buf);
}
-extern int nfsd_nrpools(void);
-extern int nfsd_get_nrthreads(int n, int *);
-extern int nfsd_set_nrthreads(int n, int *);
-
static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
{
/* if size > 0, look for an array of number of threads per node
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 3e6b3f41ee1f..100ae5641162 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -113,6 +113,124 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
}
/*
+ * Use the given filehandle to look up the corresponding export and
+ * dentry. On success, the results are used to set fh_export and
+ * fh_dentry.
+ */
+static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
+{
+ struct knfsd_fh *fh = &fhp->fh_handle;
+ struct fid *fid = NULL, sfid;
+ struct svc_export *exp;
+ struct dentry *dentry;
+ int fileid_type;
+ int data_left = fh->fh_size/4;
+ __be32 error;
+
+ error = nfserr_stale;
+ if (rqstp->rq_vers > 2)
+ error = nfserr_badhandle;
+ if (rqstp->rq_vers == 4 && fh->fh_size == 0)
+ return nfserr_nofilehandle;
+
+ if (fh->fh_version == 1) {
+ int len;
+
+ if (--data_left < 0)
+ return error;
+ if (fh->fh_auth_type != 0)
+ return error;
+ len = key_len(fh->fh_fsid_type) / 4;
+ if (len == 0)
+ return error;
+ if (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
+ /* deprecated, convert to type 3 */
+ len = key_len(FSID_ENCODE_DEV)/4;
+ fh->fh_fsid_type = FSID_ENCODE_DEV;
+ fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1])));
+ fh->fh_fsid[1] = fh->fh_fsid[2];
+ }
+ data_left -= len;
+ if (data_left < 0)
+ return error;
+ exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth);
+ fid = (struct fid *)(fh->fh_auth + len);
+ } else {
+ __u32 tfh[2];
+ dev_t xdev;
+ ino_t xino;
+
+ if (fh->fh_size != NFS_FHSIZE)
+ return error;
+ /* assume old filehandle format */
+ xdev = old_decode_dev(fh->ofh_xdev);
+ xino = u32_to_ino_t(fh->ofh_xino);
+ mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
+ exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
+ }
+
+ error = nfserr_stale;
+ if (PTR_ERR(exp) == -ENOENT)
+ return error;
+
+ if (IS_ERR(exp))
+ return nfserrno(PTR_ERR(exp));
+
+ error = nfsd_setuser_and_check_port(rqstp, exp);
+ if (error)
+ goto out;
+
+ /*
+ * Look up the dentry using the NFS file handle.
+ */
+ error = nfserr_stale;
+ if (rqstp->rq_vers > 2)
+ error = nfserr_badhandle;
+
+ if (fh->fh_version != 1) {
+ sfid.i32.ino = fh->ofh_ino;
+ sfid.i32.gen = fh->ofh_generation;
+ sfid.i32.parent_ino = fh->ofh_dirino;
+ fid = &sfid;
+ data_left = 3;
+ if (fh->ofh_dirino == 0)
+ fileid_type = FILEID_INO32_GEN;
+ else
+ fileid_type = FILEID_INO32_GEN_PARENT;
+ } else
+ fileid_type = fh->fh_fileid_type;
+
+ if (fileid_type == FILEID_ROOT)
+ dentry = dget(exp->ex_path.dentry);
+ else {
+ dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
+ data_left, fileid_type,
+ nfsd_acceptable, exp);
+ }
+ if (dentry == NULL)
+ goto out;
+ if (IS_ERR(dentry)) {
+ if (PTR_ERR(dentry) != -EINVAL)
+ error = nfserrno(PTR_ERR(dentry));
+ goto out;
+ }
+
+ if (S_ISDIR(dentry->d_inode->i_mode) &&
+ (dentry->d_flags & DCACHE_DISCONNECTED)) {
+ printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ }
+
+ fhp->fh_dentry = dentry;
+ fhp->fh_export = exp;
+ nfsd_nr_verified++;
+ return 0;
+out:
+ exp_put(exp);
+ return error;
+}
+
+/*
* Perform sanity checks on the dentry in a client's file handle.
*
* Note that the file handle dentry may need to be freed even after
@@ -124,115 +242,18 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
__be32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
{
- struct knfsd_fh *fh = &fhp->fh_handle;
- struct svc_export *exp = NULL;
+ struct svc_export *exp;
struct dentry *dentry;
- __be32 error = 0;
+ __be32 error;
dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
if (!fhp->fh_dentry) {
- struct fid *fid = NULL, sfid;
- int fileid_type;
- int data_left = fh->fh_size/4;
-
- error = nfserr_stale;
- if (rqstp->rq_vers > 2)
- error = nfserr_badhandle;
- if (rqstp->rq_vers == 4 && fh->fh_size == 0)
- return nfserr_nofilehandle;
-
- if (fh->fh_version == 1) {
- int len;
- if (--data_left<0) goto out;
- switch (fh->fh_auth_type) {
- case 0: break;
- default: goto out;
- }
- len = key_len(fh->fh_fsid_type) / 4;
- if (len == 0) goto out;
- if (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
- /* deprecated, convert to type 3 */
- len = key_len(FSID_ENCODE_DEV)/4;
- fh->fh_fsid_type = FSID_ENCODE_DEV;
- fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1])));
- fh->fh_fsid[1] = fh->fh_fsid[2];
- }
- if ((data_left -= len)<0) goto out;
- exp = rqst_exp_find(rqstp, fh->fh_fsid_type,
- fh->fh_auth);
- fid = (struct fid *)(fh->fh_auth + len);
- } else {
- __u32 tfh[2];
- dev_t xdev;
- ino_t xino;
- if (fh->fh_size != NFS_FHSIZE)
- goto out;
- /* assume old filehandle format */
- xdev = old_decode_dev(fh->ofh_xdev);
- xino = u32_to_ino_t(fh->ofh_xino);
- mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
- exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
- }
-
- error = nfserr_stale;
- if (PTR_ERR(exp) == -ENOENT)
- goto out;
-
- if (IS_ERR(exp)) {
- error = nfserrno(PTR_ERR(exp));
- goto out;
- }
-
- error = nfsd_setuser_and_check_port(rqstp, exp);
+ error = nfsd_set_fh_dentry(rqstp, fhp);
if (error)
goto out;
-
- /*
- * Look up the dentry using the NFS file handle.
- */
- error = nfserr_stale;
- if (rqstp->rq_vers > 2)
- error = nfserr_badhandle;
-
- if (fh->fh_version != 1) {
- sfid.i32.ino = fh->ofh_ino;
- sfid.i32.gen = fh->ofh_generation;
- sfid.i32.parent_ino = fh->ofh_dirino;
- fid = &sfid;
- data_left = 3;
- if (fh->ofh_dirino == 0)
- fileid_type = FILEID_INO32_GEN;
- else
- fileid_type = FILEID_INO32_GEN_PARENT;
- } else
- fileid_type = fh->fh_fileid_type;
-
- if (fileid_type == FILEID_ROOT)
- dentry = dget(exp->ex_path.dentry);
- else {
- dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
- data_left, fileid_type,
- nfsd_acceptable, exp);
- }
- if (dentry == NULL)
- goto out;
- if (IS_ERR(dentry)) {
- if (PTR_ERR(dentry) != -EINVAL)
- error = nfserrno(PTR_ERR(dentry));
- goto out;
- }
-
- if (S_ISDIR(dentry->d_inode->i_mode) &&
- (dentry->d_flags & DCACHE_DISCONNECTED)) {
- printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
- }
-
- fhp->fh_dentry = dentry;
- fhp->fh_export = exp;
- nfsd_nr_verified++;
- cache_get(&exp->h);
+ dentry = fhp->fh_dentry;
+ exp = fhp->fh_export;
} else {
/*
* just rechecking permissions
@@ -242,7 +263,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
dprintk("nfsd: fh_verify - just checking\n");
dentry = fhp->fh_dentry;
exp = fhp->fh_export;
- cache_get(&exp->h);
/*
* Set user creds for this exportpoint; necessary even
* in the "just checking" case because this may be a
@@ -281,8 +301,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
access, ntohl(error));
}
out:
- if (exp && !IS_ERR(exp))
- exp_put(exp);
if (error == nfserr_stale)
nfsdstats.fh_stale++;
return error;
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 9647b0f7bc0c..941041f4b136 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -244,7 +244,6 @@ static int nfsd_init_socks(int port)
if (error < 0)
return error;
-#ifdef CONFIG_NFSD_TCP
error = lockd_up(IPPROTO_TCP);
if (error >= 0) {
error = svc_create_xprt(nfsd_serv, "tcp", port,
@@ -254,7 +253,6 @@ static int nfsd_init_socks(int port)
}
if (error < 0)
return error;
-#endif
return 0;
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 304bf5f643c9..a3a291f771f4 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -264,7 +264,6 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
struct inode *inode;
int accmode = MAY_SATTR;
int ftype = 0;
- int imode;
__be32 err;
int host_err;
int size_change = 0;
@@ -360,25 +359,25 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
DQUOT_INIT(inode);
}
- imode = inode->i_mode;
+ /* sanitize the mode change */
if (iap->ia_valid & ATTR_MODE) {
iap->ia_mode &= S_IALLUGO;
- imode = iap->ia_mode |= (imode & ~S_IALLUGO);
- /* if changing uid/gid revoke setuid/setgid in mode */
- if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) {
- iap->ia_valid |= ATTR_KILL_PRIV;
+ iap->ia_mode |= (inode->i_mode & ~S_IALLUGO);
+ }
+
+ /* Revoke setuid/setgid on chown */
+ if (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) ||
+ ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)) {
+ iap->ia_valid |= ATTR_KILL_PRIV;
+ if (iap->ia_valid & ATTR_MODE) {
+ /* we're setting mode too, just clear the s*id bits */
iap->ia_mode &= ~S_ISUID;
+ if (iap->ia_mode & S_IXGRP)
+ iap->ia_mode &= ~S_ISGID;
+ } else {
+ /* set ATTR_KILL_* bits and let VFS handle it */
+ iap->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID);
}
- if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)
- iap->ia_mode &= ~S_ISGID;
- } else {
- /*
- * Revoke setuid/setgid bit on chown/chgrp
- */
- if ((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid)
- iap->ia_valid |= ATTR_KILL_SUID | ATTR_KILL_PRIV;
- if ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid)
- iap->ia_valid |= ATTR_KILL_SGID;
}
/* Change the attributes. */
@@ -988,7 +987,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
* flushing the data to disk is handled separately below.
*/
- if (file->f_op->fsync == 0) {/* COMMIT3 cannot work */
+ if (!file->f_op->fsync) {/* COMMIT3 cannot work */
stable = 2;
*stablep = 2; /* FILE_SYNC */
}
@@ -1152,7 +1151,7 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
#endif /* CONFIG_NFSD_V3 */
-__be32
+static __be32
nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
struct iattr *iap)
{