Commit 8df1b049 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (82 commits)
  NFSv4: Remove BKL from the nfsv4 state recovery
  SUNRPC: Remove the BKL from the callback functions
  NFS: Remove BKL from the readdir code
  NFS: Remove BKL from the symlink code
  NFS: Remove BKL from the sillydelete operations
  NFS: Remove the BKL from the rename, rmdir and unlink operations
  NFS: Remove BKL from NFS lookup code
  NFS: Remove the BKL from nfs_link()
  NFS: Remove the BKL from the inode creation operations
  NFS: Remove BKL usage from open()
  NFS: Remove BKL usage from the write path
  NFS: Remove the BKL from the permission checking code
  NFS: Remove attribute update related BKL references
  NFS: Remove BKL requirement from attribute updates
  NFS: Protect inode->i_nlink updates using inode->i_lock
  nfs: set correct fl_len in nlmclnt_test()
  SUNRPC: Support registering IPv6 interfaces with local rpcbind daemon
  SUNRPC: Refactor rpcb_register to make rpcbindv4 support easier
  SUNRPC: None of rpcb_create's callers wants a privileged source port
  SUNRPC: Introduce a specific rpcb_create for contacting localhost
  ...
parents a3cf8593 cadc723c
...@@ -1544,10 +1544,6 @@ config UFS_FS ...@@ -1544,10 +1544,6 @@ config UFS_FS
The recently released UFS2 variant (used in FreeBSD 5.x) is The recently released UFS2 variant (used in FreeBSD 5.x) is
READ-ONLY supported. READ-ONLY supported.
If you only intend to mount files from some other Unix over the
network using NFS, you don't need the UFS file system support (but
you need NFS file system support obviously).
Note that this option is generally not needed for floppies, since a Note that this option is generally not needed for floppies, since a
good portable way to transport files and directories between unixes good portable way to transport files and directories between unixes
(and even other operating systems) is given by the tar program ("man (and even other operating systems) is given by the tar program ("man
...@@ -1587,6 +1583,7 @@ menuconfig NETWORK_FILESYSTEMS ...@@ -1587,6 +1583,7 @@ menuconfig NETWORK_FILESYSTEMS
Say Y here to get to see options for network filesystems and Say Y here to get to see options for network filesystems and
filesystem-related networking code, such as NFS daemon and filesystem-related networking code, such as NFS daemon and
RPCSEC security modules. RPCSEC security modules.
This option alone does not add any kernel code. This option alone does not add any kernel code.
If you say N, all options in this submenu will be skipped and If you say N, all options in this submenu will be skipped and
...@@ -1595,76 +1592,92 @@ menuconfig NETWORK_FILESYSTEMS ...@@ -1595,76 +1592,92 @@ menuconfig NETWORK_FILESYSTEMS
if NETWORK_FILESYSTEMS if NETWORK_FILESYSTEMS
config NFS_FS config NFS_FS
tristate "NFS file system support" tristate "NFS client support"
depends on INET depends on INET
select LOCKD select LOCKD
select SUNRPC select SUNRPC
select NFS_ACL_SUPPORT if NFS_V3_ACL select NFS_ACL_SUPPORT if NFS_V3_ACL
help help
If you are connected to some other (usually local) Unix computer Choose Y here if you want to access files residing on other
(using SLIP, PLIP, PPP or Ethernet) and want to mount files residing computers using Sun's Network File System protocol. To compile
on that computer (the NFS server) using the Network File Sharing this file system support as a module, choose M here: the module
protocol, say Y. "Mounting files" means that the client can access will be called nfs.
the files with usual UNIX commands as if they were sitting on the
client's hard disk. For this to work, the server must run the
programs nfsd and mountd (but does not need to have NFS file system
support enabled in its kernel). NFS is explained in the Network
Administrator's Guide, available from
<http://www.tldp.org/docs.html#guide>, on its man page: "man
nfs", and in the NFS-HOWTO.
A superior but less widely used alternative to NFS is provided by
the Coda file system; see "Coda file system support" below.
If you say Y here, you should have said Y to TCP/IP networking also. To mount file systems exported by NFS servers, you also need to
This option would enlarge your kernel by about 27 KB. install the user space mount.nfs command which can be found in
the Linux nfs-utils package, available from http://linux-nfs.org/.
Information about using the mount command is available in the
mount(8) man page. More detail about the Linux NFS client
implementation is available via the nfs(5) man page.
To compile this file system support as a module, choose M here: the Below you can choose which versions of the NFS protocol are
module will be called nfs. available in the kernel to mount NFS servers. Support for NFS
version 2 (RFC 1094) is always available when NFS_FS is selected.
If you are configuring a diskless machine which will mount its root To configure a system which mounts its root file system via NFS
file system over NFS at boot time, say Y here and to "Kernel at boot time, say Y here, select "Kernel level IP
level IP autoconfiguration" above and to "Root file system on NFS" autoconfiguration" in the NETWORK menu, and select "Root file
below. You cannot compile this driver as a module in this case. system on NFS" below. You cannot compile this file system as a
There are two packages designed for booting diskless machines over module in this case.
the net: netboot, available from
<http://ftp1.sourceforge.net/netboot/>, and Etherboot,
available from <http://ftp1.sourceforge.net/etherboot/>.
If you don't know what all this is about, say N. If unsure, say N.
config NFS_V3 config NFS_V3
bool "Provide NFSv3 client support" bool "NFS client support for NFS version 3"
depends on NFS_FS depends on NFS_FS
help help
Say Y here if you want your NFS client to be able to speak version This option enables support for version 3 of the NFS protocol
3 of the NFS protocol. (RFC 1813) in the kernel's NFS client.
If unsure, say Y. If unsure, say Y.
config NFS_V3_ACL config NFS_V3_ACL
bool "Provide client support for the NFSv3 ACL protocol extension" bool "NFS client support for the NFSv3 ACL protocol extension"
depends on NFS_V3 depends on NFS_V3
help help
Implement the NFSv3 ACL protocol extension for manipulating POSIX Some NFS servers support an auxiliary NFSv3 ACL protocol that
Access Control Lists. The server should also be compiled with Sun added to Solaris but never became an official part of the
the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option. 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
ACLs on local files whether this protocol is available or not.
Choose Y here if your NFS server supports the Solaris NFSv3 ACL
protocol extension and you want your NFS client to allow
applications to access and modify ACLs on files on the server.
Most NFS servers don't support the Solaris NFSv3 ACL protocol
extension. You can choose N here or specify the "noacl" mount
option to prevent your NFS client from trying to use the NFSv3
ACL protocol.
If unsure, say N. If unsure, say N.
config NFS_V4 config NFS_V4
bool "Provide NFSv4 client support (EXPERIMENTAL)" bool "NFS client support for NFS version 4 (EXPERIMENTAL)"
depends on NFS_FS && EXPERIMENTAL depends on NFS_FS && EXPERIMENTAL
select RPCSEC_GSS_KRB5 select RPCSEC_GSS_KRB5
help help
Say Y here if you want your NFS client to be able to speak the newer This option enables support for version 4 of the NFS protocol
version 4 of the NFS protocol. (RFC 3530) in the kernel's NFS client.
Note: Requires auxiliary userspace daemons which may be found on To mount NFS servers using NFSv4, you also need to install user
http://www.citi.umich.edu/projects/nfsv4/ space programs which can be found in the Linux nfs-utils package,
available from http://linux-nfs.org/.
If unsure, say N. If unsure, say N.
config ROOT_NFS
bool "Root file system on NFS"
depends on NFS_FS=y && IP_PNP
help
If you want your system to mount its root file system via NFS,
choose Y here. This is common practice for managing systems
without local permanent storage. For details, read
<file:Documentation/filesystems/nfsroot.txt>.
Most people say N here.
config NFSD config NFSD
tristate "NFS server support" tristate "NFS server support"
depends on INET depends on INET
...@@ -1746,20 +1759,6 @@ config NFSD_V4 ...@@ -1746,20 +1759,6 @@ config NFSD_V4
If unsure, say N. If unsure, say N.
config ROOT_NFS
bool "Root file system on NFS"
depends on NFS_FS=y && IP_PNP
help
If you want your Linux box to mount its whole root file system (the
one containing the directory /) from some other computer over the
net via NFS (presumably because your box doesn't have a hard disk),
say Y. Read <file:Documentation/filesystems/nfsroot.txt> for
details. It is likely that in this case, you also want to say Y to
"Kernel level IP autoconfiguration" so that your box can discover
its network address at boot time.
Most people say N here.
config LOCKD config LOCKD
tristate tristate
...@@ -1800,27 +1799,6 @@ config SUNRPC_XPRT_RDMA ...@@ -1800,27 +1799,6 @@ config SUNRPC_XPRT_RDMA
If unsure, say N. If unsure, say N.
config SUNRPC_BIND34
bool "Support for rpcbind versions 3 & 4 (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
default n
help
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).
config RPCSEC_GSS_KRB5 config RPCSEC_GSS_KRB5
tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL depends on SUNRPC && EXPERIMENTAL
......
...@@ -224,7 +224,9 @@ void nlm_release_call(struct nlm_rqst *call) ...@@ -224,7 +224,9 @@ void nlm_release_call(struct nlm_rqst *call)
static void nlmclnt_rpc_release(void *data) static void nlmclnt_rpc_release(void *data)
{ {
lock_kernel();
nlm_release_call(data); nlm_release_call(data);
unlock_kernel();
} }
static int nlm_wait_on_grace(wait_queue_head_t *queue) static int nlm_wait_on_grace(wait_queue_head_t *queue)
...@@ -430,7 +432,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl) ...@@ -430,7 +432,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
* Report the conflicting lock back to the application. * Report the conflicting lock back to the application.
*/ */
fl->fl_start = req->a_res.lock.fl.fl_start; fl->fl_start = req->a_res.lock.fl.fl_start;
fl->fl_end = req->a_res.lock.fl.fl_start; fl->fl_end = req->a_res.lock.fl.fl_end;
fl->fl_type = req->a_res.lock.fl.fl_type; fl->fl_type = req->a_res.lock.fl.fl_type;
fl->fl_pid = 0; fl->fl_pid = 0;
break; break;
...@@ -710,7 +712,9 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) ...@@ -710,7 +712,9 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
die: die:
return; return;
retry_rebind: retry_rebind:
lock_kernel();
nlm_rebind_host(req->a_host); nlm_rebind_host(req->a_host);
unlock_kernel();
retry_unlock: retry_unlock:
rpc_restart_call(task); rpc_restart_call(task);
} }
...@@ -788,7 +792,9 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data) ...@@ -788,7 +792,9 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
/* Don't ever retry more than 3 times */ /* Don't ever retry more than 3 times */
if (req->a_retries++ >= NLMCLNT_MAX_RETRIES) if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
goto die; goto die;
lock_kernel();
nlm_rebind_host(req->a_host); nlm_rebind_host(req->a_host);
unlock_kernel();
rpc_restart_call(task); rpc_restart_call(task);
rpc_delay(task, 30 * HZ); rpc_delay(task, 30 * HZ);
} }
......
...@@ -248,7 +248,9 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data) ...@@ -248,7 +248,9 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
static void nlm4svc_callback_release(void *data) static void nlm4svc_callback_release(void *data)
{ {
lock_kernel();
nlm_release_call(data); nlm_release_call(data);
unlock_kernel();
} }
static const struct rpc_call_ops nlm4svc_callback_ops = { static const struct rpc_call_ops nlm4svc_callback_ops = {
......
...@@ -795,6 +795,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data) ...@@ -795,6 +795,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
dprintk("lockd: GRANT_MSG RPC callback\n"); dprintk("lockd: GRANT_MSG RPC callback\n");
lock_kernel();
/* if the block is not on a list at this point then it has /* if the block is not on a list at this point then it has
* been invalidated. Don't try to requeue it. * been invalidated. Don't try to requeue it.
* *
...@@ -804,7 +805,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data) ...@@ -804,7 +805,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
* for nlm_blocked? * for nlm_blocked?
*/ */
if (list_empty(&block->b_list)) if (list_empty(&block->b_list))
return; goto out;
/* Technically, we should down the file semaphore here. Since we /* Technically, we should down the file semaphore here. Since we
* move the block towards the head of the queue only, no harm * move the block towards the head of the queue only, no harm
...@@ -818,13 +819,17 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data) ...@@ -818,13 +819,17 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
} }
nlmsvc_insert_block(block, timeout); nlmsvc_insert_block(block, timeout);
svc_wake_up(block->b_daemon); svc_wake_up(block->b_daemon);
out:
unlock_kernel();
} }
static void nlmsvc_grant_release(void *data) static void nlmsvc_grant_release(void *data)
{ {
struct nlm_rqst *call = data; struct nlm_rqst *call = data;
lock_kernel();
nlmsvc_release_block(call->a_block); nlmsvc_release_block(call->a_block);
unlock_kernel();
} }
static const struct rpc_call_ops nlmsvc_grant_ops = { static const struct rpc_call_ops nlmsvc_grant_ops = {
......
...@@ -278,7 +278,9 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data) ...@@ -278,7 +278,9 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
static void nlmsvc_callback_release(void *data) static void nlmsvc_callback_release(void *data)
{ {
lock_kernel();
nlm_release_call(data); nlm_release_call(data);
unlock_kernel();
} }
static const struct rpc_call_ops nlmsvc_callback_ops = { static const struct rpc_call_ops nlmsvc_callback_ops = {
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
struct nfs_callback_data { struct nfs_callback_data {
unsigned int users; unsigned int users;
struct svc_serv *serv; struct svc_rqst *rqst;
struct task_struct *task; struct task_struct *task;
}; };
...@@ -91,21 +91,17 @@ nfs_callback_svc(void *vrqstp) ...@@ -91,21 +91,17 @@ nfs_callback_svc(void *vrqstp)
svc_process(rqstp); svc_process(rqstp);
} }
unlock_kernel(); unlock_kernel();
nfs_callback_info.task = NULL;
svc_exit_thread(rqstp);
return 0; return 0;
} }
/* /*
* Bring up the server process if it is not already up. * Bring up the callback thread if it is not already up.
*/ */
int nfs_callback_up(void) int nfs_callback_up(void)
{ {
struct svc_serv *serv = NULL; struct svc_serv *serv = NULL;
struct svc_rqst *rqstp;
int ret = 0; int ret = 0;
lock_kernel();
mutex_lock(&nfs_callback_mutex); mutex_lock(&nfs_callback_mutex);
if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
goto out; goto out;
...@@ -121,22 +117,23 @@ int nfs_callback_up(void) ...@@ -121,22 +117,23 @@ int nfs_callback_up(void)
nfs_callback_tcpport = ret; nfs_callback_tcpport = ret;
dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); dprintk("Callback port = 0x%x\n", nfs_callback_tcpport);
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
if (IS_ERR(rqstp)) { if (IS_ERR(nfs_callback_info.rqst)) {
ret = PTR_ERR(rqstp); ret = PTR_ERR(nfs_callback_info.rqst);
nfs_callback_info.rqst = NULL;
goto out_err; goto out_err;
} }
svc_sock_update_bufs(serv); svc_sock_update_bufs(serv);
nfs_callback_info.serv = serv;
nfs_callback_info.task = kthread_run(nfs_callback_svc, rqstp, nfs_callback_info.task = kthread_run(nfs_callback_svc,
nfs_callback_info.rqst,
"nfsv4-svc"); "nfsv4-svc");
if (IS_ERR(nfs_callback_info.task)) { if (IS_ERR(nfs_callback_info.task)) {
ret = PTR_ERR(nfs_callback_info.task); ret = PTR_ERR(nfs_callback_info.task);
nfs_callback_info.serv = NULL; svc_exit_thread(nfs_callback_info.rqst);
nfs_callback_info.rqst = NULL;
nfs_callback_info.task = NULL; nfs_callback_info.task = NULL;
svc_exit_thread(rqstp);
goto out_err; goto out_err;
} }
out: out:
...@@ -149,7 +146,6 @@ int nfs_callback_up(void) ...@@ -149,7 +146,6 @@ int nfs_callback_up(void)
if (serv) if (serv)
svc_destroy(serv); svc_destroy(serv);
mutex_unlock(&nfs_callback_mutex); mutex_unlock(&nfs_callback_mutex);
unlock_kernel();
return ret; return ret;
out_err: out_err:
dprintk("Couldn't create callback socket or server thread; err = %d\n", dprintk("Couldn't create callback socket or server thread; err = %d\n",
...@@ -159,17 +155,19 @@ int nfs_callback_up(void) ...@@ -159,17 +155,19 @@ int nfs_callback_up(void)
} }
/* /*
* Kill the server process if it is not already down. * Kill the callback thread if it's no longer being used.
*/ */
void nfs_callback_down(void) void nfs_callback_down(void)
{ {
lock_kernel();
mutex_lock(&nfs_callback_mutex); mutex_lock(&nfs_callback_mutex);
nfs_callback_info.users--; nfs_callback_info.users--;
if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
kthread_stop(nfs_callback_info.task); kthread_stop(nfs_callback_info.task);
svc_exit_thread(nfs_callback_info.rqst);
nfs_callback_info.rqst = NULL;
nfs_callback_info.task = NULL;
}
mutex_unlock(&nfs_callback_mutex); mutex_unlock(&nfs_callback_mutex);
unlock_kernel();
} }
static int nfs_callback_authenticate(struct svc_rqst *rqstp) static int nfs_callback_authenticate(struct svc_rqst *rqstp)
......
...@@ -431,14 +431,14 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, ...@@ -431,14 +431,14 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
{ {
to->to_initval = timeo * HZ / 10; to->to_initval = timeo * HZ / 10;
to->to_retries = retrans; to->to_retries = retrans;
if (!to->to_retries)
to->to_retries = 2;
switch (proto) { switch (proto) {
case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_TCP:
case XPRT_TRANSPORT_RDMA: case XPRT_TRANSPORT_RDMA:
if (to->to_retries == 0)
to->to_retries = NFS_DEF_TCP_RETRANS;
if (to->to_initval == 0) if (to->to_initval == 0)
to->to_initval = 60 * HZ; to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10;
if (to->to_initval > NFS_MAX_TCP_TIMEOUT) if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
to->to_initval = NFS_MAX_TCP_TIMEOUT; to->to_initval = NFS_MAX_TCP_TIMEOUT;
to->to_increment = to->to_initval; to->to_increment = to->to_initval;
...@@ -450,14 +450,17 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, ...@@ -450,14 +450,17 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
to->to_exponential = 0; to->to_exponential = 0;
break; break;
case XPRT_TRANSPORT_UDP: case XPRT_TRANSPORT_UDP:
default: if (to->to_retries == 0)
to->to_retries = NFS_DEF_UDP_RETRANS;
if (!to->to_initval) if (!to->to_initval)
to->to_initval = 11 * HZ / 10; to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10;
if (to->to_initval > NFS_MAX_UDP_TIMEOUT) if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
to->to_initval = NFS_MAX_UDP_TIMEOUT; to->to_initval = NFS_MAX_UDP_TIMEOUT;
to->to_maxval = NFS_MAX_UDP_TIMEOUT; to->to_maxval = NFS_MAX_UDP_TIMEOUT;
to->to_exponential = 1; to->to_exponential = 1;
break; break;
default:
BUG();
} }
} }
......
...@@ -133,13 +133,14 @@ nfs_opendir(struct inode *inode, struct file *filp) ...@@ -133,13 +133,14 @@ nfs_opendir(struct inode *inode, struct file *filp)
{ {
int res; int res;
dfprintk(VFS, "NFS: opendir(%s/%ld)\n", dfprintk(FILE, "NFS: open dir(%s/%s)\n",
inode->i_sb->s_id, inode->i_ino); filp->f_path.dentry->d_parent->d_name.name,
filp->f_path.dentry->d_name.name);
nfs_inc_stats(inode, NFSIOS_VFSOPEN);
lock_kernel();
/* Call generic open code in order to cache credentials */ /* Call generic open code in order to cache credentials */
res = nfs_open(inode, filp); res = nfs_open(inode, filp);
unlock_kernel();
return res; return res;
} }
...@@ -528,13 +529,11 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -528,13 +529,11 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
struct nfs_fattr fattr; struct nfs_fattr fattr;
long res; long res;
dfprintk(VFS, "NFS: readdir(%s/%s) starting at cookie %Lu\n", dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
(long long)filp->f_pos); (long long)filp->f_pos);
nfs_inc_stats(inode, NFSIOS_VFSGETDENTS); nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
lock_kernel();
/* /*
* filp->f_pos points to the dirent entry number. * filp->f_pos points to the dirent entry number.
* *desc->dir_cookie has the cookie for the next entry. We have * *desc->dir_cookie has the cookie for the next entry. We have
...@@ -592,10 +591,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -592,10 +591,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
} }
out: out:
nfs_unblock_sillyrename(dentry); nfs_unblock_sillyrename(dentry);
unlock_kernel();
if (res > 0) if (res > 0)
res = 0; res = 0;
dfprintk(VFS, "NFS: readdir(%s/%s) returns %ld\n", dfprintk(FILE, "NFS: readdir(%s/%s) returns %ld\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
res); res);
return res; return res;
...@@ -603,7 +601,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -603,7 +601,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
{ {
mutex_lock(&filp->f_path.dentry->d_inode->i_mutex); struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n",
dentry->d_parent->d_name.name,
dentry->d_name.name,
offset, origin);
mutex_lock(&inode->i_mutex);
switch (origin) { switch (origin) {
case 1: case 1:
offset += filp->f_pos; offset += filp->f_pos;
...@@ -619,7 +625,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) ...@@ -619,7 +625,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
nfs_file_open_context(filp)->dir_cookie = 0; nfs_file_open_context(filp)->dir_cookie = 0;
} }
out: out:
mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex); mutex_unlock(&inode->i_mutex);
return offset; return offset;
} }
...@@ -629,10 +635,11 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin) ...@@ -629,10 +635,11 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
*/ */
static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync) static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
{ {
dfprintk(VFS, "NFS: fsync_dir(%s/%s) datasync %d\n", dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
datasync); datasync);
nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC);
return 0; return 0;
} }
...@@ -767,7 +774,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -767,7 +774,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
struct nfs_fattr fattr; struct nfs_fattr fattr;
parent = dget_parent(dentry); parent = dget_parent(dentry);
lock_kernel();
dir = parent->d_inode; dir = parent->d_inode;
nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
inode = dentry->d_inode; inode = dentry->d_inode;
...@@ -805,7 +811,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -805,7 +811,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_valid: out_valid:
unlock_kernel();
dput(parent); dput(parent);
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n", dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
__func__, dentry->d_parent->d_name.name, __func__, dentry->d_parent->d_name.name,
...@@ -824,7 +829,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -824,7 +829,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
shrink_dcache_parent(dentry); shrink_dcache_parent(dentry);
} }
d_drop(dentry); d_drop(dentry);
unlock_kernel();
dput(parent); dput(parent);
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n", dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n",
__func__, dentry->d_parent->d_name.name, __func__, dentry->d_parent->d_name.name,
...@@ -858,6 +862,14 @@ static int nfs_dentry_delete(struct dentry *dentry) ...@@ -858,6 +862,14 @@ static int nfs_dentry_delete(struct dentry *dentry)
} }
static void nfs_drop_nlink(struct inode *inode)
{
spin_lock(&inode->i_lock);
if (inode->i_nlink > 0)
drop_nlink(inode);
spin_unlock(&inode->i_lock);
}
/* /*
* Called when the dentry loses inode. * Called when the dentry loses inode.
* We use it to clean up silly-renamed files. * We use it to clean up silly-renamed files.
...@@ -869,10 +881,8 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) ...@@ -869,10 +881,8 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
lock_kernel();
drop_nlink(inode); drop_nlink(inode);
nfs_complete_unlink(dentry, inode); nfs_complete_unlink(dentry, inode);
unlock_kernel();
} }
iput(inode); iput(inode);
} }
...@@ -903,8 +913,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -903,8 +913,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
res = ERR_PTR(-ENOMEM); res = ERR_PTR(-ENOMEM);
dentry->d_op = NFS_PROTO(dir)->dentry_ops; dentry->d_op = NFS_PROTO(dir)->dentry_ops;
lock_kernel();
/* /*
* If we're doing an exclusive create, optimize away the lookup * If we're doing an exclusive create, optimize away the lookup
* but don't hash the dentry. * but don't hash the dentry.
...@@ -912,7 +920,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -912,7 +920,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
if (nfs_is_exclusive_create(dir, nd)) { if (nfs_is_exclusive_create(dir, nd)) {
d_instantiate(dentry, NULL); d_instantiate(dentry, NULL);
res = NULL; res = NULL;
goto out_unlock; goto out;
} }
parent = dentry->d_parent; parent = dentry->d_parent;
...@@ -940,8 +948,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -940,8 +948,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unblock_sillyrename: out_unblock_sillyrename:
nfs_unblock_sillyrename(parent); nfs_unblock_sillyrename(parent);
out_unlock:
unlock_kernel();
out: out:
return res; return res;
} }
...@@ -999,9 +1005,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -999,9 +1005,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
} }
/* Open the file on the server */ /* Open the file on the server */
lock_kernel();
res = nfs4_atomic_open(dir, dentry, nd); res = nfs4_atomic_open(dir, dentry, nd);
unlock_kernel();
if (IS_ERR(res)) { if (IS_ERR(res)) {
error = PTR_ERR(res); error = PTR_ERR(res);
switch (error) { switch (error) {
...@@ -1063,9 +1067,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -1063,9 +1067,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
* operations that change the directory. We therefore save the * operations that change the directory. We therefore save the
* change attribute *before* we do the RPC call. * change attribute *before* we do the RPC call.
*/ */
lock_kernel();
ret = nfs4_open_revalidate(dir, dentry, openflags, nd); ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
unlock_kernel();
out: out:
dput(parent); dput(parent);
if (!ret) if (!ret)
...@@ -1218,14 +1220,11 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -1218,14 +1220,11 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
if ((nd->flags & LOOKUP_CREATE) != 0) if ((nd->flags & LOOKUP_CREATE) != 0)
open_flags = nd->intent.open.flags; open_flags = nd->intent.open.flags;
lock_kernel();
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd); error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
if (error != 0) if (error != 0)
goto out_err; goto out_err;
unlock_kernel();
return 0; return 0;
out_err: out_err:
unlock_kernel();
d_drop(dentry); d_drop(dentry);
return error; return error;
} }
...@@ -1248,14 +1247,11 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) ...@@ -1248,14 +1247,11 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
attr.ia_mode = mode; attr.ia_mode = mode;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
lock_kernel();
status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
if (status != 0) if (status != 0)
goto out_err; goto out_err;
unlock_kernel();
return 0; return 0;
out_err: out_err:
unlock_kernel();
d_drop(dentry); d_drop(dentry);
return status; return status;
} }
...@@ -1274,15 +1270,12 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -1274,15 +1270,12 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode | S_IFDIR; attr.ia_mode = mode | S_IFDIR;
lock_kernel();
error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
if (error != 0) if (error != 0)
goto out_err; goto out_err;
unlock_kernel();
return 0; return 0;
out_err: out_err:
d_drop(dentry); d_drop(dentry);
unlock_kernel();
return error; return error;
} }
...@@ -1299,14 +1292,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1299,14 +1292,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n", dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
lock_kernel();
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
/* Ensure the VFS deletes this inode */ /* Ensure the VFS deletes this inode */
if (error == 0 && dentry->d_inode != NULL) if (error == 0 && dentry->d_inode != NULL)
clear_nlink(dentry->d_inode); clear_nlink(dentry->d_inode);
else if (error == -ENOENT) else if (error == -ENOENT)
nfs_dentry_handle_enoent(dentry); nfs_dentry_handle_enoent(dentry);
unlock_kernel();
return error; return error;
} }
...@@ -1408,7 +1399,7 @@ static int nfs_safe_remove(struct dentry *dentry) ...@@ -1408,7 +1399,7 @@ static int nfs_safe_remove(struct dentry *dentry)
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
/* The VFS may want to delete this inode */ /* The VFS may want to delete this inode */
if (error == 0) if (error == 0)
drop_nlink(inode); nfs_drop_nlink(inode);
nfs_mark_for_revalidate(inode); nfs_mark_for_revalidate(inode);
} else } else
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
...@@ -1431,7 +1422,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1431,7 +1422,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id, dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name); dir->i_ino, dentry->d_name.name);
lock_kernel();
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
if (atomic_read(&dentry->d_count) > 1) { if (atomic_read(&dentry->d_count) > 1) {
...@@ -1440,7 +1430,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1440,7 +1430,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
/* Start asynchronous writeout of the inode */ /* Start asynchronous writeout of the inode */
write_inode_now(dentry->d_inode, 0); write_inode_now(dentry->d_inode, 0);
error = nfs_sillyrename(dir, dentry); error = nfs_sillyrename(dir, dentry);
unlock_kernel();
return error; return error;
} }
if (!d_unhashed(dentry)) { if (!d_unhashed(dentry)) {
...@@ -1454,7 +1443,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1454,7 +1443,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
} else if (need_rehash) } else if (need_rehash)
d_rehash(dentry); d_rehash(dentry);
unlock_kernel();
return error; return error;
} }
...@@ -1491,13 +1479,9 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym ...@@ -1491,13 +1479,9 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
attr.ia_mode = S_IFLNK | S_IRWXUGO; attr.ia_mode = S_IFLNK | S_IRWXUGO;
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
lock_kernel();
page = alloc_page(GFP_HIGHUSER); page = alloc_page(GFP_HIGHUSER);
if (!page) { if (!page)
unlock_kernel();
return -ENOMEM; return -ENOMEM;
}
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
memcpy(kaddr, symname, pathlen); memcpy(kaddr, symname, pathlen);
...@@ -1512,7 +1496,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym ...@@ -1512,7 +1496,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
dentry->d_name.name, symname, error); dentry->d_name.name, symname, error);
d_drop(dentry); d_drop(dentry);
__free_page(page); __free_page(page);
unlock_kernel();
return error; return error;
} }
...@@ -1530,7 +1513,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym ...@@ -1530,7 +1513,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
} else } else
__free_page(page); __free_page(page);
unlock_kernel();
return 0; return 0;
} }
...@@ -1544,14 +1526,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) ...@@ -1544,14 +1526,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
old_dentry->d_parent->d_name.name, old_dentry->d_name.name, old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name);
lock_kernel();
d_drop(dentry); d_drop(dentry);
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
if (error == 0) { if (error == 0) {
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
d_add(dentry, inode); d_add(dentry, inode);
} }
unlock_kernel();
return error; return error;
} }
...@@ -1591,7 +1571,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1591,7 +1571,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
* To prevent any new references to the target during the rename, * To prevent any new references to the target during the rename,
* we unhash the dentry and free the inode in advance. * we unhash the dentry and free the inode in advance.
*/ */
lock_kernel();
if (!d_unhashed(new_dentry)) { if (!d_unhashed(new_dentry)) {
d_drop(new_dentry); d_drop(new_dentry);
rehash = new_dentry; rehash = new_dentry;
...@@ -1635,7 +1614,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1635,7 +1614,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* dentry still busy? */ /* dentry still busy? */
goto out; goto out;
} else } else
drop_nlink(new_inode); nfs_drop_nlink(new_inode);
go_ahead: go_ahead:
/* /*
...@@ -1669,7 +1648,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1669,7 +1648,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
/* new dentry created? */ /* new dentry created? */
if (dentry) if (dentry)
dput(dentry); dput(dentry);
unlock_kernel();
return error; return error;
} }
...@@ -1962,8 +1940,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1962,8 +1940,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
} }
force_lookup: force_lookup:
lock_kernel();
if (!NFS_PROTO(inode)->access) if (!NFS_PROTO(inode)->access)
goto out_notsup; goto out_notsup;
...@@ -1973,7 +1949,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1973,7 +1949,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
put_rpccred(cred); put_rpccred(cred);
} else } else
res = PTR_ERR(cred); res = PTR_ERR(cred);
unlock_kernel();
out: out:
dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n", dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n",
inode->i_sb->s_id, inode->i_ino, mask, res); inode->i_sb->s_id, inode->i_ino, mask, res);
...@@ -1982,7 +1957,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1982,7 +1957,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
res = nfs_revalidate_inode(NFS_SERVER(inode), inode); res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (res == 0) if (res == 0)
res = generic_permission(inode, mask, NULL); res = generic_permission(inode, mask, NULL);
unlock_kernel();
goto out; goto out;
} }
......
...@@ -890,7 +890,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -890,7 +890,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
count = iov_length(iov, nr_segs); count = iov_length(iov, nr_segs);
nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count); nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count);
dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n", dfprintk(FILE, "NFS: direct read(%s/%s, %zd@%Ld)\n",
file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name, file->f_path.dentry->d_name.name,
count, (long long) pos); count, (long long) pos);
...@@ -947,7 +947,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -947,7 +947,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
count = iov_length(iov, nr_segs); count = iov_length(iov, nr_segs);
nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count); nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count);
dfprintk(VFS, "nfs: direct write(%s/%s, %zd@%Ld)\n", dfprintk(FILE, "NFS: direct write(%s/%s, %zd@%Ld)\n",
file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name, file->f_path.dentry->d_name.name,
count, (long long) pos); count, (long long) pos);
......
...@@ -50,7 +50,7 @@ static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov, ...@@ -50,7 +50,7 @@ static ssize_t nfs_file_read(struct kiocb *, const struct iovec *iov,
static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov, static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov,
unsigned long nr_segs, loff_t pos); unsigned long nr_segs, loff_t pos);
static int nfs_file_flush(struct file *, fl_owner_t id); static int nfs_file_flush(struct file *, fl_owner_t id);
static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); static int nfs_file_fsync(struct file *, struct dentry *dentry, int datasync);
static int nfs_check_flags(int flags); static int nfs_check_flags(int flags);
static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl); static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl);
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl);
...@@ -72,7 +72,7 @@ const struct file_operations nfs_file_operations = { ...@@ -72,7 +72,7 @@ const struct file_operations nfs_file_operations = {
.open = nfs_file_open, .open = nfs_file_open,
.flush = nfs_file_flush, .flush = nfs_file_flush,
.release = nfs_file_release, .release = nfs_file_release,
.fsync = nfs_fsync, .fsync = nfs_file_fsync,
.lock = nfs_lock, .lock = nfs_lock,
.flock = nfs_flock, .flock = nfs_flock,
.splice_read = nfs_file_splice_read, .splice_read = nfs_file_splice_read,
...@@ -119,25 +119,33 @@ nfs_file_open(struct inode *inode, struct file *filp) ...@@ -119,25 +119,33 @@ nfs_file_open(struct inode *inode, struct file *filp)
{ {
int res; int res;
dprintk("NFS: open file(%s/%s)\n",
filp->f_path.dentry->d_parent->d_name.name,
filp->f_path.dentry->d_name.name);
res = nfs_check_flags(filp->f_flags); res = nfs_check_flags(filp->f_flags);
if (res) if (res)
return res; return res;
nfs_inc_stats(inode, NFSIOS_VFSOPEN); nfs_inc_stats(inode, NFSIOS_VFSOPEN);
lock_kernel(); res = nfs_open(inode, filp);
res = NFS_PROTO(inode)->file_open(inode, filp);
unlock_kernel();
return res; return res;
} }
static int static int
nfs_file_release(struct inode *inode, struct file *filp) nfs_file_release(struct inode *inode, struct file *filp)
{ {
struct dentry *dentry = filp->f_path.dentry;
dprintk("NFS: release(%s/%s)\n",
dentry->d_parent->d_name.name,
dentry->d_name.name);
/* Ensure that dirty pages are flushed out with the right creds */ /* Ensure that dirty pages are flushed out with the right creds */
if (filp->f_mode & FMODE_WRITE) if (filp->f_mode & FMODE_WRITE)
nfs_wb_all(filp->f_path.dentry->d_inode); nfs_wb_all(dentry->d_inode);
nfs_inc_stats(inode, NFSIOS_VFSRELEASE); nfs_inc_stats(inode, NFSIOS_VFSRELEASE);
return NFS_PROTO(inode)->file_release(inode, filp); return nfs_release(inode, filp);
} }
/** /**
...@@ -171,6 +179,12 @@ static int nfs_revalidate_file_size(struct inode *inode, struct file *filp) ...@@ -171,6 +179,12 @@ static int nfs_revalidate_file_size(struct inode *inode, struct file *filp)
static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
{ {
loff_t loff; loff_t loff;
dprintk("NFS: llseek file(%s/%s, %lld, %d)\n",
filp->f_path.dentry->d_parent->d_name.name,
filp->f_path.dentry->d_name.name,
offset, origin);
/* origin == SEEK_END => we must revalidate the cached file length */ /* origin == SEEK_END => we must revalidate the cached file length */
if (origin == SEEK_END) { if (origin == SEEK_END) {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
...@@ -185,7 +199,7 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) ...@@ -185,7 +199,7 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
} }
/* /*
* Helper for nfs_file_flush() and nfs_fsync() * Helper for nfs_file_flush() and nfs_file_fsync()
* *
* Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to
* disk, but it retrieves and clears ctx->error after synching, despite * disk, but it retrieves and clears ctx->error after synching, despite
...@@ -211,16 +225,18 @@ static int nfs_do_fsync(struct nfs_open_context *ctx, struct inode *inode) ...@@ -211,16 +225,18 @@ static int nfs_do_fsync(struct nfs_open_context *ctx, struct inode *inode)
/* /*
* Flush all dirty pages, and check for write errors. * Flush all dirty pages, and check for write errors.
*
*/ */
static int static int
nfs_file_flush(struct file *file, fl_owner_t id) nfs_file_flush(struct file *file, fl_owner_t id)
{ {
struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_open_context *ctx = nfs_file_open_context(file);
struct inode *inode = file->f_path.dentry->d_inode; struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
int status; int status;
dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); dprintk("NFS: flush(%s/%s)\n",
dentry->d_parent->d_name.name,
dentry->d_name.name);
if ((file->f_mode & FMODE_WRITE) == 0) if ((file->f_mode & FMODE_WRITE) == 0)
return 0; return 0;
...@@ -245,7 +261,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -245,7 +261,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
if (iocb->ki_filp->f_flags & O_DIRECT) if (iocb->ki_filp->f_flags & O_DIRECT)
return nfs_file_direct_read(iocb, iov, nr_segs, pos); return nfs_file_direct_read(iocb, iov, nr_segs, pos);
dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n", dprintk("NFS: read(%s/%s, %lu@%lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long) pos); (unsigned long) count, (unsigned long) pos);
...@@ -265,7 +281,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos, ...@@ -265,7 +281,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos,
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
ssize_t res; ssize_t res;
dfprintk(VFS, "nfs: splice_read(%s/%s, %lu@%Lu)\n", dprintk("NFS: splice_read(%s/%s, %lu@%Lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
(unsigned long) count, (unsigned long long) *ppos); (unsigned long) count, (unsigned long long) *ppos);
...@@ -282,7 +298,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) ...@@ -282,7 +298,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int status; int status;
dfprintk(VFS, "nfs: mmap(%s/%s)\n", dprintk("NFS: mmap(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name);
status = nfs_revalidate_mapping(inode, file->f_mapping); status = nfs_revalidate_mapping(inode, file->f_mapping);
...@@ -300,12 +316,14 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) ...@@ -300,12 +316,14 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
* whether any write errors occurred for this process. * whether any write errors occurred for this process.
*/ */
static int static int
nfs_fsync(struct file *file, struct dentry *dentry, int datasync) nfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
{ {
struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_open_context *ctx = nfs_file_open_context(file);
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); dprintk("NFS: fsync file(%s/%s) datasync %d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
datasync);
nfs_inc_stats(inode, NFSIOS_VFSFSYNC); nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
return nfs_do_fsync(ctx, inode); return nfs_do_fsync(ctx, inode);
...@@ -328,6 +346,11 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping, ...@@ -328,6 +346,11 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
struct page *page; struct page *page;
index = pos >> PAGE_CACHE_SHIFT; index = pos >> PAGE_CACHE_SHIFT;
dfprintk(PAGECACHE, "NFS: write_begin(%s/%s(%ld), %u@%lld)\n",
file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name,
mapping->host->i_ino, len, (long long) pos);
page = __grab_cache_page(mapping, index); page = __grab_cache_page(mapping, index);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
...@@ -348,9 +371,32 @@ static int nfs_write_end(struct file *file, struct address_space *mapping, ...@@ -348,9 +371,32 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
unsigned offset = pos & (PAGE_CACHE_SIZE - 1); unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
int status; int status;
lock_kernel(); dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n",
file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name,
mapping->host->i_ino, len, (long long) pos);
/*
* Zero any uninitialised parts of the page, and then mark the page
* as up to date if it turns out that we're extending the file.
*/
if (!PageUptodate(page)) {
unsigned pglen = nfs_page_length(page);
unsigned end = offset + len;
if (pglen == 0) {
zero_user_segments(page, 0, offset,
end, PAGE_CACHE_SIZE);
SetPageUptodate(page);
} else if (end >= pglen) {
zero_user_segment(page, end, PAGE_CACHE_SIZE);
if (offset == 0)
SetPageUptodate(page);
} else
zero_user_segment(page, pglen, PAGE_CACHE_SIZE);
}
status = nfs_updatepage(file, page, offset, copied); status = nfs_updatepage(file, page, offset, copied);
unlock_kernel();
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
...@@ -362,6 +408,8 @@ static int nfs_write_end(struct file *file, struct address_space *mapping, ...@@ -362,6 +408,8 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
static void nfs_invalidate_page(struct page *page, unsigned long offset) static void nfs_invalidate_page(struct page *page, unsigned long offset)
{ {
dfprintk(PAGECACHE, "NFS: invalidate_page(%p, %lu)\n", page, offset);
if (offset != 0) if (offset != 0)
return; return;
/* Cancel any unstarted writes on this page */ /* Cancel any unstarted writes on this page */
...@@ -370,13 +418,20 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset) ...@@ -370,13 +418,20 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
static int nfs_release_page(struct page *page, gfp_t gfp) static int nfs_release_page(struct page *page, gfp_t gfp)
{ {
dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
/* If PagePrivate() is set, then the page is not freeable */ /* If PagePrivate() is set, then the page is not freeable */
return 0; return 0;
} }
static int nfs_launder_page(struct page *page) static int nfs_launder_page(struct page *page)
{ {
return nfs_wb_page(page->mapping->host, page); struct inode *inode = page->mapping->host;
dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n",
inode->i_ino, (long long)page_offset(page));
return nfs_wb_page(inode, page);
} }
const struct address_space_operations nfs_file_aops = { const struct address_space_operations nfs_file_aops = {
...@@ -396,13 +451,19 @@ const struct address_space_operations nfs_file_aops = { ...@@ -396,13 +451,19 @@ const struct address_space_operations nfs_file_aops = {
static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page) static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page)
{ {
struct file *filp = vma->vm_file; struct file *filp = vma->vm_file;
struct dentry *dentry = filp->f_path.dentry;
unsigned pagelen; unsigned pagelen;
int ret = -EINVAL; int ret = -EINVAL;
struct address_space *mapping; struct address_space *mapping;
dfprintk(PAGECACHE, "NFS: vm_page_mkwrite(%s/%s(%ld), offset %lld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
filp->f_mapping->host->i_ino,
(long long)page_offset(page));
lock_page(page); lock_page(page);
mapping = page->mapping; mapping = page->mapping;
if (mapping != vma->vm_file->f_path.dentry->d_inode->i_mapping) if (mapping != dentry->d_inode->i_mapping)
goto out_unlock; goto out_unlock;
ret = 0; ret = 0;
...@@ -450,9 +511,9 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -450,9 +511,9 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
if (iocb->ki_filp->f_flags & O_DIRECT) if (iocb->ki_filp->f_flags & O_DIRECT)
return nfs_file_direct_write(iocb, iov, nr_segs, pos); return nfs_file_direct_write(iocb, iov, nr_segs, pos);
dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%Ld)\n", dprintk("NFS: write(%s/%s, %lu@%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_ino, (unsigned long) count, (long long) pos); (unsigned long) count, (long long) pos);
result = -EBUSY; result = -EBUSY;
if (IS_SWAPFILE(inode)) if (IS_SWAPFILE(inode))
...@@ -586,7 +647,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) ...@@ -586,7 +647,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
* This makes locking act as a cache coherency point. * This makes locking act as a cache coherency point.
*/ */
nfs_sync_mapping(filp->f_mapping); nfs_sync_mapping(filp->f_mapping);
nfs_zap_caches(inode); if (!nfs_have_delegation(inode, FMODE_READ))
nfs_zap_caches(inode);
out: out:
return status; return status;
} }
...@@ -596,23 +658,35 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) ...@@ -596,23 +658,35 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
*/ */
static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{ {
struct inode * inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int ret = -ENOLCK;
dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n", dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n",
inode->i_sb->s_id, inode->i_ino, filp->f_path.dentry->d_parent->d_name.name,
filp->f_path.dentry->d_name.name,
fl->fl_type, fl->fl_flags, fl->fl_type, fl->fl_flags,
(long long)fl->fl_start, (long long)fl->fl_end); (long long)fl->fl_start, (long long)fl->fl_end);
nfs_inc_stats(inode, NFSIOS_VFSLOCK); nfs_inc_stats(inode, NFSIOS_VFSLOCK);
/* No mandatory locks over NFS */ /* No mandatory locks over NFS */
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK) if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
return -ENOLCK; goto out_err;
if (NFS_PROTO(inode)->lock_check_bounds != NULL) {
ret = NFS_PROTO(inode)->lock_check_bounds(fl);
if (ret < 0)
goto out_err;
}
if (IS_GETLK(cmd)) if (IS_GETLK(cmd))
return do_getlk(filp, cmd, fl); ret = do_getlk(filp, cmd, fl);
if (fl->fl_type == F_UNLCK) else if (fl->fl_type == F_UNLCK)
return do_unlk(filp, cmd, fl); ret = do_unlk(filp, cmd, fl);
return do_setlk(filp, cmd, fl); else
ret = do_setlk(filp, cmd, fl);
out_err:
return ret;
} }
/* /*
...@@ -620,9 +694,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -620,9 +694,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
*/ */
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
{ {
dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n", dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n",
filp->f_path.dentry->d_inode->i_sb->s_id, filp->f_path.dentry->d_parent->d_name.name,
filp->f_path.dentry->d_inode->i_ino, filp->f_path.dentry->d_name.name,
fl->fl_type, fl->fl_flags); fl->fl_type, fl->fl_flags);
/* /*
...@@ -645,12 +719,15 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -645,12 +719,15 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
return do_setlk(filp, cmd, fl); return do_setlk(filp, cmd, fl);
} }
/*
* There is no protocol support for leases, so we have no way to implement
* them correctly in the face of opens by other clients.
*/
static int nfs_setlease(struct file *file, long arg, struct file_lock **fl) static int nfs_setlease(struct file *file, long arg, struct file_lock **fl)
{ {
/* dprintk("NFS: setlease(%s/%s, arg=%ld)\n",
* There is no protocol support for leases, so we have no way file->f_path.dentry->d_parent->d_name.name,
* to implement them correctly in the face of opens by other file->f_path.dentry->d_name.name, arg);
* clients.
*/
return -EINVAL; return -EINVAL;
} }
...@@ -57,8 +57,6 @@ static int enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED; ...@@ -57,8 +57,6 @@ static int enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
static void nfs_invalidate_inode(struct inode *); static void nfs_invalidate_inode(struct inode *);
static int nfs_update_inode(struct inode *, struct nfs_fattr *); static int nfs_update_inode(struct inode *, struct nfs_fattr *);
static void nfs_zap_acl_cache(struct inode *);
static struct kmem_cache * nfs_inode_cachep; static struct kmem_cache * nfs_inode_cachep;
static inline unsigned long static inline unsigned long
...@@ -167,7 +165,7 @@ void nfs_zap_mapping(struct inode *inode, struct address_space *mapping) ...@@ -167,7 +165,7 @@ void nfs_zap_mapping(struct inode *inode, struct address_space *mapping)
} }
} }
static void nfs_zap_acl_cache(struct inode *inode) void nfs_zap_acl_cache(struct inode *inode)
{ {
void (*clear_acl_cache)(struct inode *); void (*clear_acl_cache)(struct inode *);
...@@ -347,7 +345,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -347,7 +345,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
goto out; goto out;
} }
#define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET) #define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE)
int int
nfs_setattr(struct dentry *dentry, struct iattr *attr) nfs_setattr(struct dentry *dentry, struct iattr *attr)
...@@ -369,10 +367,9 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -369,10 +367,9 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
/* Optimization: if the end result is no change, don't RPC */ /* Optimization: if the end result is no change, don't RPC */
attr->ia_valid &= NFS_VALID_ATTRS; attr->ia_valid &= NFS_VALID_ATTRS;
if (attr->ia_valid == 0) if ((attr->ia_valid & ~ATTR_FILE) == 0)
return 0; return 0;
lock_kernel();
/* Write all dirty data */ /* Write all dirty data */
if (S_ISREG(inode->i_mode)) { if (S_ISREG(inode->i_mode)) {
filemap_write_and_wait(inode->i_mapping); filemap_write_and_wait(inode->i_mapping);
...@@ -386,10 +383,65 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -386,10 +383,65 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
if (error == 0) if (error == 0)
nfs_refresh_inode(inode, &fattr); nfs_refresh_inode(inode, &fattr);
unlock_kernel();
return error; return error;
} }
/**
* nfs_vmtruncate - unmap mappings "freed" by truncate() syscall
* @inode: inode of the file used
* @offset: file offset to start truncating
*
* This is a copy of the common vmtruncate, but with the locking
* corrected to take into account the fact that NFS requires
* inode->i_size to be updated under the inode->i_lock.
*/
static int nfs_vmtruncate(struct inode * inode, loff_t offset)
{
if (i_size_read(inode) < offset) {
unsigned long limit;
limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
if (limit != RLIM_INFINITY && offset > limit)
goto out_sig;
if (offset > inode->i_sb->s_maxbytes)
goto out_big;
spin_lock(&inode->i_lock);
i_size_write(inode, offset);
spin_unlock(&inode->i_lock);
} else {
struct address_space *mapping = inode->i_mapping;
/*
* truncation of in-use swapfiles is disallowed - it would
* cause subsequent swapout to scribble on the now-freed
* blocks.
*/
if (IS_SWAPFILE(inode))
return -ETXTBSY;
spin_lock(&inode->i_lock);
i_size_write(inode, offset);
spin_unlock(&inode->i_lock);
/*
* unmap_mapping_range is called twice, first simply for
* efficiency so that truncate_inode_pages does fewer
* single-page unmaps. However after this first call, and
* before truncate_inode_pages finishes, it is possible for
* private pages to be COWed, which remain after
* truncate_inode_pages finishes, hence the second
* unmap_mapping_range call must be made for correctness.
*/
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
truncate_inode_pages(mapping, offset);
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
}
return 0;
out_sig:
send_sig(SIGXFSZ, current, 0);
out_big:
return -EFBIG;
}
/** /**
* nfs_setattr_update_inode - Update inode metadata after a setattr call. * nfs_setattr_update_inode - Update inode metadata after a setattr call.
* @inode: pointer to struct inode * @inode: pointer to struct inode
...@@ -416,8 +468,7 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr) ...@@ -416,8 +468,7 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
} }
if ((attr->ia_valid & ATTR_SIZE) != 0) { if ((attr->ia_valid & ATTR_SIZE) != 0) {
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC); nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
inode->i_size = attr->ia_size; nfs_vmtruncate(inode, attr->ia_size);
vmtruncate(inode, attr->ia_size);
} }
} }
...@@ -647,7 +698,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -647,7 +698,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
inode->i_sb->s_id, (long long)NFS_FILEID(inode)); inode->i_sb->s_id, (long long)NFS_FILEID(inode));
nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
lock_kernel();
if (is_bad_inode(inode)) if (is_bad_inode(inode))
goto out_nowait; goto out_nowait;
if (NFS_STALE(inode)) if (NFS_STALE(inode))
...@@ -696,7 +746,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -696,7 +746,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
nfs_wake_up_inode(inode); nfs_wake_up_inode(inode);
out_nowait: out_nowait:
unlock_kernel();
return status; return status;
} }
...@@ -831,9 +880,9 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -831,9 +880,9 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
nfsi->cache_validity |= NFS_INO_INVALID_DATA; nfsi->cache_validity |= NFS_INO_INVALID_DATA;
} }
if (inode->i_size == nfs_size_to_loff_t(fattr->pre_size) && if (i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size) &&
nfsi->npages == 0) nfsi->npages == 0)
inode->i_size = nfs_size_to_loff_t(fattr->size); i_size_write(inode, nfs_size_to_loff_t(fattr->size));
} }
} }
...@@ -974,7 +1023,7 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa ...@@ -974,7 +1023,7 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa
(fattr->valid & NFS_ATTR_WCC) == 0) { (fattr->valid & NFS_ATTR_WCC) == 0) {
memcpy(&fattr->pre_ctime, &inode->i_ctime, sizeof(fattr->pre_ctime)); memcpy(&fattr->pre_ctime, &inode->i_ctime, sizeof(fattr->pre_ctime));
memcpy(&fattr->pre_mtime, &inode->i_mtime, sizeof(fattr->pre_mtime)); memcpy(&fattr->pre_mtime, &inode->i_mtime, sizeof(fattr->pre_mtime));
fattr->pre_size = inode->i_size; fattr->pre_size = i_size_read(inode);
fattr->valid |= NFS_ATTR_WCC; fattr->valid |= NFS_ATTR_WCC;
} }
return nfs_post_op_update_inode(inode, fattr); return nfs_post_op_update_inode(inode, fattr);
...@@ -1059,7 +1108,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1059,7 +1108,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
/* Do we perhaps have any outstanding writes, or has /* Do we perhaps have any outstanding writes, or has
* the file grown beyond our last write? */ * the file grown beyond our last write? */
if (nfsi->npages == 0 || new_isize > cur_isize) { if (nfsi->npages == 0 || new_isize > cur_isize) {
inode->i_size = new_isize; i_size_write(inode, new_isize);
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
} }
dprintk("NFS: isize change on server for file %s/%ld\n", dprintk("NFS: isize change on server for file %s/%ld\n",
......
...@@ -150,6 +150,7 @@ extern void nfs_clear_inode(struct inode *); ...@@ -150,6 +150,7 @@ extern void nfs_clear_inode(struct inode *);
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern void nfs4_clear_inode(struct inode *); extern void nfs4_clear_inode(struct inode *);
#endif #endif
void nfs_zap_acl_cache(struct inode *inode);
/* super.c */ /* super.c */
extern struct file_system_type nfs_xdev_fs_type; extern struct file_system_type nfs_xdev_fs_type;
......
...@@ -5,135 +5,41 @@ ...@@ -5,135 +5,41 @@
* *
* Copyright (C) 2005, 2006 Chuck Lever <cel@netapp.com> * Copyright (C) 2005, 2006 Chuck Lever <cel@netapp.com>
* *
* NFS client per-mount statistics provide information about the health of
* the NFS client and the health of each NFS mount point. Generally these
* are not for detailed problem diagnosis, but simply to indicate that there
* is a problem.
*
* These counters are not meant to be human-readable, but are meant to be
* integrated into system monitoring tools such as "sar" and "iostat". As
* such, the counters are sampled by the tools over time, and are never
* zeroed after a file system is mounted. Moving averages can be computed
* by the tools by taking the difference between two instantaneous samples
* and dividing that by the time between the samples.
*/ */
#ifndef _NFS_IOSTAT #ifndef _NFS_IOSTAT
#define _NFS_IOSTAT #define _NFS_IOSTAT
#define NFS_IOSTAT_VERS "1.0"
/*
* NFS byte counters
*
* 1. SERVER - the number of payload bytes read from or written to the
* server by the NFS client via an NFS READ or WRITE request.
*
* 2. NORMAL - the number of bytes read or written by applications via
* the read(2) and write(2) system call interfaces.
*
* 3. DIRECT - the number of bytes read or written from files opened
* with the O_DIRECT flag.
*
* These counters give a view of the data throughput into and out of the NFS
* client. Comparing the number of bytes requested by an application with the
* number of bytes the client requests from the server can provide an
* indication of client efficiency (per-op, cache hits, etc).
*
* These counters can also help characterize which access methods are in
* use. DIRECT by itself shows whether there is any O_DIRECT traffic.
* NORMAL + DIRECT shows how much data is going through the system call
* interface. A large amount of SERVER traffic without much NORMAL or
* DIRECT traffic shows that applications are using mapped files.
*
* NFS page counters
*
* These count the number of pages read or written via nfs_readpage(),
* nfs_readpages(), or their write equivalents.
*/
enum nfs_stat_bytecounters {
NFSIOS_NORMALREADBYTES = 0,
NFSIOS_NORMALWRITTENBYTES,
NFSIOS_DIRECTREADBYTES,
NFSIOS_DIRECTWRITTENBYTES,
NFSIOS_SERVERREADBYTES,
NFSIOS_SERVERWRITTENBYTES,
NFSIOS_READPAGES,
NFSIOS_WRITEPAGES,
__NFSIOS_BYTESMAX,
};
/*
* NFS event counters
*
* These counters provide a low-overhead way of monitoring client activity
* without enabling NFS trace debugging. The counters show the rate at
* which VFS requests are made, and how often the client invalidates its
* data and attribute caches. This allows system administrators to monitor
* such things as how close-to-open is working, and answer questions such
* as "why are there so many GETATTR requests on the wire?"
*
* They also count anamolous events such as short reads and writes, silly
* renames due to close-after-delete, and operations that change the size
* of a file (such operations can often be the source of data corruption
* if applications aren't using file locking properly).
*/
enum nfs_stat_eventcounters {
NFSIOS_INODEREVALIDATE = 0,
NFSIOS_DENTRYREVALIDATE,
NFSIOS_DATAINVALIDATE,
NFSIOS_ATTRINVALIDATE,
NFSIOS_VFSOPEN,
NFSIOS_VFSLOOKUP,
NFSIOS_VFSACCESS,
NFSIOS_VFSUPDATEPAGE,
NFSIOS_VFSREADPAGE,
NFSIOS_VFSREADPAGES,
NFSIOS_VFSWRITEPAGE,
NFSIOS_VFSWRITEPAGES,
NFSIOS_VFSGETDENTS,
NFSIOS_VFSSETATTR,
NFSIOS_VFSFLUSH,
NFSIOS_VFSFSYNC,
NFSIOS_VFSLOCK,
NFSIOS_VFSRELEASE,
NFSIOS_CONGESTIONWAIT,
NFSIOS_SETATTRTRUNC,
NFSIOS_EXTENDWRITE,
NFSIOS_SILLYRENAME,
NFSIOS_SHORTREAD,
NFSIOS_SHORTWRITE,
NFSIOS_DELAY,
__NFSIOS_COUNTSMAX,
};
#ifdef __KERNEL__
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/cache.h> #include <linux/cache.h>
#include <linux/nfs_iostat.h>
struct nfs_iostats { struct nfs_iostats {
unsigned long long bytes[__NFSIOS_BYTESMAX]; unsigned long long bytes[__NFSIOS_BYTESMAX];
unsigned long events[__NFSIOS_COUNTSMAX]; unsigned long events[__NFSIOS_COUNTSMAX];
} ____cacheline_aligned; } ____cacheline_aligned;
static inline void nfs_inc_server_stats(struct nfs_server *server, enum nfs_stat_eventcounters stat) static inline void nfs_inc_server_stats(const struct nfs_server *server,
enum nfs_stat_eventcounters stat)
{ {
struct nfs_iostats *iostats; struct nfs_iostats *iostats;
int cpu; int cpu;
cpu = get_cpu(); cpu = get_cpu();
iostats = per_cpu_ptr(server->io_stats, cpu); iostats = per_cpu_ptr(server->io_stats, cpu);
iostats->events[stat] ++; iostats->events[stat]++;
put_cpu_no_resched(); put_cpu_no_resched();
} }
static inline void nfs_inc_stats(struct inode *inode, enum nfs_stat_eventcounters stat) static inline void nfs_inc_stats(const struct inode *inode,
enum nfs_stat_eventcounters stat)
{ {
nfs_inc_server_stats(NFS_SERVER(inode), stat); nfs_inc_server_stats(NFS_SERVER(inode), stat);
} }
static inline void nfs_add_server_stats(struct nfs_server *server, enum nfs_stat_bytecounters stat, unsigned long addend) static inline void nfs_add_server_stats(const struct nfs_server *server,
enum nfs_stat_bytecounters stat,
unsigned long addend)
{ {
struct nfs_iostats *iostats; struct nfs_iostats *iostats;
int cpu; int cpu;
...@@ -144,7 +50,9 @@ static inline void nfs_add_server_stats(struct nfs_server *server, enum nfs_stat ...@@ -144,7 +50,9 @@ static inline void nfs_add_server_stats(struct nfs_server *server, enum nfs_stat
put_cpu_no_resched(); put_cpu_no_resched();
} }
static inline void nfs_add_stats(struct inode *inode, enum nfs_stat_bytecounters stat, unsigned long addend) static inline void nfs_add_stats(const struct inode *inode,
enum nfs_stat_bytecounters stat,
unsigned long addend)
{ {
nfs_add_server_stats(NFS_SERVER(inode), stat, addend); nfs_add_server_stats(NFS_SERVER(inode), stat, addend);
} }
...@@ -160,5 +68,4 @@ static inline void nfs_free_iostats(struct nfs_iostats *stats) ...@@ -160,5 +68,4 @@ static inline void nfs_free_iostats(struct nfs_iostats *stats)
free_percpu(stats); free_percpu(stats);
} }
#endif #endif /* _NFS_IOSTAT */
#endif
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
#include <linux/nfsacl.h> #include <linux/nfsacl.h>
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_PROC #define NFSDBG_FACILITY NFSDBG_PROC
ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size) ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size)
...@@ -205,6 +207,8 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type) ...@@ -205,6 +207,8 @@ struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
status = nfs_revalidate_inode(server, inode); status = nfs_revalidate_inode(server, inode);
if (status < 0) if (status < 0)
return ERR_PTR(status); return ERR_PTR(status);
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
nfs_zap_acl_cache(inode);
acl = nfs3_get_cached_acl(inode, type); acl = nfs3_get_cached_acl(inode, type);
if (acl != ERR_PTR(-EAGAIN)) if (acl != ERR_PTR(-EAGAIN))
return acl; return acl;
...@@ -319,9 +323,8 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, ...@@ -319,9 +323,8 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
dprintk("NFS call setacl\n"); dprintk("NFS call setacl\n");
msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL]; msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL];
status = rpc_call_sync(server->client_acl, &msg, 0); status = rpc_call_sync(server->client_acl, &msg, 0);
spin_lock(&inode->i_lock); nfs_access_zap_cache(inode);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS; nfs_zap_acl_cache(inode);
spin_unlock(&inode->i_lock);
dprintk("NFS reply setacl: %d\n", status); dprintk("NFS reply setacl: %d\n", status);
/* pages may have been allocated at the xdr layer. */ /* pages may have been allocated at the xdr layer. */
......
...@@ -129,6 +129,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -129,6 +129,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
int status; int status;
dprintk("NFS call setattr\n"); dprintk("NFS call setattr\n");
if (sattr->ia_valid & ATTR_FILE)
msg.rpc_cred = nfs_file_cred(sattr->ia_file);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status == 0) if (status == 0)
...@@ -248,6 +250,53 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page, ...@@ -248,6 +250,53 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page,
return status; return status;
} }
struct nfs3_createdata {
struct rpc_message msg;
union {
struct nfs3_createargs create;
struct nfs3_mkdirargs mkdir;
struct nfs3_symlinkargs symlink;
struct nfs3_mknodargs mknod;
} arg;
struct nfs3_diropres res;
struct nfs_fh fh;
struct nfs_fattr fattr;
struct nfs_fattr dir_attr;
};
static struct nfs3_createdata *nfs3_alloc_createdata(void)
{
struct nfs3_createdata *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data != NULL) {
data->msg.rpc_argp = &data->arg;
data->msg.rpc_resp = &data->res;
data->res.fh = &data->fh;
data->res.fattr = &data->fattr;
data->res.dir_attr = &data->dir_attr;
nfs_fattr_init(data->res.fattr);
nfs_fattr_init(data->res.dir_attr);
}
return data;
}
static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_createdata *data)
{
int status;
status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
nfs_post_op_update_inode(dir, data->res.dir_attr);
if (status == 0)
status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
return status;
}
static void nfs3_free_createdata(struct nfs3_createdata *data)
{
kfree(data);
}
/* /*
* Create a regular file. * Create a regular file.
* For now, we don't implement O_EXCL. * For now, we don't implement O_EXCL.
...@@ -256,70 +305,60 @@ static int ...@@ -256,70 +305,60 @@ static int
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags, struct nameidata *nd) int flags, struct nameidata *nd)
{ {
struct nfs_fh fhandle; struct nfs3_createdata *data;
struct nfs_fattr fattr;
struct nfs_fattr dir_attr;
struct nfs3_createargs arg = {
.fh = NFS_FH(dir),
.name = dentry->d_name.name,
.len = dentry->d_name.len,
.sattr = sattr,
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
.fh = &fhandle,
.fattr = &fattr
};
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_CREATE],
.rpc_argp = &arg,
.rpc_resp = &res,
};
mode_t mode = sattr->ia_mode; mode_t mode = sattr->ia_mode;
int status; int status = -ENOMEM;
dprintk("NFS call create %s\n", dentry->d_name.name); dprintk("NFS call create %s\n", dentry->d_name.name);
arg.createmode = NFS3_CREATE_UNCHECKED;
data = nfs3_alloc_createdata();
if (data == NULL)
goto out;
data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_CREATE];
data->arg.create.fh = NFS_FH(dir);
data->arg.create.name = dentry->d_name.name;
data->arg.create.len = dentry->d_name.len;
data->arg.create.sattr = sattr;
data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
if (flags & O_EXCL) { if (flags & O_EXCL) {
arg.createmode = NFS3_CREATE_EXCLUSIVE; data->arg.create.createmode = NFS3_CREATE_EXCLUSIVE;
arg.verifier[0] = jiffies; data->arg.create.verifier[0] = jiffies;
arg.verifier[1] = current->pid; data->arg.create.verifier[1] = current->pid;
} }
sattr->ia_mode &= ~current->fs->umask; sattr->ia_mode &= ~current->fs->umask;
again: for (;;) {
nfs_fattr_init(&dir_attr); status = nfs3_do_create(dir, dentry, data);
nfs_fattr_init(&fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_refresh_inode(dir, &dir_attr);
/* If the server doesn't support the exclusive creation semantics, if (status != -ENOTSUPP)
* try again with simple 'guarded' mode. */ break;
if (status == -ENOTSUPP) { /* If the server doesn't support the exclusive creation
switch (arg.createmode) { * semantics, try again with simple 'guarded' mode. */
switch (data->arg.create.createmode) {
case NFS3_CREATE_EXCLUSIVE: case NFS3_CREATE_EXCLUSIVE:
arg.createmode = NFS3_CREATE_GUARDED; data->arg.create.createmode = NFS3_CREATE_GUARDED;
break; break;
case NFS3_CREATE_GUARDED: case NFS3_CREATE_GUARDED:
arg.createmode = NFS3_CREATE_UNCHECKED; data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
break; break;
case NFS3_CREATE_UNCHECKED: case NFS3_CREATE_UNCHECKED:
goto out; goto out;
} }
goto again; nfs_fattr_init(data->res.dir_attr);
nfs_fattr_init(data->res.fattr);
} }
if (status == 0)
status = nfs_instantiate(dentry, &fhandle, &fattr);
if (status != 0) if (status != 0)
goto out; goto out;
/* When we created the file with exclusive semantics, make /* When we created the file with exclusive semantics, make
* sure we set the attributes afterwards. */ * sure we set the attributes afterwards. */
if (arg.createmode == NFS3_CREATE_EXCLUSIVE) { if (data->arg.create.createmode == NFS3_CREATE_EXCLUSIVE) {
dprintk("NFS call setattr (post-create)\n"); dprintk("NFS call setattr (post-create)\n");
if (!(sattr->ia_valid & ATTR_ATIME_SET)) if (!(sattr->ia_valid & ATTR_ATIME_SET))
...@@ -330,14 +369,15 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, ...@@ -330,14 +369,15 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
/* Note: we could use a guarded setattr here, but I'm /* Note: we could use a guarded setattr here, but I'm
* not sure this buys us anything (and I'd have * not sure this buys us anything (and I'd have
* to revamp the NFSv3 XDR code) */ * to revamp the NFSv3 XDR code) */
status = nfs3_proc_setattr(dentry, &fattr, sattr); status = nfs3_proc_setattr(dentry, data->res.fattr, sattr);
nfs_post_op_update_inode(dentry->d_inode, &fattr); nfs_post_op_update_inode(dentry->d_inode, data->res.fattr);
dprintk("NFS reply setattr (post-create): %d\n", status); dprintk("NFS reply setattr (post-create): %d\n", status);
if (status != 0)
goto out;
} }
if (status != 0)
goto out;
status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode); status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out: out:
nfs3_free_createdata(data);
dprintk("NFS reply create: %d\n", status); dprintk("NFS reply create: %d\n", status);
return status; return status;
} }
...@@ -452,40 +492,28 @@ static int ...@@ -452,40 +492,28 @@ static int
nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
unsigned int len, struct iattr *sattr) unsigned int len, struct iattr *sattr)
{ {
struct nfs_fh fhandle; struct nfs3_createdata *data;
struct nfs_fattr fattr, dir_attr; int status = -ENOMEM;
struct nfs3_symlinkargs arg = {
.fromfh = NFS_FH(dir),
.fromname = dentry->d_name.name,
.fromlen = dentry->d_name.len,
.pages = &page,
.pathlen = len,
.sattr = sattr
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
.fh = &fhandle,
.fattr = &fattr
};
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
if (len > NFS3_MAXPATHLEN) if (len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG; return -ENAMETOOLONG;
dprintk("NFS call symlink %s\n", dentry->d_name.name); dprintk("NFS call symlink %s\n", dentry->d_name.name);
nfs_fattr_init(&dir_attr); data = nfs3_alloc_createdata();
nfs_fattr_init(&fattr); if (data == NULL)
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0)
goto out; goto out;
status = nfs_instantiate(dentry, &fhandle, &fattr); data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK];
data->arg.symlink.fromfh = NFS_FH(dir);
data->arg.symlink.fromname = dentry->d_name.name;
data->arg.symlink.fromlen = dentry->d_name.len;
data->arg.symlink.pages = &page;
data->arg.symlink.pathlen = len;
data->arg.symlink.sattr = sattr;
status = nfs3_do_create(dir, dentry, data);
nfs3_free_createdata(data);
out: out:
dprintk("NFS reply symlink: %d\n", status); dprintk("NFS reply symlink: %d\n", status);
return status; return status;
...@@ -494,42 +522,31 @@ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, ...@@ -494,42 +522,31 @@ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
static int static int
nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
{ {
struct nfs_fh fhandle; struct nfs3_createdata *data;
struct nfs_fattr fattr, dir_attr;
struct nfs3_mkdirargs arg = {
.fh = NFS_FH(dir),
.name = dentry->d_name.name,
.len = dentry->d_name.len,
.sattr = sattr
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
.fh = &fhandle,
.fattr = &fattr
};
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int mode = sattr->ia_mode; int mode = sattr->ia_mode;
int status; int status = -ENOMEM;
dprintk("NFS call mkdir %s\n", dentry->d_name.name); dprintk("NFS call mkdir %s\n", dentry->d_name.name);
sattr->ia_mode &= ~current->fs->umask; sattr->ia_mode &= ~current->fs->umask;
nfs_fattr_init(&dir_attr); data = nfs3_alloc_createdata();
nfs_fattr_init(&fattr); if (data == NULL)
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0)
goto out; goto out;
status = nfs_instantiate(dentry, &fhandle, &fattr);
data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR];
data->arg.mkdir.fh = NFS_FH(dir);
data->arg.mkdir.name = dentry->d_name.name;
data->arg.mkdir.len = dentry->d_name.len;
data->arg.mkdir.sattr = sattr;
status = nfs3_do_create(dir, dentry, data);
if (status != 0) if (status != 0)
goto out; goto out;
status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode); status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out: out:
nfs3_free_createdata(data);
dprintk("NFS reply mkdir: %d\n", status); dprintk("NFS reply mkdir: %d\n", status);
return status; return status;
} }
...@@ -615,52 +632,50 @@ static int ...@@ -615,52 +632,50 @@ static int
nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
dev_t rdev) dev_t rdev)
{ {
struct nfs_fh fh; struct nfs3_createdata *data;
struct nfs_fattr fattr, dir_attr;
struct nfs3_mknodargs arg = {
.fh = NFS_FH(dir),
.name = dentry->d_name.name,
.len = dentry->d_name.len,
.sattr = sattr,
.rdev = rdev
};
struct nfs3_diropres res = {
.dir_attr = &dir_attr,
.fh = &fh,
.fattr = &fattr
};
struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_MKNOD],
.rpc_argp = &arg,
.rpc_resp = &res,
};
mode_t mode = sattr->ia_mode; mode_t mode = sattr->ia_mode;
int status; int status = -ENOMEM;
switch (sattr->ia_mode & S_IFMT) {
case S_IFBLK: arg.type = NF3BLK; break;
case S_IFCHR: arg.type = NF3CHR; break;
case S_IFIFO: arg.type = NF3FIFO; break;
case S_IFSOCK: arg.type = NF3SOCK; break;
default: return -EINVAL;
}
dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name, dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name,
MAJOR(rdev), MINOR(rdev)); MAJOR(rdev), MINOR(rdev));
sattr->ia_mode &= ~current->fs->umask; sattr->ia_mode &= ~current->fs->umask;
nfs_fattr_init(&dir_attr); data = nfs3_alloc_createdata();
nfs_fattr_init(&fattr); if (data == NULL)
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0)
goto out; goto out;
status = nfs_instantiate(dentry, &fh, &fattr);
data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKNOD];
data->arg.mknod.fh = NFS_FH(dir);
data->arg.mknod.name = dentry->d_name.name;
data->arg.mknod.len = dentry->d_name.len;
data->arg.mknod.sattr = sattr;
data->arg.mknod.rdev = rdev;
switch (sattr->ia_mode & S_IFMT) {
case S_IFBLK:
data->arg.mknod.type = NF3BLK;
break;
case S_IFCHR:
data->arg.mknod.type = NF3CHR;
break;
case S_IFIFO:
data->arg.mknod.type = NF3FIFO;
break;
case S_IFSOCK:
data->arg.mknod.type = NF3SOCK;
break;
default:
status = -EINVAL;
goto out;
}
status = nfs3_do_create(dir, dentry, data);
if (status != 0) if (status != 0)
goto out; goto out;
status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode); status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
out: out:
nfs3_free_createdata(data);
dprintk("NFS reply mknod: %d\n", status); dprintk("NFS reply mknod: %d\n", status);
return status; return status;
} }
...@@ -801,8 +816,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = { ...@@ -801,8 +816,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.write_done = nfs3_write_done, .write_done = nfs3_write_done,
.commit_setup = nfs3_proc_commit_setup, .commit_setup = nfs3_proc_commit_setup,
.commit_done = nfs3_commit_done, .commit_done = nfs3_commit_done,
.file_open = nfs_open,
.file_release = nfs_release,
.lock = nfs3_proc_lock, .lock = nfs3_proc_lock,
.clear_acl_cache = nfs3_forget_cached_acls, .clear_acl_cache = nfs3_forget_cached_acls,
}; };
...@@ -451,9 +451,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) ...@@ -451,9 +451,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
/* Save the delegation */ /* Save the delegation */
memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data)); memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
rcu_read_unlock(); rcu_read_unlock();
lock_kernel();
ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode); ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
unlock_kernel();
if (ret != 0) if (ret != 0)
goto out; goto out;
ret = -EAGAIN; ret = -EAGAIN;
...@@ -1139,8 +1137,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int ...@@ -1139,8 +1137,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int
return res; return res;
} }
static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct iattr *sattr, struct nfs4_state *state) struct nfs_fattr *fattr, struct iattr *sattr,
struct nfs4_state *state)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs_setattrargs arg = { struct nfs_setattrargs arg = {
...@@ -1154,9 +1153,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, ...@@ -1154,9 +1153,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
.server = server, .server = server,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
.rpc_cred = cred,
}; };
unsigned long timestamp = jiffies; unsigned long timestamp = jiffies;
int status; int status;
...@@ -1166,7 +1166,6 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, ...@@ -1166,7 +1166,6 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) { if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) {
/* Use that stateid */ /* Use that stateid */
} else if (state != NULL) { } else if (state != NULL) {
msg.rpc_cred = state->owner->so_cred;
nfs4_copy_stateid(&arg.stateid, state, current->files); nfs4_copy_stateid(&arg.stateid, state, current->files);
} else } else
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
...@@ -1177,15 +1176,16 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, ...@@ -1177,15 +1176,16 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
return status; return status;
} }
static int nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr, static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct iattr *sattr, struct nfs4_state *state) struct nfs_fattr *fattr, struct iattr *sattr,
struct nfs4_state *state)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
int err; int err;
do { do {
err = nfs4_handle_exception(server, err = nfs4_handle_exception(server,
_nfs4_do_setattr(inode, fattr, sattr, state), _nfs4_do_setattr(inode, cred, fattr, sattr, state),
&exception); &exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
...@@ -1647,29 +1647,25 @@ static int ...@@ -1647,29 +1647,25 @@ static int
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
struct iattr *sattr) struct iattr *sattr)
{ {
struct rpc_cred *cred;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct nfs_open_context *ctx; struct rpc_cred *cred = NULL;
struct nfs4_state *state = NULL; struct nfs4_state *state = NULL;
int status; int status;
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
cred = rpc_lookup_cred();
if (IS_ERR(cred))
return PTR_ERR(cred);
/* Search for an existing open(O_WRITE) file */ /* Search for an existing open(O_WRITE) file */
ctx = nfs_find_open_context(inode, cred, FMODE_WRITE); if (sattr->ia_valid & ATTR_FILE) {
if (ctx != NULL) struct nfs_open_context *ctx;
ctx = nfs_file_open_context(sattr->ia_file);
cred = ctx->cred;
state = ctx->state; state = ctx->state;
}
status = nfs4_do_setattr(inode, fattr, sattr, state); status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
if (status == 0) if (status == 0)
nfs_setattr_update_inode(inode, sattr); nfs_setattr_update_inode(inode, sattr);
if (ctx != NULL)
put_nfs_open_context(ctx);
put_rpccred(cred);
return status; return status;
} }
...@@ -1897,17 +1893,16 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, ...@@ -1897,17 +1893,16 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
goto out; goto out;
} }
state = nfs4_do_open(dir, &path, flags, sattr, cred); state = nfs4_do_open(dir, &path, flags, sattr, cred);
put_rpccred(cred);
d_drop(dentry); d_drop(dentry);
if (IS_ERR(state)) { if (IS_ERR(state)) {
status = PTR_ERR(state); status = PTR_ERR(state);
goto out; goto out_putcred;
} }
d_add(dentry, igrab(state->inode)); d_add(dentry, igrab(state->inode));
nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
if (flags & O_EXCL) { if (flags & O_EXCL) {
struct nfs_fattr fattr; struct nfs_fattr fattr;
status = nfs4_do_setattr(state->inode, &fattr, sattr, state); status = nfs4_do_setattr(state->inode, cred, &fattr, sattr, state);
if (status == 0) if (status == 0)
nfs_setattr_update_inode(state->inode, sattr); nfs_setattr_update_inode(state->inode, sattr);
nfs_post_op_update_inode(state->inode, &fattr); nfs_post_op_update_inode(state->inode, &fattr);
...@@ -1916,6 +1911,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, ...@@ -1916,6 +1911,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
status = nfs4_intent_set_file(nd, &path, state); status = nfs4_intent_set_file(nd, &path, state);
else else
nfs4_close_sync(&path, state, flags); nfs4_close_sync(&path, state, flags);
out_putcred:
put_rpccred(cred);
out: out:
return status; return status;
} }
...@@ -2079,47 +2076,81 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n ...@@ -2079,47 +2076,81 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n
return err; return err;
} }
struct nfs4_createdata {
struct rpc_message msg;
struct nfs4_create_arg arg;
struct nfs4_create_res res;
struct nfs_fh fh;
struct nfs_fattr fattr;
struct nfs_fattr dir_fattr;
};
static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
struct qstr *name, struct iattr *sattr, u32 ftype)
{
struct nfs4_createdata *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data != NULL) {
struct nfs_server *server = NFS_SERVER(dir);
data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE];
data->msg.rpc_argp = &data->arg;
data->msg.rpc_resp = &data->res;
data->arg.dir_fh = NFS_FH(dir);
data->arg.server = server;
data->arg.name = name;
data->arg.attrs = sattr;
data->arg.ftype = ftype;
data->arg.bitmask = server->attr_bitmask;
data->res.server = server;
data->res.fh = &data->fh;
data->res.fattr = &data->fattr;
data->res.dir_fattr = &data->dir_fattr;
nfs_fattr_init(data->res.fattr);
nfs_fattr_init(data->res.dir_fattr);
}
return data;
}
static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
{
int status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
if (status == 0) {
update_changeattr(dir, &data->res.dir_cinfo);
nfs_post_op_update_inode(dir, data->res.dir_fattr);
status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
}
return status;
}
static void nfs4_free_createdata(struct nfs4_createdata *data)
{
kfree(data);
}
static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
struct page *page, unsigned int len, struct iattr *sattr) struct page *page, unsigned int len, struct iattr *sattr)
{ {
struct nfs_server *server = NFS_SERVER(dir); struct nfs4_createdata *data;
struct nfs_fh fhandle; int status = -ENAMETOOLONG;
struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
.name = &dentry->d_name,
.attrs = sattr,
.ftype = NF4LNK,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = server,
.fh = &fhandle,
.fattr = &fattr,
.dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
if (len > NFS4_MAXPATHLEN) if (len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG; goto out;
arg.u.symlink.pages = &page; status = -ENOMEM;
arg.u.symlink.len = len; data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4LNK);
nfs_fattr_init(&fattr); if (data == NULL)
nfs_fattr_init(&dir_fattr); goto out;
data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK];
data->arg.u.symlink.pages = &page;
data->arg.u.symlink.len = len;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = nfs4_do_create(dir, dentry, data);
if (!status) {
update_changeattr(dir, &res.dir_cinfo); nfs4_free_createdata(data);
nfs_post_op_update_inode(dir, res.dir_fattr); out:
status = nfs_instantiate(dentry, &fhandle, &fattr);
}
return status; return status;
} }
...@@ -2140,39 +2171,17 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, ...@@ -2140,39 +2171,17 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
struct iattr *sattr) struct iattr *sattr)
{ {
struct nfs_server *server = NFS_SERVER(dir); struct nfs4_createdata *data;
struct nfs_fh fhandle; int status = -ENOMEM;
struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
.name = &dentry->d_name,
.attrs = sattr,
.ftype = NF4DIR,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = server,
.fh = &fhandle,
.fattr = &fattr,
.dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
nfs_fattr_init(&fattr); data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4DIR);
nfs_fattr_init(&dir_fattr); if (data == NULL)
goto out;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status) { status = nfs4_do_create(dir, dentry, data);
update_changeattr(dir, &res.dir_cinfo);
nfs_post_op_update_inode(dir, res.dir_fattr); nfs4_free_createdata(data);
status = nfs_instantiate(dentry, &fhandle, &fattr); out:
}
return status; return status;
} }
...@@ -2242,56 +2251,34 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, ...@@ -2242,56 +2251,34 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
struct iattr *sattr, dev_t rdev) struct iattr *sattr, dev_t rdev)
{ {
struct nfs_server *server = NFS_SERVER(dir); struct nfs4_createdata *data;
struct nfs_fh fh; int mode = sattr->ia_mode;
struct nfs_fattr fattr, dir_fattr; int status = -ENOMEM;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
.name = &dentry->d_name,
.attrs = sattr,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = server,
.fh = &fh,
.fattr = &fattr,
.dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
int mode = sattr->ia_mode;
nfs_fattr_init(&fattr);
nfs_fattr_init(&dir_fattr);
BUG_ON(!(sattr->ia_valid & ATTR_MODE)); BUG_ON(!(sattr->ia_valid & ATTR_MODE));
BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK);
if (data == NULL)
goto out;
if (S_ISFIFO(mode)) if (S_ISFIFO(mode))
arg.ftype = NF4FIFO; data->arg.ftype = NF4FIFO;
else if (S_ISBLK(mode)) { else if (S_ISBLK(mode)) {
arg.ftype = NF4BLK; data->arg.ftype = NF4BLK;
arg.u.device.specdata1 = MAJOR(rdev); data->arg.u.device.specdata1 = MAJOR(rdev);
arg.u.device.specdata2 = MINOR(rdev); data->arg.u.device.specdata2 = MINOR(rdev);
} }
else if (S_ISCHR(mode)) { else if (S_ISCHR(mode)) {
arg.ftype = NF4CHR; data->arg.ftype = NF4CHR;
arg.u.device.specdata1 = MAJOR(rdev); data->arg.u.device.specdata1 = MAJOR(rdev);
arg.u.device.specdata2 = MINOR(rdev); data->arg.u.device.specdata2 = MINOR(rdev);
} }
else
arg.ftype = NF4SOCK;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = nfs4_do_create(dir, dentry, data);
if (status == 0) {
update_changeattr(dir, &res.dir_cinfo); nfs4_free_createdata(data);
nfs_post_op_update_inode(dir, res.dir_fattr); out:
status = nfs_instantiate(dentry, &fh, &fattr);
}
return status; return status;
} }
...@@ -2706,6 +2693,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen) ...@@ -2706,6 +2693,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
ret = nfs_revalidate_inode(server, inode); ret = nfs_revalidate_inode(server, inode);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
nfs_zap_acl_cache(inode);
ret = nfs4_read_cached_acl(inode, buf, buflen); ret = nfs4_read_cached_acl(inode, buf, buflen);
if (ret != -ENOENT) if (ret != -ENOENT)
return ret; return ret;
...@@ -2733,7 +2722,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl ...@@ -2733,7 +2722,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
nfs_inode_return_delegation(inode); nfs_inode_return_delegation(inode);
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_zap_caches(inode); nfs_access_zap_cache(inode);
nfs_zap_acl_cache(inode);
return ret; return ret;
} }
...@@ -2767,8 +2757,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server) ...@@ -2767,8 +2757,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
task->tk_status = 0; task->tk_status = 0;
return -EAGAIN; return -EAGAIN;
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
nfs_inc_server_stats((struct nfs_server *) server, nfs_inc_server_stats(server, NFSIOS_DELAY);
NFSIOS_DELAY);
case -NFS4ERR_GRACE: case -NFS4ERR_GRACE:
rpc_delay(task, NFS4_POLL_RETRY_MAX); rpc_delay(task, NFS4_POLL_RETRY_MAX);
task->tk_status = 0; task->tk_status = 0;
...@@ -2933,7 +2922,7 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre ...@@ -2933,7 +2922,7 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre
int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred) int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
{ {
long timeout; long timeout = 0;
int err; int err;
do { do {
err = _nfs4_proc_setclientid_confirm(clp, cred); err = _nfs4_proc_setclientid_confirm(clp, cred);
...@@ -3725,8 +3714,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = { ...@@ -3725,8 +3714,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.write_done = nfs4_write_done, .write_done = nfs4_write_done,
.commit_setup = nfs4_proc_commit_setup, .commit_setup = nfs4_proc_commit_setup,
.commit_done = nfs4_commit_done, .commit_done = nfs4_commit_done,
.file_open = nfs_open,
.file_release = nfs_release,
.lock = nfs4_proc_lock, .lock = nfs4_proc_lock,
.clear_acl_cache = nfs4_zap_acl_attr, .clear_acl_cache = nfs4_zap_acl_attr,
}; };
......
...@@ -940,7 +940,6 @@ static int reclaimer(void *ptr) ...@@ -940,7 +940,6 @@ static int reclaimer(void *ptr)
allow_signal(SIGKILL); allow_signal(SIGKILL);
/* Ensure exclusive access to NFSv4 state */ /* Ensure exclusive access to NFSv4 state */
lock_kernel();
down_write(&clp->cl_sem); down_write(&clp->cl_sem);
/* Are there any NFS mounts out there? */ /* Are there any NFS mounts out there? */
if (list_empty(&clp->cl_superblocks)) if (list_empty(&clp->cl_superblocks))
...@@ -1000,7 +999,6 @@ static int reclaimer(void *ptr) ...@@ -1000,7 +999,6 @@ static int reclaimer(void *ptr)
nfs_delegation_reap_unclaimed(clp); nfs_delegation_reap_unclaimed(clp);
out: out:
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
unlock_kernel();
if (status == -NFS4ERR_CB_PATH_DOWN) if (status == -NFS4ERR_CB_PATH_DOWN)
nfs_handle_cb_pathdown(clp); nfs_handle_cb_pathdown(clp);
nfs4_clear_recover_bit(clp); nfs4_clear_recover_bit(clp);
......
/* /*
* $Id: nfsroot.c,v 1.45 1998/03/07 10:44:46 mj Exp $
*
* Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de> * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
* *
* Allow an NFS filesystem to be mounted as root. The way this works is: * Allow an NFS filesystem to be mounted as root. The way this works is:
...@@ -297,10 +295,10 @@ static int __init root_nfs_name(char *name) ...@@ -297,10 +295,10 @@ static int __init root_nfs_name(char *name)
nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */ nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */
nfs_data.rsize = NFS_DEF_FILE_IO_SIZE; nfs_data.rsize = NFS_DEF_FILE_IO_SIZE;
nfs_data.wsize = NFS_DEF_FILE_IO_SIZE; nfs_data.wsize = NFS_DEF_FILE_IO_SIZE;
nfs_data.acregmin = 3; nfs_data.acregmin = NFS_DEF_ACREGMIN;
nfs_data.acregmax = 60; nfs_data.acregmax = NFS_DEF_ACREGMAX;
nfs_data.acdirmin = 30; nfs_data.acdirmin = NFS_DEF_ACDIRMIN;
nfs_data.acdirmax = 60; nfs_data.acdirmax = NFS_DEF_ACDIRMAX;
strcpy(buf, NFS_ROOT); strcpy(buf, NFS_ROOT);
/* Process options received from the remote server */ /* Process options received from the remote server */
......
...@@ -129,6 +129,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -129,6 +129,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
sattr->ia_mode &= S_IALLUGO; sattr->ia_mode &= S_IALLUGO;
dprintk("NFS call setattr\n"); dprintk("NFS call setattr\n");
if (sattr->ia_valid & ATTR_FILE)
msg.rpc_cred = nfs_file_cred(sattr->ia_file);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status == 0) if (status == 0)
...@@ -598,6 +600,29 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) ...@@ -598,6 +600,29 @@ nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
} }
/* Helper functions for NFS lock bounds checking */
#define NFS_LOCK32_OFFSET_MAX ((__s32)0x7fffffffUL)
static int nfs_lock_check_bounds(const struct file_lock *fl)
{
__s32 start, end;
start = (__s32)fl->fl_start;
if ((loff_t)start != fl->fl_start)
goto out_einval;
if (fl->fl_end != OFFSET_MAX) {
end = (__s32)fl->fl_end;
if ((loff_t)end != fl->fl_end)
goto out_einval;
} else
end = NFS_LOCK32_OFFSET_MAX;
if (start < 0 || start > end)
goto out_einval;
return 0;
out_einval:
return -EINVAL;
}
const struct nfs_rpc_ops nfs_v2_clientops = { const struct nfs_rpc_ops nfs_v2_clientops = {
.version = 2, /* protocol version */ .version = 2, /* protocol version */
...@@ -630,7 +655,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = { ...@@ -630,7 +655,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.write_setup = nfs_proc_write_setup, .write_setup = nfs_proc_write_setup,
.write_done = nfs_write_done, .write_done = nfs_write_done,
.commit_setup = nfs_proc_commit_setup, .commit_setup = nfs_proc_commit_setup,
.file_open = nfs_open,
.file_release = nfs_release,
.lock = nfs_proc_lock, .lock = nfs_proc_lock,
.lock_check_bounds = nfs_lock_check_bounds,
}; };
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <linux/netdevice.h>
#include <linux/nfs_xdr.h> #include <linux/nfs_xdr.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/parser.h> #include <linux/parser.h>
...@@ -65,7 +66,6 @@ ...@@ -65,7 +66,6 @@
enum { enum {
/* Mount options that take no arguments */ /* Mount options that take no arguments */
Opt_soft, Opt_hard, Opt_soft, Opt_hard,
Opt_intr, Opt_nointr,
Opt_posix, Opt_noposix, Opt_posix, Opt_noposix,
Opt_cto, Opt_nocto, Opt_cto, Opt_nocto,
Opt_ac, Opt_noac, Opt_ac, Opt_noac,
...@@ -92,8 +92,8 @@ enum { ...@@ -92,8 +92,8 @@ enum {
Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost, Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
Opt_addr, Opt_mountaddr, Opt_clientaddr, Opt_addr, Opt_mountaddr, Opt_clientaddr,
/* Mount options that are ignored */ /* Special mount options */
Opt_userspace, Opt_deprecated, Opt_userspace, Opt_deprecated, Opt_sloppy,
Opt_err Opt_err
}; };
...@@ -101,10 +101,14 @@ enum { ...@@ -101,10 +101,14 @@ enum {
static match_table_t nfs_mount_option_tokens = { static match_table_t nfs_mount_option_tokens = {
{ Opt_userspace, "bg" }, { Opt_userspace, "bg" },
{ Opt_userspace, "fg" }, { Opt_userspace, "fg" },
{ Opt_userspace, "retry=%s" },
{ Opt_sloppy, "sloppy" },
{ Opt_soft, "soft" }, { Opt_soft, "soft" },
{ Opt_hard, "hard" }, { Opt_hard, "hard" },
{ Opt_intr, "intr" }, { Opt_deprecated, "intr" },
{ Opt_nointr, "nointr" }, { Opt_deprecated, "nointr" },
{ Opt_posix, "posix" }, { Opt_posix, "posix" },
{ Opt_noposix, "noposix" }, { Opt_noposix, "noposix" },
{ Opt_cto, "cto" }, { Opt_cto, "cto" },
...@@ -136,7 +140,6 @@ static match_table_t nfs_mount_option_tokens = { ...@@ -136,7 +140,6 @@ static match_table_t nfs_mount_option_tokens = {
{ Opt_acdirmin, "acdirmin=%u" }, { Opt_acdirmin, "acdirmin=%u" },
{ Opt_acdirmax, "acdirmax=%u" }, { Opt_acdirmax, "acdirmax=%u" },
{ Opt_actimeo, "actimeo=%u" }, { Opt_actimeo, "actimeo=%u" },
{ Opt_userspace, "retry=%u" },
{ Opt_namelen, "namlen=%u" }, { Opt_namelen, "namlen=%u" },
{ Opt_mountport, "mountport=%u" }, { Opt_mountport, "mountport=%u" },
{ Opt_mountvers, "mountvers=%u" }, { Opt_mountvers, "mountvers=%u" },
...@@ -207,6 +210,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, ...@@ -207,6 +210,7 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
static void nfs_kill_super(struct super_block *); static void nfs_kill_super(struct super_block *);
static void nfs_put_super(struct super_block *); static void nfs_put_super(struct super_block *);
static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
static struct file_system_type nfs_fs_type = { static struct file_system_type nfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -234,6 +238,7 @@ static const struct super_operations nfs_sops = { ...@@ -234,6 +238,7 @@ static const struct super_operations nfs_sops = {
.umount_begin = nfs_umount_begin, .umount_begin = nfs_umount_begin,
.show_options = nfs_show_options, .show_options = nfs_show_options,
.show_stats = nfs_show_stats, .show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
}; };
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
...@@ -278,6 +283,7 @@ static const struct super_operations nfs4_sops = { ...@@ -278,6 +283,7 @@ static const struct super_operations nfs4_sops = {
.umount_begin = nfs_umount_begin, .umount_begin = nfs_umount_begin,
.show_options = nfs_show_options, .show_options = nfs_show_options,
.show_stats = nfs_show_stats, .show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
}; };
#endif #endif
...@@ -368,8 +374,6 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -368,8 +374,6 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
}; };
int error; int error;
lock_kernel();
error = server->nfs_client->rpc_ops->statfs(server, fh, &res); error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
if (error < 0) if (error < 0)
goto out_err; goto out_err;
...@@ -401,12 +405,10 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -401,12 +405,10 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_namelen = server->namelen; buf->f_namelen = server->namelen;
unlock_kernel();
return 0; return 0;
out_err: out_err:
dprintk("%s: statfs error = %d\n", __func__, -error); dprintk("%s: statfs error = %d\n", __func__, -error);
unlock_kernel();
return error; return error;
} }
...@@ -514,13 +516,13 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, ...@@ -514,13 +516,13 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
if (nfss->bsize != 0) if (nfss->bsize != 0)
seq_printf(m, ",bsize=%u", nfss->bsize); seq_printf(m, ",bsize=%u", nfss->bsize);
seq_printf(m, ",namlen=%u", nfss->namelen); seq_printf(m, ",namlen=%u", nfss->namelen);
if (nfss->acregmin != 3*HZ || showdefaults) if (nfss->acregmin != NFS_DEF_ACREGMIN*HZ || showdefaults)
seq_printf(m, ",acregmin=%u", nfss->acregmin/HZ); seq_printf(m, ",acregmin=%u", nfss->acregmin/HZ);
if (nfss->acregmax != 60*HZ || showdefaults) if (nfss->acregmax != NFS_DEF_ACREGMAX*HZ || showdefaults)
seq_printf(m, ",acregmax=%u", nfss->acregmax/HZ); seq_printf(m, ",acregmax=%u", nfss->acregmax/HZ);
if (nfss->acdirmin != 30*HZ || showdefaults) if (nfss->acdirmin != NFS_DEF_ACDIRMIN*HZ || showdefaults)
seq_printf(m, ",acdirmin=%u", nfss->acdirmin/HZ); seq_printf(m, ",acdirmin=%u", nfss->acdirmin/HZ);
if (nfss->acdirmax != 60*HZ || showdefaults) if (nfss->acdirmax != NFS_DEF_ACDIRMAX*HZ || showdefaults)
seq_printf(m, ",acdirmax=%u", nfss->acdirmax/HZ); seq_printf(m, ",acdirmax=%u", nfss->acdirmax/HZ);
for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) { for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
if (nfss->flags & nfs_infop->flag) if (nfss->flags & nfs_infop->flag)
...@@ -702,49 +704,233 @@ static int nfs_verify_server_address(struct sockaddr *addr) ...@@ -702,49 +704,233 @@ static int nfs_verify_server_address(struct sockaddr *addr)
return 0; return 0;
} }
static void nfs_parse_ipv4_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sap;
u8 *addr = (u8 *)&sin->sin_addr.s_addr;
if (str_len <= INET_ADDRSTRLEN) {
dfprintk(MOUNT, "NFS: parsing IPv4 address %*s\n",
(int)str_len, string);
sin->sin_family = AF_INET;
*addr_len = sizeof(*sin);
if (in4_pton(string, str_len, addr, '\0', NULL))
return;
}
sap->sa_family = AF_UNSPEC;
*addr_len = 0;
}
#define IPV6_SCOPE_DELIMITER '%'
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
const char *delim,
struct sockaddr_in6 *sin6)
{
char *p;
size_t len;
if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
return ;
if (*delim != IPV6_SCOPE_DELIMITER)
return;
len = (string + str_len) - delim - 1;
p = kstrndup(delim + 1, len, GFP_KERNEL);
if (p) {
unsigned long scope_id = 0;
struct net_device *dev;
dev = dev_get_by_name(&init_net, p);
if (dev != NULL) {
scope_id = dev->ifindex;
dev_put(dev);
} else {
/* scope_id is set to zero on error */
strict_strtoul(p, 10, &scope_id);
}
kfree(p);
sin6->sin6_scope_id = scope_id;
dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
}
}
static void nfs_parse_ipv6_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
const char *delim;
if (str_len <= INET6_ADDRSTRLEN) {
dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n",
(int)str_len, string);
sin6->sin6_family = AF_INET6;
*addr_len = sizeof(*sin6);
if (in6_pton(string, str_len, addr, IPV6_SCOPE_DELIMITER, &delim)) {
nfs_parse_ipv6_scope_id(string, str_len, delim, sin6);
return;
}
}
sap->sa_family = AF_UNSPEC;
*addr_len = 0;
}
#else
static void nfs_parse_ipv6_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
sap->sa_family = AF_UNSPEC;
*addr_len = 0;
}
#endif
/* /*
* Parse string addresses passed in via a mount option, * Construct a sockaddr based on the contents of a string that contains
* and construct a sockaddr based on the result. * an IP address in presentation format.
* *
* If address parsing fails, set the sockaddr's address * If there is a problem constructing the new sockaddr, set the address
* family to AF_UNSPEC to force nfs_verify_server_address() * family to AF_UNSPEC.
* to punt the mount.
*/ */
static void nfs_parse_server_address(char *value, static void nfs_parse_ip_address(char *string, size_t str_len,
struct sockaddr *sap, struct sockaddr *sap, size_t *addr_len)
size_t *len)
{ {
if (strchr(value, ':')) { unsigned int i, colons;
struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
u8 *addr = (u8 *)&ap->sin6_addr.in6_u;
ap->sin6_family = AF_INET6; colons = 0;
*len = sizeof(*ap); for (i = 0; i < str_len; i++)
if (in6_pton(value, -1, addr, '\0', NULL)) if (string[i] == ':')
return; colons++;
} else {
struct sockaddr_in *ap = (struct sockaddr_in *)sap; if (colons >= 2)
u8 *addr = (u8 *)&ap->sin_addr.s_addr; nfs_parse_ipv6_address(string, str_len, sap, addr_len);
else
nfs_parse_ipv4_address(string, str_len, sap, addr_len);
}
/*
* Sanity check the NFS transport protocol.
*
*/
static void nfs_validate_transport_protocol(struct nfs_parsed_mount_data *mnt)
{
switch (mnt->nfs_server.protocol) {
case XPRT_TRANSPORT_UDP:
case XPRT_TRANSPORT_TCP:
case XPRT_TRANSPORT_RDMA:
break;
default:
mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
}
}
/*
* For text based NFSv2/v3 mounts, the mount protocol transport default
* settings should depend upon the specified NFS transport.
*/
static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)
{
nfs_validate_transport_protocol(mnt);
ap->sin_family = AF_INET; if (mnt->mount_server.protocol == XPRT_TRANSPORT_UDP ||
*len = sizeof(*ap); mnt->mount_server.protocol == XPRT_TRANSPORT_TCP)
if (in4_pton(value, -1, addr, '\0', NULL))
return; return;
switch (mnt->nfs_server.protocol) {
case XPRT_TRANSPORT_UDP:
mnt->mount_server.protocol = XPRT_TRANSPORT_UDP;
break;
case XPRT_TRANSPORT_TCP:
case XPRT_TRANSPORT_RDMA:
mnt->mount_server.protocol = XPRT_TRANSPORT_TCP;
} }
}
sap->sa_family = AF_UNSPEC; /*
*len = 0; * Parse the value of the 'sec=' option.
*
* The flavor_len setting is for v4 mounts.
*/
static int nfs_parse_security_flavors(char *value,
struct nfs_parsed_mount_data *mnt)
{
substring_t args[MAX_OPT_ARGS];
dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
switch (match_token(value, nfs_secflavor_tokens, args)) {
case Opt_sec_none:
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_NULL;
break;
case Opt_sec_sys:
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_UNIX;
break;
case Opt_sec_krb5:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
break;
case Opt_sec_krb5i:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
break;
case Opt_sec_krb5p:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
break;
case Opt_sec_lkey:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
break;
case Opt_sec_lkeyi:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
break;
case Opt_sec_lkeyp:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
break;
case Opt_sec_spkm:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
break;
case Opt_sec_spkmi:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
break;
case Opt_sec_spkmp:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
break;
default:
return 0;
}
return 1;
}
static void nfs_parse_invalid_value(const char *option)
{
dfprintk(MOUNT, "NFS: bad value specified for %s option\n", option);
} }
/* /*
* Error-check and convert a string of mount options from user space into * Error-check and convert a string of mount options from user space into
* a data structure * a data structure. The whole mount string is processed; bad options are
* skipped as they are encountered. If there were no errors, return 1;
* otherwise return 0 (zero).
*/ */
static int nfs_parse_mount_options(char *raw, static int nfs_parse_mount_options(char *raw,
struct nfs_parsed_mount_data *mnt) struct nfs_parsed_mount_data *mnt)
{ {
char *p, *string, *secdata; char *p, *string, *secdata;
int rc; int rc, sloppy = 0, errors = 0;
if (!raw) { if (!raw) {
dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
...@@ -777,15 +963,16 @@ static int nfs_parse_mount_options(char *raw, ...@@ -777,15 +963,16 @@ static int nfs_parse_mount_options(char *raw,
token = match_token(p, nfs_mount_option_tokens, args); token = match_token(p, nfs_mount_option_tokens, args);
switch (token) { switch (token) {
/*
* boolean options: foo/nofoo
*/
case Opt_soft: case Opt_soft:
mnt->flags |= NFS_MOUNT_SOFT; mnt->flags |= NFS_MOUNT_SOFT;
break; break;
case Opt_hard: case Opt_hard:
mnt->flags &= ~NFS_MOUNT_SOFT; mnt->flags &= ~NFS_MOUNT_SOFT;
break; break;
case Opt_intr:
case Opt_nointr:
break;
case Opt_posix: case Opt_posix:
mnt->flags |= NFS_MOUNT_POSIX; mnt->flags |= NFS_MOUNT_POSIX;
break; break;
...@@ -819,20 +1006,14 @@ static int nfs_parse_mount_options(char *raw, ...@@ -819,20 +1006,14 @@ static int nfs_parse_mount_options(char *raw,
case Opt_udp: case Opt_udp:
mnt->flags &= ~NFS_MOUNT_TCP; mnt->flags &= ~NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
mnt->timeo = 7;
mnt->retrans = 5;
break; break;
case Opt_tcp: case Opt_tcp:
mnt->flags |= NFS_MOUNT_TCP; mnt->flags |= NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
mnt->timeo = 600;
mnt->retrans = 2;
break; break;
case Opt_rdma: case Opt_rdma:
mnt->flags |= NFS_MOUNT_TCP; /* for side protocols */ mnt->flags |= NFS_MOUNT_TCP; /* for side protocols */
mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
mnt->timeo = 600;
mnt->retrans = 2;
break; break;
case Opt_acl: case Opt_acl:
mnt->flags &= ~NFS_MOUNT_NOACL; mnt->flags &= ~NFS_MOUNT_NOACL;
...@@ -853,165 +1034,144 @@ static int nfs_parse_mount_options(char *raw, ...@@ -853,165 +1034,144 @@ static int nfs_parse_mount_options(char *raw,
mnt->flags |= NFS_MOUNT_UNSHARED; mnt->flags |= NFS_MOUNT_UNSHARED;
break; break;
/*
* options that take numeric values
*/
case Opt_port: case Opt_port:
if (match_int(args, &option)) if (match_int(args, &option) ||
return 0; option < 0 || option > USHORT_MAX) {
if (option < 0 || option > 65535) errors++;
return 0; nfs_parse_invalid_value("port");
mnt->nfs_server.port = option; } else
mnt->nfs_server.port = option;
break; break;
case Opt_rsize: case Opt_rsize:
if (match_int(args, &mnt->rsize)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("rsize");
} else
mnt->rsize = option;
break; break;
case Opt_wsize: case Opt_wsize:
if (match_int(args, &mnt->wsize)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("wsize");
} else
mnt->wsize = option;
break; break;
case Opt_bsize: case Opt_bsize:
if (match_int(args, &option)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
if (option < 0) nfs_parse_invalid_value("bsize");
return 0; } else
mnt->bsize = option; mnt->bsize = option;
break; break;
case Opt_timeo: case Opt_timeo:
if (match_int(args, &mnt->timeo)) if (match_int(args, &option) || option <= 0) {
return 0; errors++;
nfs_parse_invalid_value("timeo");
} else
mnt->timeo = option;
break; break;
case Opt_retrans: case Opt_retrans:
if (match_int(args, &mnt->retrans)) if (match_int(args, &option) || option <= 0) {
return 0; errors++;
nfs_parse_invalid_value("retrans");
} else
mnt->retrans = option;
break; break;
case Opt_acregmin: case Opt_acregmin:
if (match_int(args, &mnt->acregmin)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("acregmin");
} else
mnt->acregmin = option;
break; break;
case Opt_acregmax: case Opt_acregmax:
if (match_int(args, &mnt->acregmax)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("acregmax");
} else
mnt->acregmax = option;
break; break;
case Opt_acdirmin: case Opt_acdirmin:
if (match_int(args, &mnt->acdirmin)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("acdirmin");
} else
mnt->acdirmin = option;
break; break;
case Opt_acdirmax: case Opt_acdirmax:
if (match_int(args, &mnt->acdirmax)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("acdirmax");
} else
mnt->acdirmax = option;
break; break;
case Opt_actimeo: case Opt_actimeo:
if (match_int(args, &option)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
if (option < 0) nfs_parse_invalid_value("actimeo");
return 0; } else
mnt->acregmin = mnt->acregmin = mnt->acregmax =
mnt->acregmax = mnt->acdirmin = mnt->acdirmax = option;
mnt->acdirmin =
mnt->acdirmax = option;
break; break;
case Opt_namelen: case Opt_namelen:
if (match_int(args, &mnt->namlen)) if (match_int(args, &option) || option < 0) {
return 0; errors++;
nfs_parse_invalid_value("namlen");
} else
mnt->namlen = option;
break; break;
case Opt_mountport: case Opt_mountport:
if (match_int(args, &option)) if (match_int(args, &option) ||
return 0; option < 0 || option > USHORT_MAX) {
if (option < 0 || option > 65535) errors++;
return 0; nfs_parse_invalid_value("mountport");
mnt->mount_server.port = option; } else
mnt->mount_server.port = option;
break; break;
case Opt_mountvers: case Opt_mountvers:
if (match_int(args, &option)) if (match_int(args, &option) ||
return 0; option < NFS_MNT_VERSION ||
if (option < 0) option > NFS_MNT3_VERSION) {
return 0; errors++;
mnt->mount_server.version = option; nfs_parse_invalid_value("mountvers");
} else
mnt->mount_server.version = option;
break; break;
case Opt_nfsvers: case Opt_nfsvers:
if (match_int(args, &option)) if (match_int(args, &option)) {
return 0; errors++;
nfs_parse_invalid_value("nfsvers");
break;
}
switch (option) { switch (option) {
case 2: case NFS2_VERSION:
mnt->flags &= ~NFS_MOUNT_VER3; mnt->flags &= ~NFS_MOUNT_VER3;
break; break;
case 3: case NFS3_VERSION:
mnt->flags |= NFS_MOUNT_VER3; mnt->flags |= NFS_MOUNT_VER3;
break; break;
default: default:
goto out_unrec_vers; errors++;
nfs_parse_invalid_value("nfsvers");
} }
break; break;
/*
* options that take text values
*/
case Opt_sec: case Opt_sec:
string = match_strdup(args); string = match_strdup(args);
if (string == NULL) if (string == NULL)
goto out_nomem; goto out_nomem;
token = match_token(string, nfs_secflavor_tokens, args); rc = nfs_parse_security_flavors(string, mnt);
kfree(string); kfree(string);
if (!rc) {
/* errors++;
* The flags setting is for v2/v3. The flavor_len dfprintk(MOUNT, "NFS: unrecognized "
* setting is for v4. v2/v3 also need to know the "security flavor\n");
* difference between NULL and UNIX.
*/
switch (token) {
case Opt_sec_none:
mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_NULL;
break;
case Opt_sec_sys:
mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_UNIX;
break;
case Opt_sec_krb5:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
break;
case Opt_sec_krb5i:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
break;
case Opt_sec_krb5p:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
break;
case Opt_sec_lkey:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
break;
case Opt_sec_lkeyi:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
break;
case Opt_sec_lkeyp:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
break;
case Opt_sec_spkm:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
break;
case Opt_sec_spkmi:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
break;
case Opt_sec_spkmp:
mnt->flags |= NFS_MOUNT_SECFLAVOUR;
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
break;
default:
goto out_unrec_sec;
} }
break; break;
case Opt_proto: case Opt_proto:
...@@ -1026,24 +1186,20 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1026,24 +1186,20 @@ static int nfs_parse_mount_options(char *raw,
case Opt_xprt_udp: case Opt_xprt_udp:
mnt->flags &= ~NFS_MOUNT_TCP; mnt->flags &= ~NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP; mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
mnt->timeo = 7;
mnt->retrans = 5;
break; break;
case Opt_xprt_tcp: case Opt_xprt_tcp:
mnt->flags |= NFS_MOUNT_TCP; mnt->flags |= NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP; mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
mnt->timeo = 600;
mnt->retrans = 2;
break; break;
case Opt_xprt_rdma: case Opt_xprt_rdma:
/* vector side protocols to TCP */ /* vector side protocols to TCP */
mnt->flags |= NFS_MOUNT_TCP; mnt->flags |= NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
mnt->timeo = 600;
mnt->retrans = 2;
break; break;
default: default:
goto out_unrec_xprt; errors++;
dfprintk(MOUNT, "NFS: unrecognized "
"transport protocol\n");
} }
break; break;
case Opt_mountproto: case Opt_mountproto:
...@@ -1063,16 +1219,19 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1063,16 +1219,19 @@ static int nfs_parse_mount_options(char *raw,
break; break;
case Opt_xprt_rdma: /* not used for side protocols */ case Opt_xprt_rdma: /* not used for side protocols */
default: default:
goto out_unrec_xprt; errors++;
dfprintk(MOUNT, "NFS: unrecognized "
"transport protocol\n");
} }
break; break;
case Opt_addr: case Opt_addr:
string = match_strdup(args); string = match_strdup(args);
if (string == NULL) if (string == NULL)
goto out_nomem; goto out_nomem;
nfs_parse_server_address(string, (struct sockaddr *) nfs_parse_ip_address(string, strlen(string),
&mnt->nfs_server.address, (struct sockaddr *)
&mnt->nfs_server.addrlen); &mnt->nfs_server.address,
&mnt->nfs_server.addrlen);
kfree(string); kfree(string);
break; break;
case Opt_clientaddr: case Opt_clientaddr:
...@@ -1093,24 +1252,33 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1093,24 +1252,33 @@ static int nfs_parse_mount_options(char *raw,
string = match_strdup(args); string = match_strdup(args);
if (string == NULL) if (string == NULL)
goto out_nomem; goto out_nomem;
nfs_parse_server_address(string, (struct sockaddr *) nfs_parse_ip_address(string, strlen(string),
&mnt->mount_server.address, (struct sockaddr *)
&mnt->mount_server.addrlen); &mnt->mount_server.address,
&mnt->mount_server.addrlen);
kfree(string); kfree(string);
break; break;
/*
* Special options
*/
case Opt_sloppy:
sloppy = 1;
dfprintk(MOUNT, "NFS: relaxing parsing rules\n");
break;
case Opt_userspace: case Opt_userspace:
case Opt_deprecated: case Opt_deprecated:
dfprintk(MOUNT, "NFS: ignoring mount option "
"'%s'\n", p);
break; break;
default: default:
goto out_unknown; errors++;
dfprintk(MOUNT, "NFS: unrecognized mount option "
"'%s'\n", p);
} }
} }
nfs_set_port((struct sockaddr *)&mnt->nfs_server.address,
mnt->nfs_server.port);
return 1; return 1;
out_nomem: out_nomem:
...@@ -1120,21 +1288,6 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1120,21 +1288,6 @@ static int nfs_parse_mount_options(char *raw,
free_secdata(secdata); free_secdata(secdata);
printk(KERN_INFO "NFS: security options invalid: %d\n", rc); printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
return 0; return 0;
out_unrec_vers:
printk(KERN_INFO "NFS: unrecognized NFS version number\n");
return 0;
out_unrec_xprt:
printk(KERN_INFO "NFS: unrecognized transport protocol\n");
return 0;
out_unrec_sec:
printk(KERN_INFO "NFS: unrecognized security flavor\n");
return 0;
out_unknown:
printk(KERN_INFO "NFS: unknown mount option: %s\n", p);
return 0;
} }
/* /*
...@@ -1188,11 +1341,146 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args, ...@@ -1188,11 +1341,146 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
if (status == 0) if (status == 0)
return 0; return 0;
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d", dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
hostname, status); hostname, status);
return status; return status;
} }
static int nfs_parse_simple_hostname(const char *dev_name,
char **hostname, size_t maxnamlen,
char **export_path, size_t maxpathlen)
{
size_t len;
char *colon, *comma;
colon = strchr(dev_name, ':');
if (colon == NULL)
goto out_bad_devname;
len = colon - dev_name;
if (len > maxnamlen)
goto out_hostname;
/* N.B. caller will free nfs_server.hostname in all cases */
*hostname = kstrndup(dev_name, len, GFP_KERNEL);
if (!*hostname)
goto out_nomem;
/* kill possible hostname list: not supported */
comma = strchr(*hostname, ',');
if (comma != NULL) {
if (comma == *hostname)
goto out_bad_devname;
*comma = '\0';
}
colon++;
len = strlen(colon);
if (len > maxpathlen)
goto out_path;
*export_path = kstrndup(colon, len, GFP_KERNEL);
if (!*export_path)
goto out_nomem;
dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path);
return 0;
out_bad_devname:
dfprintk(MOUNT, "NFS: device name not in host:path format\n");
return -EINVAL;
out_nomem:
dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
return -ENOMEM;
out_hostname:
dfprintk(MOUNT, "NFS: server hostname too long\n");
return -ENAMETOOLONG;
out_path:
dfprintk(MOUNT, "NFS: export pathname too long\n");
return -ENAMETOOLONG;
}
/*
* Hostname has square brackets around it because it contains one or
* more colons. We look for the first closing square bracket, and a
* colon must follow it.
*/
static int nfs_parse_protected_hostname(const char *dev_name,
char **hostname, size_t maxnamlen,
char **export_path, size_t maxpathlen)
{
size_t len;
char *start, *end;
start = (char *)(dev_name + 1);
end = strchr(start, ']');
if (end == NULL)
goto out_bad_devname;
if (*(end + 1) != ':')
goto out_bad_devname;
len = end - start;
if (len > maxnamlen)
goto out_hostname;
/* N.B. caller will free nfs_server.hostname in all cases */
*hostname = kstrndup(start, len, GFP_KERNEL);
if (*hostname == NULL)
goto out_nomem;
end += 2;
len = strlen(end);
if (len > maxpathlen)
goto out_path;
*export_path = kstrndup(end, len, GFP_KERNEL);
if (!*export_path)
goto out_nomem;
return 0;
out_bad_devname:
dfprintk(MOUNT, "NFS: device name not in host:path format\n");
return -EINVAL;
out_nomem:
dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
return -ENOMEM;
out_hostname:
dfprintk(MOUNT, "NFS: server hostname too long\n");
return -ENAMETOOLONG;
out_path:
dfprintk(MOUNT, "NFS: export pathname too long\n");
return -ENAMETOOLONG;
}
/*
* Split "dev_name" into "hostname:export_path".
*
* The leftmost colon demarks the split between the server's hostname
* and the export path. If the hostname starts with a left square
* bracket, then it may contain colons.
*
* Note: caller frees hostname and export path, even on error.
*/
static int nfs_parse_devname(const char *dev_name,
char **hostname, size_t maxnamlen,
char **export_path, size_t maxpathlen)
{
if (*dev_name == '[')
return nfs_parse_protected_hostname(dev_name,
hostname, maxnamlen,
export_path, maxpathlen);
return nfs_parse_simple_hostname(dev_name,
hostname, maxnamlen,
export_path, maxpathlen);
}
/* /*
* Validate the NFS2/NFS3 mount data * Validate the NFS2/NFS3 mount data
* - fills in the mount root filehandle * - fills in the mount root filehandle
...@@ -1222,16 +1510,14 @@ static int nfs_validate_mount_data(void *options, ...@@ -1222,16 +1510,14 @@ static int nfs_validate_mount_data(void *options,
args->flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP); args->flags = (NFS_MOUNT_VER3 | NFS_MOUNT_TCP);
args->rsize = NFS_MAX_FILE_IO_SIZE; args->rsize = NFS_MAX_FILE_IO_SIZE;
args->wsize = NFS_MAX_FILE_IO_SIZE; args->wsize = NFS_MAX_FILE_IO_SIZE;
args->timeo = 600; args->acregmin = NFS_DEF_ACREGMIN;
args->retrans = 2; args->acregmax = NFS_DEF_ACREGMAX;
args->acregmin = 3; args->acdirmin = NFS_DEF_ACDIRMIN;
args->acregmax = 60; args->acdirmax = NFS_DEF_ACDIRMAX;
args->acdirmin = 30;
args->acdirmax = 60;
args->mount_server.port = 0; /* autobind unless user sets port */ 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.port = 0; /* autobind unless user sets port */
args->nfs_server.protocol = XPRT_TRANSPORT_TCP; args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
args->auth_flavors[0] = RPC_AUTH_UNIX;
switch (data->version) { switch (data->version) {
case 1: case 1:
...@@ -1289,7 +1575,9 @@ static int nfs_validate_mount_data(void *options, ...@@ -1289,7 +1575,9 @@ static int nfs_validate_mount_data(void *options,
args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL); args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
args->namlen = data->namlen; args->namlen = data->namlen;
args->bsize = data->bsize; args->bsize = data->bsize;
args->auth_flavors[0] = data->pseudoflavor;
if (data->flags & NFS_MOUNT_SECFLAVOUR)
args->auth_flavors[0] = data->pseudoflavor;
if (!args->nfs_server.hostname) if (!args->nfs_server.hostname)
goto out_nomem; goto out_nomem;
...@@ -1321,8 +1609,6 @@ static int nfs_validate_mount_data(void *options, ...@@ -1321,8 +1609,6 @@ static int nfs_validate_mount_data(void *options,
break; break;
default: { default: {
unsigned int len;
char *c;
int status; int status;
if (nfs_parse_mount_options((char *)options, args) == 0) if (nfs_parse_mount_options((char *)options, args) == 0)
...@@ -1332,21 +1618,22 @@ static int nfs_validate_mount_data(void *options, ...@@ -1332,21 +1618,22 @@ static int nfs_validate_mount_data(void *options,
&args->nfs_server.address)) &args->nfs_server.address))
goto out_no_address; goto out_no_address;
c = strchr(dev_name, ':'); nfs_set_port((struct sockaddr *)&args->nfs_server.address,
if (c == NULL) args->nfs_server.port);
return -EINVAL;
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++; nfs_set_mount_transport_protocol(args);
if (strlen(c) > NFS_MAXPATHLEN)
return -ENAMETOOLONG; status = nfs_parse_devname(dev_name,
args->nfs_server.export_path = c; &args->nfs_server.hostname,
PAGE_SIZE,
&args->nfs_server.export_path,
NFS_MAXPATHLEN);
if (!status)
status = nfs_try_mount(args, mntfh);
kfree(args->nfs_server.export_path);
args->nfs_server.export_path = NULL;
status = nfs_try_mount(args, mntfh);
if (status) if (status)
return status; return status;
...@@ -1354,9 +1641,6 @@ static int nfs_validate_mount_data(void *options, ...@@ -1354,9 +1641,6 @@ static int nfs_validate_mount_data(void *options,
} }
} }
if (!(args->flags & NFS_MOUNT_SECFLAVOUR))
args->auth_flavors[0] = RPC_AUTH_UNIX;
#ifndef CONFIG_NFS_V3 #ifndef CONFIG_NFS_V3
if (args->flags & NFS_MOUNT_VER3) if (args->flags & NFS_MOUNT_VER3)
goto out_v3_not_compiled; goto out_v3_not_compiled;
...@@ -1396,6 +1680,80 @@ static int nfs_validate_mount_data(void *options, ...@@ -1396,6 +1680,80 @@ static int nfs_validate_mount_data(void *options,
return -EINVAL; return -EINVAL;
} }
static int
nfs_compare_remount_data(struct nfs_server *nfss,
struct nfs_parsed_mount_data *data)
{
if (data->flags != nfss->flags ||
data->rsize != nfss->rsize ||
data->wsize != nfss->wsize ||
data->retrans != nfss->client->cl_timeout->to_retries ||
data->auth_flavors[0] != nfss->client->cl_auth->au_flavor ||
data->acregmin != nfss->acregmin / HZ ||
data->acregmax != nfss->acregmax / HZ ||
data->acdirmin != nfss->acdirmin / HZ ||
data->acdirmax != nfss->acdirmax / HZ ||
data->timeo != (10U * nfss->client->cl_timeout->to_initval / HZ) ||
data->nfs_server.addrlen != nfss->nfs_client->cl_addrlen ||
memcmp(&data->nfs_server.address, &nfss->nfs_client->cl_addr,
data->nfs_server.addrlen) != 0)
return -EINVAL;
return 0;
}
static int
nfs_remount(struct super_block *sb, int *flags, char *raw_data)
{
int error;
struct nfs_server *nfss = sb->s_fs_info;
struct nfs_parsed_mount_data *data;
struct nfs_mount_data *options = (struct nfs_mount_data *)raw_data;
struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;
u32 nfsvers = nfss->nfs_client->rpc_ops->version;
/*
* Userspace mount programs that send binary options generally send
* them populated with default values. We have no way to know which
* ones were explicitly specified. Fall back to legacy behavior and
* just return success.
*/
if ((nfsvers == 4 && options4->version == 1) ||
(nfsvers <= 3 && options->version >= 1 &&
options->version <= 6))
return 0;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/* fill out struct with values from existing mount */
data->flags = nfss->flags;
data->rsize = nfss->rsize;
data->wsize = nfss->wsize;
data->retrans = nfss->client->cl_timeout->to_retries;
data->auth_flavors[0] = nfss->client->cl_auth->au_flavor;
data->acregmin = nfss->acregmin / HZ;
data->acregmax = nfss->acregmax / HZ;
data->acdirmin = nfss->acdirmin / HZ;
data->acdirmax = nfss->acdirmax / HZ;
data->timeo = 10U * nfss->client->cl_timeout->to_initval / HZ;
data->nfs_server.addrlen = nfss->nfs_client->cl_addrlen;
memcpy(&data->nfs_server.address, &nfss->nfs_client->cl_addr,
data->nfs_server.addrlen);
/* overwrite those values with any that were specified */
error = nfs_parse_mount_options((char *)options, data);
if (error < 0)
goto out;
/* compare new mount options with old ones */
error = nfs_compare_remount_data(nfss, data);
out:
kfree(data);
return error;
}
/* /*
* Initialise the common bits of the superblock * Initialise the common bits of the superblock
*/ */
...@@ -1811,14 +2169,13 @@ static int nfs4_validate_mount_data(void *options, ...@@ -1811,14 +2169,13 @@ static int nfs4_validate_mount_data(void *options,
args->rsize = NFS_MAX_FILE_IO_SIZE; args->rsize = NFS_MAX_FILE_IO_SIZE;
args->wsize = NFS_MAX_FILE_IO_SIZE; args->wsize = NFS_MAX_FILE_IO_SIZE;
args->timeo = 600; args->acregmin = NFS_DEF_ACREGMIN;
args->retrans = 2; args->acregmax = NFS_DEF_ACREGMAX;
args->acregmin = 3; args->acdirmin = NFS_DEF_ACDIRMIN;
args->acregmax = 60; args->acdirmax = NFS_DEF_ACDIRMAX;
args->acdirmin = 30;
args->acdirmax = 60;
args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */ args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */
args->nfs_server.protocol = XPRT_TRANSPORT_TCP; args->auth_flavors[0] = RPC_AUTH_UNIX;
args->auth_flavor_len = 0;
switch (data->version) { switch (data->version) {
case 1: case 1:
...@@ -1834,18 +2191,13 @@ static int nfs4_validate_mount_data(void *options, ...@@ -1834,18 +2191,13 @@ static int nfs4_validate_mount_data(void *options,
&args->nfs_server.address)) &args->nfs_server.address))
goto out_no_address; goto out_no_address;
switch (data->auth_flavourlen) { if (data->auth_flavourlen) {
case 0: if (data->auth_flavourlen > 1)
args->auth_flavors[0] = RPC_AUTH_UNIX; goto out_inval_auth;
break;
case 1:
if (copy_from_user(&args->auth_flavors[0], if (copy_from_user(&args->auth_flavors[0],
data->auth_flavours, data->auth_flavours,
sizeof(args->auth_flavors[0]))) sizeof(args->auth_flavors[0])))
return -EFAULT; return -EFAULT;
break;
default:
goto out_inval_auth;
} }
c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN); c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
...@@ -1879,10 +2231,11 @@ static int nfs4_validate_mount_data(void *options, ...@@ -1879,10 +2231,11 @@ static int nfs4_validate_mount_data(void *options,
args->acdirmin = data->acdirmin; args->acdirmin = data->acdirmin;
args->acdirmax = data->acdirmax; args->acdirmax = data->acdirmax;
args->nfs_server.protocol = data->proto; args->nfs_server.protocol = data->proto;
nfs_validate_transport_protocol(args);
break; break;
default: { default: {
unsigned int len; int status;
if (nfs_parse_mount_options((char *)options, args) == 0) if (nfs_parse_mount_options((char *)options, args) == 0)
return -EINVAL; return -EINVAL;
...@@ -1891,44 +2244,25 @@ static int nfs4_validate_mount_data(void *options, ...@@ -1891,44 +2244,25 @@ static int nfs4_validate_mount_data(void *options,
&args->nfs_server.address)) &args->nfs_server.address))
return -EINVAL; return -EINVAL;
switch (args->auth_flavor_len) { nfs_set_port((struct sockaddr *)&args->nfs_server.address,
case 0: args->nfs_server.port);
args->auth_flavors[0] = RPC_AUTH_UNIX;
break;
case 1:
break;
default:
goto out_inval_auth;
}
/* nfs_validate_transport_protocol(args);
* Split "dev_name" into "hostname:mntpath".
*/
c = strchr(dev_name, ':');
if (c == NULL)
return -EINVAL;
/* while calculating len, pretend ':' is '\0' */
len = c - dev_name;
if (len > NFS4_MAXNAMLEN)
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); if (args->auth_flavor_len > 1)
goto out_inval_auth;
if (args->client_address == NULL) if (args->client_address == NULL)
goto out_no_client_address; goto out_no_client_address;
status = nfs_parse_devname(dev_name,
&args->nfs_server.hostname,
NFS4_MAXNAMLEN,
&args->nfs_server.export_path,
NFS4_MAXPATHLEN);
if (status < 0)
return status;
break; break;
} }
} }
...@@ -1944,10 +2278,6 @@ static int nfs4_validate_mount_data(void *options, ...@@ -1944,10 +2278,6 @@ static int nfs4_validate_mount_data(void *options,
data->auth_flavourlen); data->auth_flavourlen);
return -EINVAL; return -EINVAL;
out_nomem:
dfprintk(MOUNT, "NFS4: not enough memory to handle mount options\n");
return -ENOMEM;
out_no_address: out_no_address:
dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n"); dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
return -EINVAL; return -EINVAL;
......
...@@ -34,9 +34,6 @@ ...@@ -34,9 +34,6 @@
/* /*
* Local function declarations * Local function declarations
*/ */
static struct nfs_page * nfs_update_request(struct nfs_open_context*,
struct page *,
unsigned int, unsigned int);
static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc, static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc,
struct inode *inode, int ioflags); struct inode *inode, int ioflags);
static void nfs_redirty_request(struct nfs_page *req); static void nfs_redirty_request(struct nfs_page *req);
...@@ -136,16 +133,21 @@ static struct nfs_page *nfs_page_find_request(struct page *page) ...@@ -136,16 +133,21 @@ static struct nfs_page *nfs_page_find_request(struct page *page)
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count) static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
loff_t end, i_size = i_size_read(inode); loff_t end, i_size;
pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; pgoff_t end_index;
spin_lock(&inode->i_lock);
i_size = i_size_read(inode);
end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
if (i_size > 0 && page->index < end_index) if (i_size > 0 && page->index < end_index)
return; goto out;
end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count); end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
if (i_size >= end) if (i_size >= end)
return; goto out;
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
i_size_write(inode, end); i_size_write(inode, end);
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
out:
spin_unlock(&inode->i_lock);
} }
/* A writeback failed: mark the page as bad, and invalidate the page cache */ /* A writeback failed: mark the page as bad, and invalidate the page cache */
...@@ -169,29 +171,6 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int ...@@ -169,29 +171,6 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int
SetPageUptodate(page); SetPageUptodate(page);
} }
static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
unsigned int offset, unsigned int count)
{
struct nfs_page *req;
int ret;
for (;;) {
req = nfs_update_request(ctx, page, offset, count);
if (!IS_ERR(req))
break;
ret = PTR_ERR(req);
if (ret != -EBUSY)
return ret;
ret = nfs_wb_page(page->mapping->host, page);
if (ret != 0)
return ret;
}
/* Update file length */
nfs_grow_file(page, offset, count);
nfs_clear_page_tag_locked(req);
return 0;
}
static int wb_priority(struct writeback_control *wbc) static int wb_priority(struct writeback_control *wbc)
{ {
if (wbc->for_reclaim) if (wbc->for_reclaim)
...@@ -268,12 +247,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, ...@@ -268,12 +247,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
return ret; return ret;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
} }
if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { if (test_bit(PG_CLEAN, &req->wb_flags)) {
/* This request is marked for commit */
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
nfs_clear_page_tag_locked(req); BUG();
nfs_pageio_complete(pgio);
return 0;
} }
if (nfs_set_page_writeback(page) != 0) { if (nfs_set_page_writeback(page) != 0) {
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
...@@ -355,11 +331,19 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) ...@@ -355,11 +331,19 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
/* /*
* Insert a write request into an inode * Insert a write request into an inode
*/ */
static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
int error; int error;
error = radix_tree_preload(GFP_NOFS);
if (error != 0)
goto out;
/* Lock the request! */
nfs_lock_request_dontget(req);
spin_lock(&inode->i_lock);
error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req); error = radix_tree_insert(&nfsi->nfs_page_tree, req->wb_index, req);
BUG_ON(error); BUG_ON(error);
if (!nfsi->npages) { if (!nfsi->npages) {
...@@ -373,6 +357,10 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req) ...@@ -373,6 +357,10 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
kref_get(&req->wb_kref); kref_get(&req->wb_kref);
radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index, radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
NFS_PAGE_TAG_LOCKED); NFS_PAGE_TAG_LOCKED);
spin_unlock(&inode->i_lock);
radix_tree_preload_end();
out:
return error;
} }
/* /*
...@@ -405,19 +393,6 @@ nfs_mark_request_dirty(struct nfs_page *req) ...@@ -405,19 +393,6 @@ nfs_mark_request_dirty(struct nfs_page *req)
__set_page_dirty_nobuffers(req->wb_page); __set_page_dirty_nobuffers(req->wb_page);
} }
/*
* Check if a request is dirty
*/
static inline int
nfs_dirty_request(struct nfs_page *req)
{
struct page *page = req->wb_page;
if (page == NULL || test_bit(PG_NEED_COMMIT, &req->wb_flags))
return 0;
return !PageWriteback(page);
}
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
/* /*
* Add a request to the inode's commit list. * Add a request to the inode's commit list.
...@@ -430,7 +405,7 @@ nfs_mark_request_commit(struct nfs_page *req) ...@@ -430,7 +405,7 @@ nfs_mark_request_commit(struct nfs_page *req)
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
nfsi->ncommit++; nfsi->ncommit++;
set_bit(PG_NEED_COMMIT, &(req)->wb_flags); set_bit(PG_CLEAN, &(req)->wb_flags);
radix_tree_tag_set(&nfsi->nfs_page_tree, radix_tree_tag_set(&nfsi->nfs_page_tree,
req->wb_index, req->wb_index,
NFS_PAGE_TAG_COMMIT); NFS_PAGE_TAG_COMMIT);
...@@ -440,6 +415,19 @@ nfs_mark_request_commit(struct nfs_page *req) ...@@ -440,6 +415,19 @@ nfs_mark_request_commit(struct nfs_page *req)
__mark_inode_dirty(inode, I_DIRTY_DATASYNC); __mark_inode_dirty(inode, I_DIRTY_DATASYNC);
} }
static int
nfs_clear_request_commit(struct nfs_page *req)
{
struct page *page = req->wb_page;
if (test_and_clear_bit(PG_CLEAN, &(req)->wb_flags)) {
dec_zone_page_state(page, NR_UNSTABLE_NFS);
dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
return 1;
}
return 0;
}
static inline static inline
int nfs_write_need_commit(struct nfs_write_data *data) int nfs_write_need_commit(struct nfs_write_data *data)
{ {
...@@ -449,7 +437,7 @@ int nfs_write_need_commit(struct nfs_write_data *data) ...@@ -449,7 +437,7 @@ int nfs_write_need_commit(struct nfs_write_data *data)
static inline static inline
int nfs_reschedule_unstable_write(struct nfs_page *req) int nfs_reschedule_unstable_write(struct nfs_page *req)
{ {
if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) {
nfs_mark_request_commit(req); nfs_mark_request_commit(req);
return 1; return 1;
} }
...@@ -465,6 +453,12 @@ nfs_mark_request_commit(struct nfs_page *req) ...@@ -465,6 +453,12 @@ nfs_mark_request_commit(struct nfs_page *req)
{ {
} }
static inline int
nfs_clear_request_commit(struct nfs_page *req)
{
return 0;
}
static inline static inline
int nfs_write_need_commit(struct nfs_write_data *data) int nfs_write_need_commit(struct nfs_write_data *data)
{ {
...@@ -522,11 +516,8 @@ static void nfs_cancel_commit_list(struct list_head *head) ...@@ -522,11 +516,8 @@ static void nfs_cancel_commit_list(struct list_head *head)
while(!list_empty(head)) { while(!list_empty(head)) {
req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
BDI_RECLAIMABLE);
nfs_list_remove_request(req); nfs_list_remove_request(req);
clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); nfs_clear_request_commit(req);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
nfs_unlock_request(req); nfs_unlock_request(req);
} }
...@@ -564,110 +555,124 @@ static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pg ...@@ -564,110 +555,124 @@ static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pg
#endif #endif
/* /*
* Try to update any existing write request, or create one if there is none. * Search for an existing write request, and attempt to update
* In order to match, the request's credentials must match those of * it to reflect a new dirty region on a given page.
* the calling process.
* *
* Note: Should always be called with the Page Lock held! * If the attempt fails, then the existing request is flushed out
* to disk.
*/ */
static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
struct page *page, unsigned int offset, unsigned int bytes) struct page *page,
unsigned int offset,
unsigned int bytes)
{ {
struct address_space *mapping = page->mapping; struct nfs_page *req;
struct inode *inode = mapping->host; unsigned int rqend;
struct nfs_page *req, *new = NULL; unsigned int end;
pgoff_t rqend, end; int error;
if (!PagePrivate(page))
return NULL;
end = offset + bytes; end = offset + bytes;
spin_lock(&inode->i_lock);
for (;;) { for (;;) {
/* Loop over all inode entries and see if we find req = nfs_page_find_request_locked(page);
* A request for the page we wish to update if (req == NULL)
goto out_unlock;
rqend = req->wb_offset + req->wb_bytes;
/*
* Tell the caller to flush out the request if
* the offsets are non-contiguous.
* Note: nfs_flush_incompatible() will already
* have flushed out requests having wrong owners.
*/ */
if (new) { if (offset > rqend
if (radix_tree_preload(GFP_NOFS)) { || end < req->wb_offset)
nfs_release_request(new); goto out_flushme;
return ERR_PTR(-ENOMEM);
}
}
spin_lock(&inode->i_lock); if (nfs_set_page_tag_locked(req))
req = nfs_page_find_request_locked(page);
if (req) {
if (!nfs_set_page_tag_locked(req)) {
int error;
spin_unlock(&inode->i_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0) {
if (new) {
radix_tree_preload_end();
nfs_release_request(new);
}
return ERR_PTR(error);
}
continue;
}
spin_unlock(&inode->i_lock);
if (new) {
radix_tree_preload_end();
nfs_release_request(new);
}
break; break;
}
if (new) { /* The request is locked, so wait and then retry */
nfs_lock_request_dontget(new);
nfs_inode_add_request(inode, new);
spin_unlock(&inode->i_lock);
radix_tree_preload_end();
req = new;
goto zero_page;
}
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
error = nfs_wait_on_request(req);
new = nfs_create_request(ctx, inode, page, offset, bytes); nfs_release_request(req);
if (IS_ERR(new)) if (error != 0)
return new; goto out_err;
spin_lock(&inode->i_lock);
} }
/* We have a request for our page. if (nfs_clear_request_commit(req))
* If the creds don't match, or the radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
* page addresses don't match, req->wb_index, NFS_PAGE_TAG_COMMIT);
* tell the caller to wait on the conflicting
* request.
*/
rqend = req->wb_offset + req->wb_bytes;
if (req->wb_context != ctx
|| req->wb_page != page
|| !nfs_dirty_request(req)
|| offset > rqend || end < req->wb_offset) {
nfs_clear_page_tag_locked(req);
return ERR_PTR(-EBUSY);
}
/* Okay, the request matches. Update the region */ /* Okay, the request matches. Update the region */
if (offset < req->wb_offset) { if (offset < req->wb_offset) {
req->wb_offset = offset; req->wb_offset = offset;
req->wb_pgbase = offset; req->wb_pgbase = offset;
req->wb_bytes = max(end, rqend) - req->wb_offset;
goto zero_page;
} }
if (end > rqend) if (end > rqend)
req->wb_bytes = end - req->wb_offset; req->wb_bytes = end - req->wb_offset;
else
req->wb_bytes = rqend - req->wb_offset;
out_unlock:
spin_unlock(&inode->i_lock);
return req; return req;
zero_page: out_flushme:
/* If this page might potentially be marked as up to date, spin_unlock(&inode->i_lock);
* then we need to zero any uninitalised data. */ nfs_release_request(req);
if (req->wb_pgbase == 0 && req->wb_bytes != PAGE_CACHE_SIZE error = nfs_wb_page(inode, page);
&& !PageUptodate(req->wb_page)) out_err:
zero_user_segment(req->wb_page, req->wb_bytes, PAGE_CACHE_SIZE); return ERR_PTR(error);
}
/*
* Try to update an existing write request, or create one if there is none.
*
* Note: Should always be called with the Page Lock held to prevent races
* if we have to add a new request. Also assumes that the caller has
* already called nfs_flush_incompatible() if necessary.
*/
static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
struct page *page, unsigned int offset, unsigned int bytes)
{
struct inode *inode = page->mapping->host;
struct nfs_page *req;
int error;
req = nfs_try_to_update_request(inode, page, offset, bytes);
if (req != NULL)
goto out;
req = nfs_create_request(ctx, inode, page, offset, bytes);
if (IS_ERR(req))
goto out;
error = nfs_inode_add_request(inode, req);
if (error != 0) {
nfs_release_request(req);
req = ERR_PTR(error);
}
out:
return req; return req;
} }
static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
unsigned int offset, unsigned int count)
{
struct nfs_page *req;
req = nfs_setup_write_request(ctx, page, offset, count);
if (IS_ERR(req))
return PTR_ERR(req);
/* Update file length */
nfs_grow_file(page, offset, count);
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
nfs_clear_page_tag_locked(req);
return 0;
}
int nfs_flush_incompatible(struct file *file, struct page *page) int nfs_flush_incompatible(struct file *file, struct page *page)
{ {
struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_open_context *ctx = nfs_file_open_context(file);
...@@ -685,8 +690,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page) ...@@ -685,8 +690,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
req = nfs_page_find_request(page); req = nfs_page_find_request(page);
if (req == NULL) if (req == NULL)
return 0; return 0;
do_flush = req->wb_page != page || req->wb_context != ctx do_flush = req->wb_page != page || req->wb_context != ctx;
|| !nfs_dirty_request(req);
nfs_release_request(req); nfs_release_request(req);
if (!do_flush) if (!do_flush)
return 0; return 0;
...@@ -721,10 +725,10 @@ int nfs_updatepage(struct file *file, struct page *page, ...@@ -721,10 +725,10 @@ int nfs_updatepage(struct file *file, struct page *page,
nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE); nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n", dprintk("NFS: nfs_updatepage(%s/%s %d@%lld)\n",
file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name, count, file->f_path.dentry->d_name.name, count,
(long long)(page_offset(page) +offset)); (long long)(page_offset(page) + offset));
/* If we're not using byte range locks, and we know the page /* If we're not using byte range locks, and we know the page
* is up to date, it may be more efficient to extend the write * is up to date, it may be more efficient to extend the write
...@@ -744,7 +748,7 @@ int nfs_updatepage(struct file *file, struct page *page, ...@@ -744,7 +748,7 @@ int nfs_updatepage(struct file *file, struct page *page,
else else
__set_page_dirty_nobuffers(page); __set_page_dirty_nobuffers(page);
dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n",
status, (long long)i_size_read(inode)); status, (long long)i_size_read(inode));
return status; return status;
} }
...@@ -752,12 +756,7 @@ int nfs_updatepage(struct file *file, struct page *page, ...@@ -752,12 +756,7 @@ int nfs_updatepage(struct file *file, struct page *page,
static void nfs_writepage_release(struct nfs_page *req) static void nfs_writepage_release(struct nfs_page *req)
{ {
if (PageError(req->wb_page)) { if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) {
nfs_end_page_writeback(req->wb_page);
nfs_inode_remove_request(req);
} else if (!nfs_reschedule_unstable_write(req)) {
/* Set the PG_uptodate flag */
nfs_mark_uptodate(req->wb_page, req->wb_pgbase, req->wb_bytes);
nfs_end_page_writeback(req->wb_page); nfs_end_page_writeback(req->wb_page);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
} else } else
...@@ -834,7 +833,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req, ...@@ -834,7 +833,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
NFS_PROTO(inode)->write_setup(data, &msg); NFS_PROTO(inode)->write_setup(data, &msg);
dprintk("NFS: %5u initiated write call " dprintk("NFS: %5u initiated write call "
"(req %s/%Ld, %u bytes @ offset %Lu)\n", "(req %s/%lld, %u bytes @ offset %llu)\n",
data->task.tk_pid, data->task.tk_pid,
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode), (long long)NFS_FILEID(inode),
...@@ -978,13 +977,13 @@ static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, ...@@ -978,13 +977,13 @@ static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
{ {
struct nfs_write_data *data = calldata; struct nfs_write_data *data = calldata;
struct nfs_page *req = data->req;
dprintk("NFS: write (%s/%Ld %d@%Ld)", dprintk("NFS: %5u write(%s/%lld %d@%lld)",
req->wb_context->path.dentry->d_inode->i_sb->s_id, task->tk_pid,
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), data->req->wb_context->path.dentry->d_inode->i_sb->s_id,
req->wb_bytes, (long long)
(long long)req_offset(req)); NFS_FILEID(data->req->wb_context->path.dentry->d_inode),
data->req->wb_bytes, (long long)req_offset(data->req));
nfs_writeback_done(task, data); nfs_writeback_done(task, data);
} }
...@@ -1058,7 +1057,8 @@ static void nfs_writeback_release_full(void *calldata) ...@@ -1058,7 +1057,8 @@ static void nfs_writeback_release_full(void *calldata)
nfs_list_remove_request(req); nfs_list_remove_request(req);
dprintk("NFS: write (%s/%Ld %d@%Ld)", dprintk("NFS: %5u write (%s/%lld %d@%lld)",
data->task.tk_pid,
req->wb_context->path.dentry->d_inode->i_sb->s_id, req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes, req->wb_bytes,
...@@ -1078,8 +1078,6 @@ static void nfs_writeback_release_full(void *calldata) ...@@ -1078,8 +1078,6 @@ static void nfs_writeback_release_full(void *calldata)
dprintk(" marked for commit\n"); dprintk(" marked for commit\n");
goto next; goto next;
} }
/* Set the PG_uptodate flag? */
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
dprintk(" OK\n"); dprintk(" OK\n");
remove_request: remove_request:
nfs_end_page_writeback(page); nfs_end_page_writeback(page);
...@@ -1133,7 +1131,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) ...@@ -1133,7 +1131,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
static unsigned long complain; static unsigned long complain;
if (time_before(complain, jiffies)) { if (time_before(complain, jiffies)) {
dprintk("NFS: faulty NFS server %s:" dprintk("NFS: faulty NFS server %s:"
" (committed = %d) != (stable = %d)\n", " (committed = %d) != (stable = %d)\n",
NFS_SERVER(data->inode)->nfs_client->cl_hostname, NFS_SERVER(data->inode)->nfs_client->cl_hostname,
resp->verf->committed, argp->stable); resp->verf->committed, argp->stable);
...@@ -1297,12 +1295,9 @@ static void nfs_commit_release(void *calldata) ...@@ -1297,12 +1295,9 @@ static void nfs_commit_release(void *calldata)
while (!list_empty(&data->pages)) { while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next); req = nfs_list_entry(data->pages.next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
clear_bit(PG_NEED_COMMIT, &(req)->wb_flags); nfs_clear_request_commit(req);
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
BDI_RECLAIMABLE);
dprintk("NFS: commit (%s/%Ld %d@%Ld)", dprintk("NFS: commit (%s/%lld %d@%lld)",
req->wb_context->path.dentry->d_inode->i_sb->s_id, req->wb_context->path.dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
req->wb_bytes, req->wb_bytes,
...@@ -1318,9 +1313,6 @@ static void nfs_commit_release(void *calldata) ...@@ -1318,9 +1313,6 @@ static void nfs_commit_release(void *calldata)
* returned by the server against all stored verfs. */ * returned by the server against all stored verfs. */
if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
/* We have a match */ /* We have a match */
/* Set the PG_uptodate flag */
nfs_mark_uptodate(req->wb_page, req->wb_pgbase,
req->wb_bytes);
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
dprintk(" OK\n"); dprintk(" OK\n");
goto next; goto next;
...@@ -1479,7 +1471,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page) ...@@ -1479,7 +1471,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
req = nfs_page_find_request(page); req = nfs_page_find_request(page);
if (req == NULL) if (req == NULL)
goto out; goto out;
if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) { if (test_bit(PG_CLEAN, &req->wb_flags)) {
nfs_release_request(req); nfs_release_request(req);
break; break;
} }
......
...@@ -381,7 +381,7 @@ static int do_probe_callback(void *data) ...@@ -381,7 +381,7 @@ static int do_probe_callback(void *data)
.program = &cb_program, .program = &cb_program,
.version = nfs_cb_version[1]->number, .version = nfs_cb_version[1]->number,
.authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */ .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
.flags = (RPC_CLNT_CREATE_NOPING), .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
......
...@@ -44,6 +44,13 @@ ...@@ -44,6 +44,13 @@
#include <linux/types.h> #include <linux/types.h>
/*
* These mimic similar macros defined in user-space for inet_ntop(3).
* See /usr/include/netinet/in.h .
*/
#define INET_ADDRSTRLEN (16)
#define INET6_ADDRSTRLEN (48)
extern __be32 in_aton(const char *str); extern __be32 in_aton(const char *str);
extern int in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end); extern int in4_pton(const char *src, int srclen, u8 *dst, int delim, const char **end);
extern int in6_pton(const char *src, int srclen, u8 *dst, int delim, const char **end); extern int in6_pton(const char *src, int srclen, u8 *dst, int delim, const char **end);
......
...@@ -12,9 +12,19 @@ ...@@ -12,9 +12,19 @@
#include <linux/magic.h> #include <linux/magic.h>
/* Default timeout values */ /* Default timeout values */
#define NFS_DEF_UDP_TIMEO (11)
#define NFS_DEF_UDP_RETRANS (3)
#define NFS_DEF_TCP_TIMEO (600)
#define NFS_DEF_TCP_RETRANS (2)
#define NFS_MAX_UDP_TIMEOUT (60*HZ) #define NFS_MAX_UDP_TIMEOUT (60*HZ)
#define NFS_MAX_TCP_TIMEOUT (600*HZ) #define NFS_MAX_TCP_TIMEOUT (600*HZ)
#define NFS_DEF_ACREGMIN (3)
#define NFS_DEF_ACREGMAX (60)
#define NFS_DEF_ACDIRMIN (30)
#define NFS_DEF_ACDIRMAX (60)
/* /*
* When flushing a cluster of dirty pages, there can be different * When flushing a cluster of dirty pages, there can be different
* strategies: * strategies:
......
/*
* User-space visible declarations for NFS client per-mount
* point statistics
*
* Copyright (C) 2005, 2006 Chuck Lever <cel@netapp.com>
*
* NFS client per-mount statistics provide information about the
* health of the NFS client and the health of each NFS mount point.
* Generally these are not for detailed problem diagnosis, but
* simply to indicate that there is a problem.
*
* These counters are not meant to be human-readable, but are meant
* to be integrated into system monitoring tools such as "sar" and
* "iostat". As such, the counters are sampled by the tools over
* time, and are never zeroed after a file system is mounted.
* Moving averages can be computed by the tools by taking the
* difference between two instantaneous samples and dividing that
* by the time between the samples.
*/
#ifndef _LINUX_NFS_IOSTAT
#define _LINUX_NFS_IOSTAT
#define NFS_IOSTAT_VERS "1.0"
/*
* NFS byte counters
*
* 1. SERVER - the number of payload bytes read from or written
* to the server by the NFS client via an NFS READ or WRITE
* request.
*
* 2. NORMAL - the number of bytes read or written by applications
* via the read(2) and write(2) system call interfaces.
*
* 3. DIRECT - the number of bytes read or written from files
* opened with the O_DIRECT flag.
*
* These counters give a view of the data throughput into and out
* of the NFS client. Comparing the number of bytes requested by
* an application with the number of bytes the client requests from
* the server can provide an indication of client efficiency
* (per-op, cache hits, etc).
*
* These counters can also help characterize which access methods
* are in use. DIRECT by itself shows whether there is any O_DIRECT
* traffic. NORMAL + DIRECT shows how much data is going through
* the system call interface. A large amount of SERVER traffic
* without much NORMAL or DIRECT traffic shows that applications
* are using mapped files.
*
* NFS page counters
*
* These count the number of pages read or written via nfs_readpage(),
* nfs_readpages(), or their write equivalents.
*
* NB: When adding new byte counters, please include the measured
* units in the name of each byte counter to help users of this
* interface determine what exactly is being counted.
*/
enum nfs_stat_bytecounters {
NFSIOS_NORMALREADBYTES = 0,
NFSIOS_NORMALWRITTENBYTES,
NFSIOS_DIRECTREADBYTES,
NFSIOS_DIRECTWRITTENBYTES,
NFSIOS_SERVERREADBYTES,
NFSIOS_SERVERWRITTENBYTES,
NFSIOS_READPAGES,
NFSIOS_WRITEPAGES,
__NFSIOS_BYTESMAX,
};
/*
* NFS event counters
*
* These counters provide a low-overhead way of monitoring client
* activity without enabling NFS trace debugging. The counters
* show the rate at which VFS requests are made, and how often the
* client invalidates its data and attribute caches. This allows
* system administrators to monitor such things as how close-to-open
* is working, and answer questions such as "why are there so many
* GETATTR requests on the wire?"
*
* They also count anamolous events such as short reads and writes,
* silly renames due to close-after-delete, and operations that
* change the size of a file (such operations can often be the
* source of data corruption if applications aren't using file
* locking properly).
*/
enum nfs_stat_eventcounters {
NFSIOS_INODEREVALIDATE = 0,
NFSIOS_DENTRYREVALIDATE,
NFSIOS_DATAINVALIDATE,
NFSIOS_ATTRINVALIDATE,
NFSIOS_VFSOPEN,
NFSIOS_VFSLOOKUP,
NFSIOS_VFSACCESS,
NFSIOS_VFSUPDATEPAGE,
NFSIOS_VFSREADPAGE,
NFSIOS_VFSREADPAGES,
NFSIOS_VFSWRITEPAGE,
NFSIOS_VFSWRITEPAGES,
NFSIOS_VFSGETDENTS,
NFSIOS_VFSSETATTR,
NFSIOS_VFSFLUSH,
NFSIOS_VFSFSYNC,
NFSIOS_VFSLOCK,
NFSIOS_VFSRELEASE,
NFSIOS_CONGESTIONWAIT,
NFSIOS_SETATTRTRUNC,
NFSIOS_EXTENDWRITE,
NFSIOS_SILLYRENAME,
NFSIOS_SHORTREAD,
NFSIOS_SHORTWRITE,
NFSIOS_DELAY,
__NFSIOS_COUNTSMAX,
};
#endif /* _LINUX_NFS_IOSTAT */
...@@ -27,9 +27,12 @@ ...@@ -27,9 +27,12 @@
/* /*
* Valid flags for a dirty buffer * Valid flags for a dirty buffer
*/ */
#define PG_BUSY 0 enum {
#define PG_NEED_COMMIT 1 PG_BUSY = 0,
#define PG_NEED_RESCHED 2 PG_CLEAN,
PG_NEED_COMMIT,
PG_NEED_RESCHED,
};
struct nfs_inode; struct nfs_inode;
struct nfs_page { struct nfs_page {
......
...@@ -829,9 +829,8 @@ struct nfs_rpc_ops { ...@@ -829,9 +829,8 @@ struct nfs_rpc_ops {
int (*write_done) (struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *);
void (*commit_setup) (struct nfs_write_data *, struct rpc_message *); void (*commit_setup) (struct nfs_write_data *, struct rpc_message *);
int (*commit_done) (struct rpc_task *, struct nfs_write_data *); int (*commit_done) (struct rpc_task *, struct nfs_write_data *);
int (*file_open) (struct inode *, struct file *);
int (*file_release) (struct inode *, struct file *);
int (*lock)(struct file *, int, struct file_lock *); int (*lock)(struct file *, int, struct file_lock *);
int (*lock_check_bounds)(const struct file_lock *);
void (*clear_acl_cache)(struct inode *); void (*clear_acl_cache)(struct inode *);
}; };
......
...@@ -42,7 +42,8 @@ struct rpc_clnt { ...@@ -42,7 +42,8 @@ struct rpc_clnt {
unsigned int cl_softrtry : 1,/* soft timeouts */ unsigned int cl_softrtry : 1,/* soft timeouts */
cl_discrtry : 1,/* disconnect before retry */ cl_discrtry : 1,/* disconnect before retry */
cl_autobind : 1;/* use getport() */ cl_autobind : 1,/* use getport() */
cl_chatty : 1;/* be verbose */
struct rpc_rtt * cl_rtt; /* RTO estimator data */ struct rpc_rtt * cl_rtt; /* RTO estimator data */
const struct rpc_timeout *cl_timeout; /* Timeout strategy */ const struct rpc_timeout *cl_timeout; /* Timeout strategy */
...@@ -114,6 +115,7 @@ struct rpc_create_args { ...@@ -114,6 +115,7 @@ struct rpc_create_args {
#define RPC_CLNT_CREATE_NONPRIVPORT (1UL << 3) #define RPC_CLNT_CREATE_NONPRIVPORT (1UL << 3)
#define RPC_CLNT_CREATE_NOPING (1UL << 4) #define RPC_CLNT_CREATE_NOPING (1UL << 4)
#define RPC_CLNT_CREATE_DISCRTRY (1UL << 5) #define RPC_CLNT_CREATE_DISCRTRY (1UL << 5)
#define RPC_CLNT_CREATE_QUIET (1UL << 6)
struct rpc_clnt *rpc_create(struct rpc_create_args *args); struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *, struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
...@@ -123,6 +125,9 @@ void rpc_shutdown_client(struct rpc_clnt *); ...@@ -123,6 +125,9 @@ void rpc_shutdown_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *);
int rpcb_register(u32, u32, int, unsigned short, int *); int rpcb_register(u32, u32, int, unsigned short, int *);
int rpcb_v4_register(const u32 program, const u32 version,
const struct sockaddr *address,
const char *netid, int *result);
int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int); int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int);
void rpcb_getport_async(struct rpc_task *); void rpcb_getport_async(struct rpc_task *);
......
...@@ -135,7 +135,6 @@ struct rpc_task_setup { ...@@ -135,7 +135,6 @@ struct rpc_task_setup {
#define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER)
#define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS) #define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS)
#define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED)
#define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL)
#define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT) #define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT)
#define RPC_TASK_RUNNING 0 #define RPC_TASK_RUNNING 0
......
...@@ -63,22 +63,11 @@ static const struct rpc_credops gss_nullops; ...@@ -63,22 +63,11 @@ static const struct rpc_credops gss_nullops;
# define RPCDBG_FACILITY RPCDBG_AUTH # define RPCDBG_FACILITY RPCDBG_AUTH
#endif #endif
#define NFS_NGROUPS 16 #define GSS_CRED_SLACK 1024
#define GSS_CRED_SLACK 1024 /* XXX: unused */
/* length of a krb5 verifier (48), plus data added before arguments when /* length of a krb5 verifier (48), plus data added before arguments when
* using integrity (two 4-byte integers): */ * using integrity (two 4-byte integers): */
#define GSS_VERF_SLACK 100 #define GSS_VERF_SLACK 100
/* XXX this define must match the gssd define
* as it is passed to gssd to signal the use of
* machine creds should be part of the shared rpc interface */
#define CA_RUN_AS_MACHINE 0x00000200
/* dump the buffer in `emacs-hexl' style */
#define isprint(c) ((c > 0x1f) && (c < 0x7f))
struct gss_auth { struct gss_auth {
struct kref kref; struct kref kref;
struct rpc_auth rpc_auth; struct rpc_auth rpc_auth;
...@@ -146,7 +135,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest) ...@@ -146,7 +135,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest)
q = (const void *)((const char *)p + len); q = (const void *)((const char *)p + len);
if (unlikely(q > end || q < p)) if (unlikely(q > end || q < p))
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
dest->data = kmemdup(p, len, GFP_KERNEL); dest->data = kmemdup(p, len, GFP_NOFS);
if (unlikely(dest->data == NULL)) if (unlikely(dest->data == NULL))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dest->len = len; dest->len = len;
...@@ -171,7 +160,7 @@ gss_alloc_context(void) ...@@ -171,7 +160,7 @@ gss_alloc_context(void)
{ {
struct gss_cl_ctx *ctx; struct gss_cl_ctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ctx = kzalloc(sizeof(*ctx), GFP_NOFS);
if (ctx != NULL) { if (ctx != NULL) {
ctx->gc_proc = RPC_GSS_PROC_DATA; ctx->gc_proc = RPC_GSS_PROC_DATA;
ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */
...@@ -272,7 +261,7 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid) ...@@ -272,7 +261,7 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
return NULL; return NULL;
} }
/* Try to add a upcall to the pipefs queue. /* Try to add an upcall to the pipefs queue.
* If an upcall owned by our uid already exists, then we return a reference * If an upcall owned by our uid already exists, then we return a reference
* to that upcall instead of adding the new upcall. * to that upcall instead of adding the new upcall.
*/ */
...@@ -341,7 +330,7 @@ gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid) ...@@ -341,7 +330,7 @@ gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid)
{ {
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
gss_msg = kzalloc(sizeof(*gss_msg), GFP_KERNEL); gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
if (gss_msg != NULL) { if (gss_msg != NULL) {
INIT_LIST_HEAD(&gss_msg->list); INIT_LIST_HEAD(&gss_msg->list);
rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
...@@ -493,7 +482,6 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -493,7 +482,6 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
{ {
const void *p, *end; const void *p, *end;
void *buf; void *buf;
struct rpc_clnt *clnt;
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct gss_cl_ctx *ctx; struct gss_cl_ctx *ctx;
...@@ -503,11 +491,10 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) ...@@ -503,11 +491,10 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
if (mlen > MSG_BUF_MAXSIZE) if (mlen > MSG_BUF_MAXSIZE)
goto out; goto out;
err = -ENOMEM; err = -ENOMEM;
buf = kmalloc(mlen, GFP_KERNEL); buf = kmalloc(mlen, GFP_NOFS);
if (!buf) if (!buf)
goto out; goto out;
clnt = RPC_I(inode)->private;
err = -EFAULT; err = -EFAULT;
if (copy_from_user(buf, src, mlen)) if (copy_from_user(buf, src, mlen))
goto err; goto err;
...@@ -806,7 +793,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) ...@@ -806,7 +793,7 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
dprintk("RPC: gss_create_cred for uid %d, flavor %d\n", dprintk("RPC: gss_create_cred for uid %d, flavor %d\n",
acred->uid, auth->au_flavor); acred->uid, auth->au_flavor);
if (!(cred = kzalloc(sizeof(*cred), GFP_KERNEL))) if (!(cred = kzalloc(sizeof(*cred), GFP_NOFS)))
goto out_err; goto out_err;
rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops); rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
......
...@@ -70,7 +70,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res) ...@@ -70,7 +70,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
q = (const void *)((const char *)p + len); q = (const void *)((const char *)p + len);
if (unlikely(q > end || q < p)) if (unlikely(q > end || q < p))
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
res->data = kmemdup(p, len, GFP_KERNEL); res->data = kmemdup(p, len, GFP_NOFS);
if (unlikely(res->data == NULL)) if (unlikely(res->data == NULL))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
res->len = len; res->len = len;
...@@ -131,7 +131,7 @@ gss_import_sec_context_kerberos(const void *p, ...@@ -131,7 +131,7 @@ gss_import_sec_context_kerberos(const void *p,
struct krb5_ctx *ctx; struct krb5_ctx *ctx;
int tmp; int tmp;
if (!(ctx = kzalloc(sizeof(*ctx), GFP_KERNEL))) if (!(ctx = kzalloc(sizeof(*ctx), GFP_NOFS)))
goto out_err; goto out_err;
p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate)); p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate));
......
...@@ -76,7 +76,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res) ...@@ -76,7 +76,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
q = (const void *)((const char *)p + len); q = (const void *)((const char *)p + len);
if (unlikely(q > end || q < p)) if (unlikely(q > end || q < p))
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
res->data = kmemdup(p, len, GFP_KERNEL); res->data = kmemdup(p, len, GFP_NOFS);
if (unlikely(res->data == NULL)) if (unlikely(res->data == NULL))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
return q; return q;
...@@ -90,7 +90,7 @@ gss_import_sec_context_spkm3(const void *p, size_t len, ...@@ -90,7 +90,7 @@ gss_import_sec_context_spkm3(const void *p, size_t len,
struct spkm3_ctx *ctx; struct spkm3_ctx *ctx;
int version; int version;
if (!(ctx = kzalloc(sizeof(*ctx), GFP_KERNEL))) if (!(ctx = kzalloc(sizeof(*ctx), GFP_NOFS)))
goto out_err; goto out_err;
p = simple_get_bytes(p, end, &version, sizeof(version)); p = simple_get_bytes(p, end, &version, sizeof(version));
......
...@@ -90,7 +90,7 @@ asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits) ...@@ -90,7 +90,7 @@ asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
int int
decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen) decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
{ {
if (!(out->data = kzalloc(explen,GFP_KERNEL))) if (!(out->data = kzalloc(explen,GFP_NOFS)))
return 0; return 0;
out->len = explen; out->len = explen;
memcpy(out->data, in, enclen); memcpy(out->data, in, enclen);
......
...@@ -66,7 +66,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) ...@@ -66,7 +66,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", dprintk("RPC: allocating UNIX cred for uid %d gid %d\n",
acred->uid, acred->gid); acred->uid, acred->gid);
if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL))) if (!(cred = kmalloc(sizeof(*cred), GFP_NOFS)))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops); rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kallsyms.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
...@@ -58,7 +59,6 @@ static void call_start(struct rpc_task *task); ...@@ -58,7 +59,6 @@ static void call_start(struct rpc_task *task);
static void call_reserve(struct rpc_task *task); static void call_reserve(struct rpc_task *task);
static void call_reserveresult(struct rpc_task *task); static void call_reserveresult(struct rpc_task *task);
static void call_allocate(struct rpc_task *task); static void call_allocate(struct rpc_task *task);
static void call_encode(struct rpc_task *task);
static void call_decode(struct rpc_task *task); static void call_decode(struct rpc_task *task);
static void call_bind(struct rpc_task *task); static void call_bind(struct rpc_task *task);
static void call_bind_status(struct rpc_task *task); static void call_bind_status(struct rpc_task *task);
...@@ -70,9 +70,9 @@ static void call_refreshresult(struct rpc_task *task); ...@@ -70,9 +70,9 @@ static void call_refreshresult(struct rpc_task *task);
static void call_timeout(struct rpc_task *task); static void call_timeout(struct rpc_task *task);
static void call_connect(struct rpc_task *task); static void call_connect(struct rpc_task *task);
static void call_connect_status(struct rpc_task *task); static void call_connect_status(struct rpc_task *task);
static __be32 * call_header(struct rpc_task *task);
static __be32 * call_verify(struct rpc_task *task);
static __be32 *rpc_encode_header(struct rpc_task *task);
static __be32 *rpc_verify_header(struct rpc_task *task);
static int rpc_ping(struct rpc_clnt *clnt, int flags); static int rpc_ping(struct rpc_clnt *clnt, int flags);
static void rpc_register_client(struct rpc_clnt *clnt) static void rpc_register_client(struct rpc_clnt *clnt)
...@@ -324,6 +324,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) ...@@ -324,6 +324,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
clnt->cl_autobind = 1; clnt->cl_autobind = 1;
if (args->flags & RPC_CLNT_CREATE_DISCRTRY) if (args->flags & RPC_CLNT_CREATE_DISCRTRY)
clnt->cl_discrtry = 1; clnt->cl_discrtry = 1;
if (!(args->flags & RPC_CLNT_CREATE_QUIET))
clnt->cl_chatty = 1;
return clnt; return clnt;
} }
...@@ -690,6 +692,21 @@ rpc_restart_call(struct rpc_task *task) ...@@ -690,6 +692,21 @@ rpc_restart_call(struct rpc_task *task)
} }
EXPORT_SYMBOL_GPL(rpc_restart_call); EXPORT_SYMBOL_GPL(rpc_restart_call);
#ifdef RPC_DEBUG
static const char *rpc_proc_name(const struct rpc_task *task)
{
const struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
if (proc) {
if (proc->p_name)
return proc->p_name;
else
return "NULL";
} else
return "no proc";
}
#endif
/* /*
* 0. Initial state * 0. Initial state
* *
...@@ -701,9 +718,9 @@ call_start(struct rpc_task *task) ...@@ -701,9 +718,9 @@ call_start(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
dprintk("RPC: %5u call_start %s%d proc %d (%s)\n", task->tk_pid, dprintk("RPC: %5u call_start %s%d proc %s (%s)\n", task->tk_pid,
clnt->cl_protname, clnt->cl_vers, clnt->cl_protname, clnt->cl_vers,
task->tk_msg.rpc_proc->p_proc, rpc_proc_name(task),
(RPC_IS_ASYNC(task) ? "async" : "sync")); (RPC_IS_ASYNC(task) ? "async" : "sync"));
/* Increment call count */ /* Increment call count */
...@@ -861,7 +878,7 @@ rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len) ...@@ -861,7 +878,7 @@ rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
* 3. Encode arguments of an RPC call * 3. Encode arguments of an RPC call
*/ */
static void static void
call_encode(struct rpc_task *task) rpc_xdr_encode(struct rpc_task *task)
{ {
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
kxdrproc_t encode; kxdrproc_t encode;
...@@ -876,23 +893,19 @@ call_encode(struct rpc_task *task) ...@@ -876,23 +893,19 @@ call_encode(struct rpc_task *task)
(char *)req->rq_buffer + req->rq_callsize, (char *)req->rq_buffer + req->rq_callsize,
req->rq_rcvsize); req->rq_rcvsize);
/* Encode header and provided arguments */ p = rpc_encode_header(task);
encode = task->tk_msg.rpc_proc->p_encode; if (p == NULL) {
if (!(p = call_header(task))) { printk(KERN_INFO "RPC: couldn't encode RPC header, exit EIO\n");
printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
rpc_exit(task, -EIO); rpc_exit(task, -EIO);
return; return;
} }
encode = task->tk_msg.rpc_proc->p_encode;
if (encode == NULL) if (encode == NULL)
return; return;
task->tk_status = rpcauth_wrap_req(task, encode, req, p, task->tk_status = rpcauth_wrap_req(task, encode, req, p,
task->tk_msg.rpc_argp); task->tk_msg.rpc_argp);
if (task->tk_status == -ENOMEM) {
/* XXX: Is this sane? */
rpc_delay(task, 3*HZ);
task->tk_status = -EAGAIN;
}
} }
/* /*
...@@ -929,11 +942,9 @@ call_bind_status(struct rpc_task *task) ...@@ -929,11 +942,9 @@ call_bind_status(struct rpc_task *task)
} }
switch (task->tk_status) { switch (task->tk_status) {
case -EAGAIN: case -ENOMEM:
dprintk("RPC: %5u rpcbind waiting for another request " dprintk("RPC: %5u rpcbind out of memory\n", task->tk_pid);
"to finish\n", task->tk_pid); rpc_delay(task, HZ >> 2);
/* avoid busy-waiting here -- could be a network outage. */
rpc_delay(task, 5*HZ);
goto retry_timeout; goto retry_timeout;
case -EACCES: case -EACCES:
dprintk("RPC: %5u remote rpcbind: RPC program/version " dprintk("RPC: %5u remote rpcbind: RPC program/version "
...@@ -1046,10 +1057,16 @@ call_transmit(struct rpc_task *task) ...@@ -1046,10 +1057,16 @@ call_transmit(struct rpc_task *task)
/* Encode here so that rpcsec_gss can use correct sequence number. */ /* Encode here so that rpcsec_gss can use correct sequence number. */
if (rpc_task_need_encode(task)) { if (rpc_task_need_encode(task)) {
BUG_ON(task->tk_rqstp->rq_bytes_sent != 0); BUG_ON(task->tk_rqstp->rq_bytes_sent != 0);
call_encode(task); rpc_xdr_encode(task);
/* Did the encode result in an error condition? */ /* Did the encode result in an error condition? */
if (task->tk_status != 0) if (task->tk_status != 0) {
/* Was the error nonfatal? */
if (task->tk_status == -EAGAIN)
rpc_delay(task, HZ >> 4);
else
rpc_exit(task, task->tk_status);
return; return;
}
} }
xprt_transmit(task); xprt_transmit(task);
if (task->tk_status < 0) if (task->tk_status < 0)
...@@ -1132,7 +1149,8 @@ call_status(struct rpc_task *task) ...@@ -1132,7 +1149,8 @@ call_status(struct rpc_task *task)
rpc_exit(task, status); rpc_exit(task, status);
break; break;
default: default:
printk("%s: RPC call returned error %d\n", if (clnt->cl_chatty)
printk("%s: RPC call returned error %d\n",
clnt->cl_protname, -status); clnt->cl_protname, -status);
rpc_exit(task, status); rpc_exit(task, status);
} }
...@@ -1157,7 +1175,8 @@ call_timeout(struct rpc_task *task) ...@@ -1157,7 +1175,8 @@ call_timeout(struct rpc_task *task)
task->tk_timeouts++; task->tk_timeouts++;
if (RPC_IS_SOFT(task)) { if (RPC_IS_SOFT(task)) {
printk(KERN_NOTICE "%s: server %s not responding, timed out\n", if (clnt->cl_chatty)
printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
clnt->cl_protname, clnt->cl_server); clnt->cl_protname, clnt->cl_server);
rpc_exit(task, -EIO); rpc_exit(task, -EIO);
return; return;
...@@ -1165,7 +1184,8 @@ call_timeout(struct rpc_task *task) ...@@ -1165,7 +1184,8 @@ call_timeout(struct rpc_task *task)
if (!(task->tk_flags & RPC_CALL_MAJORSEEN)) { if (!(task->tk_flags & RPC_CALL_MAJORSEEN)) {
task->tk_flags |= RPC_CALL_MAJORSEEN; task->tk_flags |= RPC_CALL_MAJORSEEN;
printk(KERN_NOTICE "%s: server %s not responding, still trying\n", if (clnt->cl_chatty)
printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
clnt->cl_protname, clnt->cl_server); clnt->cl_protname, clnt->cl_server);
} }
rpc_force_rebind(clnt); rpc_force_rebind(clnt);
...@@ -1196,8 +1216,9 @@ call_decode(struct rpc_task *task) ...@@ -1196,8 +1216,9 @@ call_decode(struct rpc_task *task)
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
if (task->tk_flags & RPC_CALL_MAJORSEEN) { if (task->tk_flags & RPC_CALL_MAJORSEEN) {
printk(KERN_NOTICE "%s: server %s OK\n", if (clnt->cl_chatty)
clnt->cl_protname, clnt->cl_server); printk(KERN_NOTICE "%s: server %s OK\n",
clnt->cl_protname, clnt->cl_server);
task->tk_flags &= ~RPC_CALL_MAJORSEEN; task->tk_flags &= ~RPC_CALL_MAJORSEEN;
} }
...@@ -1224,8 +1245,7 @@ call_decode(struct rpc_task *task) ...@@ -1224,8 +1245,7 @@ call_decode(struct rpc_task *task)
goto out_retry; goto out_retry;
} }
/* Verify the RPC header */ p = rpc_verify_header(task);
p = call_verify(task);
if (IS_ERR(p)) { if (IS_ERR(p)) {
if (p == ERR_PTR(-EAGAIN)) if (p == ERR_PTR(-EAGAIN))
goto out_retry; goto out_retry;
...@@ -1243,7 +1263,7 @@ call_decode(struct rpc_task *task) ...@@ -1243,7 +1263,7 @@ call_decode(struct rpc_task *task)
return; return;
out_retry: out_retry:
task->tk_status = 0; task->tk_status = 0;
/* Note: call_verify() may have freed the RPC slot */ /* Note: rpc_verify_header() may have freed the RPC slot */
if (task->tk_rqstp == req) { if (task->tk_rqstp == req) {
req->rq_received = req->rq_rcv_buf.len = 0; req->rq_received = req->rq_rcv_buf.len = 0;
if (task->tk_client->cl_discrtry) if (task->tk_client->cl_discrtry)
...@@ -1290,11 +1310,8 @@ call_refreshresult(struct rpc_task *task) ...@@ -1290,11 +1310,8 @@ call_refreshresult(struct rpc_task *task)
return; return;
} }
/*
* Call header serialization
*/
static __be32 * static __be32 *
call_header(struct rpc_task *task) rpc_encode_header(struct rpc_task *task)
{ {
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
...@@ -1314,11 +1331,8 @@ call_header(struct rpc_task *task) ...@@ -1314,11 +1331,8 @@ call_header(struct rpc_task *task)
return p; return p;
} }
/*
* Reply header verification
*/
static __be32 * static __be32 *
call_verify(struct rpc_task *task) rpc_verify_header(struct rpc_task *task)
{ {
struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0]; struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
int len = task->tk_rqstp->rq_rcv_buf.len >> 2; int len = task->tk_rqstp->rq_rcv_buf.len >> 2;
...@@ -1392,7 +1406,7 @@ call_verify(struct rpc_task *task) ...@@ -1392,7 +1406,7 @@ call_verify(struct rpc_task *task)
task->tk_action = call_bind; task->tk_action = call_bind;
goto out_retry; goto out_retry;
case RPC_AUTH_TOOWEAK: case RPC_AUTH_TOOWEAK:
printk(KERN_NOTICE "call_verify: server %s requires stronger " printk(KERN_NOTICE "RPC: server %s requires stronger "
"authentication.\n", task->tk_client->cl_server); "authentication.\n", task->tk_client->cl_server);
break; break;
default: default:
...@@ -1431,10 +1445,10 @@ call_verify(struct rpc_task *task) ...@@ -1431,10 +1445,10 @@ call_verify(struct rpc_task *task)
error = -EPROTONOSUPPORT; error = -EPROTONOSUPPORT;
goto out_err; goto out_err;
case RPC_PROC_UNAVAIL: case RPC_PROC_UNAVAIL:
dprintk("RPC: %5u %s: proc %p unsupported by program %u, " dprintk("RPC: %5u %s: proc %s unsupported by program %u, "
"version %u on server %s\n", "version %u on server %s\n",
task->tk_pid, __func__, task->tk_pid, __func__,
task->tk_msg.rpc_proc, rpc_proc_name(task),
task->tk_client->cl_prog, task->tk_client->cl_prog,
task->tk_client->cl_vers, task->tk_client->cl_vers,
task->tk_client->cl_server); task->tk_client->cl_server);
...@@ -1517,44 +1531,53 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int ...@@ -1517,44 +1531,53 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int
EXPORT_SYMBOL_GPL(rpc_call_null); EXPORT_SYMBOL_GPL(rpc_call_null);
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
static void rpc_show_header(void)
{
printk(KERN_INFO "-pid- flgs status -client- --rqstp- "
"-timeout ---ops--\n");
}
static void rpc_show_task(const struct rpc_clnt *clnt,
const struct rpc_task *task)
{
const char *rpc_waitq = "none";
char *p, action[KSYM_SYMBOL_LEN];
if (RPC_IS_QUEUED(task))
rpc_waitq = rpc_qname(task->tk_waitqueue);
/* map tk_action pointer to a function name; then trim off
* the "+0x0 [sunrpc]" */
sprint_symbol(action, (unsigned long)task->tk_action);
p = strchr(action, '+');
if (p)
*p = '\0';
printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%s q:%s\n",
task->tk_pid, task->tk_flags, task->tk_status,
clnt, task->tk_rqstp, task->tk_timeout, task->tk_ops,
clnt->cl_protname, clnt->cl_vers, rpc_proc_name(task),
action, rpc_waitq);
}
void rpc_show_tasks(void) void rpc_show_tasks(void)
{ {
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct rpc_task *t; struct rpc_task *task;
int header = 0;
spin_lock(&rpc_client_lock); spin_lock(&rpc_client_lock);
if (list_empty(&all_clients))
goto out;
printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout "
"-rpcwait -action- ---ops--\n");
list_for_each_entry(clnt, &all_clients, cl_clients) { list_for_each_entry(clnt, &all_clients, cl_clients) {
if (list_empty(&clnt->cl_tasks))
continue;
spin_lock(&clnt->cl_lock); spin_lock(&clnt->cl_lock);
list_for_each_entry(t, &clnt->cl_tasks, tk_task) { list_for_each_entry(task, &clnt->cl_tasks, tk_task) {
const char *rpc_waitq = "none"; if (!header) {
int proc; rpc_show_header();
header++;
if (t->tk_msg.rpc_proc) }
proc = t->tk_msg.rpc_proc->p_proc; rpc_show_task(clnt, task);
else
proc = -1;
if (RPC_IS_QUEUED(t))
rpc_waitq = rpc_qname(t->tk_waitqueue);
printk("%5u %04d %04x %6d %8p %6d %8p %8ld %8s %8p %8p\n",
t->tk_pid, proc,
t->tk_flags, t->tk_status,
t->tk_client,
(t->tk_client ? t->tk_client->cl_prog : 0),
t->tk_rqstp, t->tk_timeout,
rpc_waitq,
t->tk_action, t->tk_ops);
} }
spin_unlock(&clnt->cl_lock); spin_unlock(&clnt->cl_lock);
} }
out:
spin_unlock(&rpc_client_lock); spin_unlock(&rpc_client_lock);
} }
#endif #endif
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
#define RPCBIND_PROGRAM (100000u) #define RPCBIND_PROGRAM (100000u)
#define RPCBIND_PORT (111u) #define RPCBIND_PORT (111u)
#define RPCBVERS_2 (2u)
#define RPCBVERS_3 (3u)
#define RPCBVERS_4 (4u)
enum { enum {
RPCBPROC_NULL, RPCBPROC_NULL,
RPCBPROC_SET, RPCBPROC_SET,
...@@ -64,6 +68,7 @@ enum { ...@@ -64,6 +68,7 @@ enum {
#define RPCB_MAXOWNERLEN sizeof(RPCB_OWNER_STRING) #define RPCB_MAXOWNERLEN sizeof(RPCB_OWNER_STRING)
static void rpcb_getport_done(struct rpc_task *, void *); static void rpcb_getport_done(struct rpc_task *, void *);
static void rpcb_map_release(void *data);
static struct rpc_program rpcb_program; static struct rpc_program rpcb_program;
struct rpcbind_args { struct rpcbind_args {
...@@ -76,41 +81,73 @@ struct rpcbind_args { ...@@ -76,41 +81,73 @@ struct rpcbind_args {
const char * r_netid; const char * r_netid;
const char * r_addr; const char * r_addr;
const char * r_owner; const char * r_owner;
int r_status;
}; };
static struct rpc_procinfo rpcb_procedures2[]; static struct rpc_procinfo rpcb_procedures2[];
static struct rpc_procinfo rpcb_procedures3[]; static struct rpc_procinfo rpcb_procedures3[];
static struct rpc_procinfo rpcb_procedures4[];
struct rpcb_info { struct rpcb_info {
int rpc_vers; u32 rpc_vers;
struct rpc_procinfo * rpc_proc; struct rpc_procinfo * rpc_proc;
}; };
static struct rpcb_info rpcb_next_version[]; static struct rpcb_info rpcb_next_version[];
static struct rpcb_info rpcb_next_version6[]; static struct rpcb_info rpcb_next_version6[];
static const struct rpc_call_ops rpcb_getport_ops = {
.rpc_call_done = rpcb_getport_done,
.rpc_release = rpcb_map_release,
};
static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status)
{
xprt_clear_binding(xprt);
rpc_wake_up_status(&xprt->binding, status);
}
static void rpcb_map_release(void *data) static void rpcb_map_release(void *data)
{ {
struct rpcbind_args *map = data; struct rpcbind_args *map = data;
rpcb_wake_rpcbind_waiters(map->r_xprt, map->r_status);
xprt_put(map->r_xprt); xprt_put(map->r_xprt);
kfree(map); kfree(map);
} }
static const struct rpc_call_ops rpcb_getport_ops = { static const struct sockaddr_in rpcb_inaddr_loopback = {
.rpc_call_done = rpcb_getport_done, .sin_family = AF_INET,
.rpc_release = rpcb_map_release, .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
.sin_port = htons(RPCBIND_PORT),
}; };
static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status) static const struct sockaddr_in6 rpcb_in6addr_loopback = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
.sin6_port = htons(RPCBIND_PORT),
};
static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
size_t addrlen, u32 version)
{ {
xprt_clear_binding(xprt); struct rpc_create_args args = {
rpc_wake_up_status(&xprt->binding, status); .protocol = XPRT_TRANSPORT_UDP,
.address = addr,
.addrsize = addrlen,
.servername = "localhost",
.program = &rpcb_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
.flags = RPC_CLNT_CREATE_NOPING,
};
return rpc_create(&args);
} }
static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
size_t salen, int proto, u32 version, size_t salen, int proto, u32 version)
int privileged)
{ {
struct rpc_create_args args = { struct rpc_create_args args = {
.protocol = proto, .protocol = proto,
...@@ -120,7 +157,8 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, ...@@ -120,7 +157,8 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
.program = &rpcb_program, .program = &rpcb_program,
.version = version, .version = version,
.authflavor = RPC_AUTH_UNIX, .authflavor = RPC_AUTH_UNIX,
.flags = RPC_CLNT_CREATE_NOPING, .flags = (RPC_CLNT_CREATE_NOPING |
RPC_CLNT_CREATE_NONPRIVPORT),
}; };
switch (srvaddr->sa_family) { switch (srvaddr->sa_family) {
...@@ -134,29 +172,72 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, ...@@ -134,29 +172,72 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
return NULL; return NULL;
} }
if (!privileged)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
return rpc_create(&args); return rpc_create(&args);
} }
static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
u32 version, struct rpc_message *msg,
int *result)
{
struct rpc_clnt *rpcb_clnt;
int error = 0;
*result = 0;
rpcb_clnt = rpcb_create_local(addr, addrlen, version);
if (!IS_ERR(rpcb_clnt)) {
error = rpc_call_sync(rpcb_clnt, msg, 0);
rpc_shutdown_client(rpcb_clnt);
} else
error = PTR_ERR(rpcb_clnt);
if (error < 0)
printk(KERN_WARNING "RPC: failed to contact local rpcbind "
"server (errno %d).\n", -error);
dprintk("RPC: registration status %d/%d\n", error, *result);
return error;
}
/** /**
* rpcb_register - set or unset a port registration with the local rpcbind svc * rpcb_register - set or unset a port registration with the local rpcbind svc
* @prog: RPC program number to bind * @prog: RPC program number to bind
* @vers: RPC version number to bind * @vers: RPC version number to bind
* @prot: transport protocol to use to make this request * @prot: transport protocol to register
* @port: port value to register * @port: port value to register
* @okay: result code * @okay: OUT: result code
*
* RPC services invoke this function to advertise their contact
* information via the system's rpcbind daemon. RPC services
* invoke this function once for each [program, version, transport]
* tuple they wish to advertise.
*
* Callers may also unregister RPC services that are no longer
* available by setting the passed-in port to zero. This removes
* all registered transports for [program, version] from the local
* rpcbind database.
*
* Returns zero if the registration request was dispatched
* successfully and a reply was received. The rpcbind daemon's
* boolean result code is stored in *okay.
*
* Returns an errno value and sets *result to zero if there was
* some problem that prevented the rpcbind request from being
* dispatched, or if the rpcbind daemon did not respond within
* the timeout.
* *
* port == 0 means unregister, port != 0 means register. * This function uses rpcbind protocol version 2 to contact the
* local rpcbind daemon.
* *
* This routine supports only rpcbind version 2. * Registration works over both AF_INET and AF_INET6, and services
* registered via this function are advertised as available for any
* address. If the local rpcbind daemon is listening on AF_INET6,
* services registered via this function will be advertised on
* IN6ADDR_ANY (ie available for all AF_INET and AF_INET6
* addresses).
*/ */
int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{ {
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct rpcbind_args map = { struct rpcbind_args map = {
.r_prog = prog, .r_prog = prog,
.r_vers = vers, .r_vers = vers,
...@@ -164,32 +245,159 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) ...@@ -164,32 +245,159 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
.r_port = port, .r_port = port,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &rpcb_procedures2[port ?
RPCBPROC_SET : RPCBPROC_UNSET],
.rpc_argp = &map, .rpc_argp = &map,
.rpc_resp = okay, .rpc_resp = okay,
}; };
struct rpc_clnt *rpcb_clnt;
int error = 0;
dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " dprintk("RPC: %sregistering (%u, %u, %d, %u) with local "
"rpcbind\n", (port ? "" : "un"), "rpcbind\n", (port ? "" : "un"),
prog, vers, prot, port); prog, vers, prot, port);
rpcb_clnt = rpcb_create("localhost", (struct sockaddr *) &sin, msg.rpc_proc = &rpcb_procedures2[RPCBPROC_UNSET];
sizeof(sin), XPRT_TRANSPORT_UDP, 2, 1); if (port)
if (IS_ERR(rpcb_clnt)) msg.rpc_proc = &rpcb_procedures2[RPCBPROC_SET];
return PTR_ERR(rpcb_clnt);
error = rpc_call_sync(rpcb_clnt, &msg, 0); return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
sizeof(rpcb_inaddr_loopback),
RPCBVERS_2, &msg, okay);
}
rpc_shutdown_client(rpcb_clnt); /*
if (error < 0) * Fill in AF_INET family-specific arguments to register
printk(KERN_WARNING "RPC: failed to contact local rpcbind " */
"server (errno %d).\n", -error); static int rpcb_register_netid4(struct sockaddr_in *address_to_register,
dprintk("RPC: registration status %d/%d\n", error, *okay); struct rpc_message *msg)
{
struct rpcbind_args *map = msg->rpc_argp;
unsigned short port = ntohs(address_to_register->sin_port);
char buf[32];
/* Construct AF_INET universal address */
snprintf(buf, sizeof(buf),
NIPQUAD_FMT".%u.%u",
NIPQUAD(address_to_register->sin_addr.s_addr),
port >> 8, port & 0xff);
map->r_addr = buf;
dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with "
"local rpcbind\n", (port ? "" : "un"),
map->r_prog, map->r_vers,
map->r_addr, map->r_netid);
msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET];
if (port)
msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET];
return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
sizeof(rpcb_inaddr_loopback),
RPCBVERS_4, msg, msg->rpc_resp);
}
return error; /*
* Fill in AF_INET6 family-specific arguments to register
*/
static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
struct rpc_message *msg)
{
struct rpcbind_args *map = msg->rpc_argp;
unsigned short port = ntohs(address_to_register->sin6_port);
char buf[64];
/* Construct AF_INET6 universal address */
snprintf(buf, sizeof(buf),
NIP6_FMT".%u.%u",
NIP6(address_to_register->sin6_addr),
port >> 8, port & 0xff);
map->r_addr = buf;
dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with "
"local rpcbind\n", (port ? "" : "un"),
map->r_prog, map->r_vers,
map->r_addr, map->r_netid);
msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET];
if (port)
msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET];
return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback,
sizeof(rpcb_in6addr_loopback),
RPCBVERS_4, msg, msg->rpc_resp);
}
/**
* rpcb_v4_register - set or unset a port registration with the local rpcbind
* @program: RPC program number of service to (un)register
* @version: RPC version number of service to (un)register
* @address: address family, IP address, and port to (un)register
* @netid: netid of transport protocol to (un)register
* @result: result code from rpcbind RPC call
*
* RPC services invoke this function to advertise their contact
* information via the system's rpcbind daemon. RPC services
* invoke this function once for each [program, version, address,
* netid] tuple they wish to advertise.
*
* Callers may also unregister RPC services that are no longer
* available by setting the port number in the passed-in address
* to zero. Callers pass a netid of "" to unregister all
* transport netids associated with [program, version, address].
*
* Returns zero if the registration request was dispatched
* successfully and a reply was received. The rpcbind daemon's
* result code is stored in *result.
*
* Returns an errno value and sets *result to zero if there was
* some problem that prevented the rpcbind request from being
* dispatched, or if the rpcbind daemon did not respond within
* the timeout.
*
* This function uses rpcbind protocol version 4 to contact the
* local rpcbind daemon. The local rpcbind daemon must support
* version 4 of the rpcbind protocol in order for these functions
* to register a service successfully.
*
* Supported netids include "udp" and "tcp" for UDP and TCP over
* IPv4, and "udp6" and "tcp6" for UDP and TCP over IPv6,
* respectively.
*
* The contents of @address determine the address family and the
* port to be registered. The usual practice is to pass INADDR_ANY
* as the raw address, but specifying a non-zero address is also
* supported by this API if the caller wishes to advertise an RPC
* service on a specific network interface.
*
* Note that passing in INADDR_ANY does not create the same service
* registration as IN6ADDR_ANY. The former advertises an RPC
* service on any IPv4 address, but not on IPv6. The latter
* advertises the service on all IPv4 and IPv6 addresses.
*/
int rpcb_v4_register(const u32 program, const u32 version,
const struct sockaddr *address, const char *netid,
int *result)
{
struct rpcbind_args map = {
.r_prog = program,
.r_vers = version,
.r_netid = netid,
.r_owner = RPCB_OWNER_STRING,
};
struct rpc_message msg = {
.rpc_argp = &map,
.rpc_resp = result,
};
*result = 0;
switch (address->sa_family) {
case AF_INET:
return rpcb_register_netid4((struct sockaddr_in *)address,
&msg);
case AF_INET6:
return rpcb_register_netid6((struct sockaddr_in6 *)address,
&msg);
}
return -EAFNOSUPPORT;
} }
/** /**
...@@ -227,7 +435,7 @@ int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot) ...@@ -227,7 +435,7 @@ int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot)
__func__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); __func__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin, rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin,
sizeof(*sin), prot, 2, 0); sizeof(*sin), prot, RPCBVERS_2);
if (IS_ERR(rpcb_clnt)) if (IS_ERR(rpcb_clnt))
return PTR_ERR(rpcb_clnt); return PTR_ERR(rpcb_clnt);
...@@ -289,17 +497,16 @@ void rpcb_getport_async(struct rpc_task *task) ...@@ -289,17 +497,16 @@ void rpcb_getport_async(struct rpc_task *task)
/* Autobind on cloned rpc clients is discouraged */ /* Autobind on cloned rpc clients is discouraged */
BUG_ON(clnt->cl_parent != clnt); BUG_ON(clnt->cl_parent != clnt);
/* Put self on the wait queue to ensure we get notified if
* some other task is already attempting to bind the port */
rpc_sleep_on(&xprt->binding, task, NULL);
if (xprt_test_and_set_binding(xprt)) { if (xprt_test_and_set_binding(xprt)) {
status = -EAGAIN; /* tell caller to check again */
dprintk("RPC: %5u %s: waiting for another binder\n", dprintk("RPC: %5u %s: waiting for another binder\n",
task->tk_pid, __func__); task->tk_pid, __func__);
goto bailout_nowake; return;
} }
/* Put self on queue before sending rpcbind request, in case
* rpcb_getport_done completes before we return from rpc_run_task */
rpc_sleep_on(&xprt->binding, task, NULL);
/* Someone else may have bound if we slept */ /* Someone else may have bound if we slept */
if (xprt_bound(xprt)) { if (xprt_bound(xprt)) {
status = 0; status = 0;
...@@ -338,7 +545,7 @@ void rpcb_getport_async(struct rpc_task *task) ...@@ -338,7 +545,7 @@ void rpcb_getport_async(struct rpc_task *task)
task->tk_pid, __func__, bind_version); task->tk_pid, __func__, bind_version);
rpcb_clnt = rpcb_create(clnt->cl_server, sap, salen, xprt->prot, rpcb_clnt = rpcb_create(clnt->cl_server, sap, salen, xprt->prot,
bind_version, 0); bind_version);
if (IS_ERR(rpcb_clnt)) { if (IS_ERR(rpcb_clnt)) {
status = PTR_ERR(rpcb_clnt); status = PTR_ERR(rpcb_clnt);
dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n", dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n",
...@@ -361,15 +568,15 @@ void rpcb_getport_async(struct rpc_task *task) ...@@ -361,15 +568,15 @@ void rpcb_getport_async(struct rpc_task *task)
map->r_netid = rpc_peeraddr2str(clnt, RPC_DISPLAY_NETID); map->r_netid = rpc_peeraddr2str(clnt, RPC_DISPLAY_NETID);
map->r_addr = rpc_peeraddr2str(rpcb_clnt, RPC_DISPLAY_UNIVERSAL_ADDR); map->r_addr = rpc_peeraddr2str(rpcb_clnt, RPC_DISPLAY_UNIVERSAL_ADDR);
map->r_owner = RPCB_OWNER_STRING; /* ignored for GETADDR */ map->r_owner = RPCB_OWNER_STRING; /* ignored for GETADDR */
map->r_status = -EIO;
child = rpcb_call_async(rpcb_clnt, map, proc); child = rpcb_call_async(rpcb_clnt, map, proc);
rpc_release_client(rpcb_clnt); rpc_release_client(rpcb_clnt);
if (IS_ERR(child)) { if (IS_ERR(child)) {
status = -EIO;
/* rpcb_map_release() has freed the arguments */ /* rpcb_map_release() has freed the arguments */
dprintk("RPC: %5u %s: rpc_run_task failed\n", dprintk("RPC: %5u %s: rpc_run_task failed\n",
task->tk_pid, __func__); task->tk_pid, __func__);
goto bailout_nofree; return;
} }
rpc_put_task(child); rpc_put_task(child);
...@@ -378,7 +585,6 @@ void rpcb_getport_async(struct rpc_task *task) ...@@ -378,7 +585,6 @@ void rpcb_getport_async(struct rpc_task *task)
bailout_nofree: bailout_nofree:
rpcb_wake_rpcbind_waiters(xprt, status); rpcb_wake_rpcbind_waiters(xprt, status);
bailout_nowake:
task->tk_status = status; task->tk_status = status;
} }
EXPORT_SYMBOL_GPL(rpcb_getport_async); EXPORT_SYMBOL_GPL(rpcb_getport_async);
...@@ -417,9 +623,13 @@ static void rpcb_getport_done(struct rpc_task *child, void *data) ...@@ -417,9 +623,13 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)
dprintk("RPC: %5u rpcb_getport_done(status %d, port %u)\n", dprintk("RPC: %5u rpcb_getport_done(status %d, port %u)\n",
child->tk_pid, status, map->r_port); child->tk_pid, status, map->r_port);
rpcb_wake_rpcbind_waiters(xprt, status); map->r_status = status;
} }
/*
* XDR functions for rpcbind
*/
static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p, static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p,
struct rpcbind_args *rpcb) struct rpcbind_args *rpcb)
{ {
...@@ -438,7 +648,7 @@ static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p, ...@@ -438,7 +648,7 @@ static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p,
unsigned short *portp) unsigned short *portp)
{ {
*portp = (unsigned short) ntohl(*p++); *portp = (unsigned short) ntohl(*p++);
dprintk("RPC: rpcb_decode_getport result %u\n", dprintk("RPC: rpcb_decode_getport result %u\n",
*portp); *portp);
return 0; return 0;
} }
...@@ -447,8 +657,8 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p, ...@@ -447,8 +657,8 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p,
unsigned int *boolp) unsigned int *boolp)
{ {
*boolp = (unsigned int) ntohl(*p++); *boolp = (unsigned int) ntohl(*p++);
dprintk("RPC: rpcb_decode_set result %u\n", dprintk("RPC: rpcb_decode_set: call %s\n",
*boolp); (*boolp ? "succeeded" : "failed"));
return 0; return 0;
} }
...@@ -571,52 +781,60 @@ static int rpcb_decode_getaddr(struct rpc_rqst *req, __be32 *p, ...@@ -571,52 +781,60 @@ static int rpcb_decode_getaddr(struct rpc_rqst *req, __be32 *p,
static struct rpc_procinfo rpcb_procedures2[] = { static struct rpc_procinfo rpcb_procedures2[] = {
PROC(SET, mapping, set), PROC(SET, mapping, set),
PROC(UNSET, mapping, set), PROC(UNSET, mapping, set),
PROC(GETADDR, mapping, getport), PROC(GETPORT, mapping, getport),
}; };
static struct rpc_procinfo rpcb_procedures3[] = { static struct rpc_procinfo rpcb_procedures3[] = {
PROC(SET, mapping, set), PROC(SET, getaddr, set),
PROC(UNSET, mapping, set), PROC(UNSET, getaddr, set),
PROC(GETADDR, getaddr, getaddr), PROC(GETADDR, getaddr, getaddr),
}; };
static struct rpc_procinfo rpcb_procedures4[] = { static struct rpc_procinfo rpcb_procedures4[] = {
PROC(SET, mapping, set), PROC(SET, getaddr, set),
PROC(UNSET, mapping, set), PROC(UNSET, getaddr, set),
PROC(GETADDR, getaddr, getaddr),
PROC(GETVERSADDR, getaddr, getaddr), PROC(GETVERSADDR, getaddr, getaddr),
}; };
static struct rpcb_info rpcb_next_version[] = { static struct rpcb_info rpcb_next_version[] = {
#ifdef CONFIG_SUNRPC_BIND34 {
{ 4, &rpcb_procedures4[RPCBPROC_GETVERSADDR] }, .rpc_vers = RPCBVERS_2,
{ 3, &rpcb_procedures3[RPCBPROC_GETADDR] }, .rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT],
#endif },
{ 2, &rpcb_procedures2[RPCBPROC_GETPORT] }, {
{ 0, NULL }, .rpc_proc = NULL,
},
}; };
static struct rpcb_info rpcb_next_version6[] = { static struct rpcb_info rpcb_next_version6[] = {
#ifdef CONFIG_SUNRPC_BIND34 {
{ 4, &rpcb_procedures4[RPCBPROC_GETVERSADDR] }, .rpc_vers = RPCBVERS_4,
{ 3, &rpcb_procedures3[RPCBPROC_GETADDR] }, .rpc_proc = &rpcb_procedures4[RPCBPROC_GETADDR],
#endif },
{ 0, NULL }, {
.rpc_vers = RPCBVERS_3,
.rpc_proc = &rpcb_procedures3[RPCBPROC_GETADDR],
},
{
.rpc_proc = NULL,
},
}; };
static struct rpc_version rpcb_version2 = { static struct rpc_version rpcb_version2 = {
.number = 2, .number = RPCBVERS_2,
.nrprocs = RPCB_HIGHPROC_2, .nrprocs = RPCB_HIGHPROC_2,
.procs = rpcb_procedures2 .procs = rpcb_procedures2
}; };
static struct rpc_version rpcb_version3 = { static struct rpc_version rpcb_version3 = {
.number = 3, .number = RPCBVERS_3,
.nrprocs = RPCB_HIGHPROC_3, .nrprocs = RPCB_HIGHPROC_3,
.procs = rpcb_procedures3 .procs = rpcb_procedures3
}; };
static struct rpc_version rpcb_version4 = { static struct rpc_version rpcb_version4 = {
.number = 4, .number = RPCBVERS_4,
.nrprocs = RPCB_HIGHPROC_4, .nrprocs = RPCB_HIGHPROC_4,
.procs = rpcb_procedures4 .procs = rpcb_procedures4
}; };
......
...@@ -576,9 +576,7 @@ EXPORT_SYMBOL_GPL(rpc_delay); ...@@ -576,9 +576,7 @@ EXPORT_SYMBOL_GPL(rpc_delay);
*/ */
static void rpc_prepare_task(struct rpc_task *task) static void rpc_prepare_task(struct rpc_task *task)
{ {
lock_kernel();
task->tk_ops->rpc_call_prepare(task, task->tk_calldata); task->tk_ops->rpc_call_prepare(task, task->tk_calldata);
unlock_kernel();
} }
/* /*
...@@ -588,9 +586,7 @@ void rpc_exit_task(struct rpc_task *task) ...@@ -588,9 +586,7 @@ void rpc_exit_task(struct rpc_task *task)
{ {
task->tk_action = NULL; task->tk_action = NULL;
if (task->tk_ops->rpc_call_done != NULL) { if (task->tk_ops->rpc_call_done != NULL) {
lock_kernel();
task->tk_ops->rpc_call_done(task, task->tk_calldata); task->tk_ops->rpc_call_done(task, task->tk_calldata);
unlock_kernel();
if (task->tk_action != NULL) { if (task->tk_action != NULL) {
WARN_ON(RPC_ASSASSINATED(task)); WARN_ON(RPC_ASSASSINATED(task));
/* Always release the RPC slot and buffer memory */ /* Always release the RPC slot and buffer memory */
...@@ -602,11 +598,8 @@ EXPORT_SYMBOL_GPL(rpc_exit_task); ...@@ -602,11 +598,8 @@ EXPORT_SYMBOL_GPL(rpc_exit_task);
void rpc_release_calldata(const struct rpc_call_ops *ops, void *calldata) void rpc_release_calldata(const struct rpc_call_ops *ops, void *calldata)
{ {
if (ops->rpc_release != NULL) { if (ops->rpc_release != NULL)
lock_kernel();
ops->rpc_release(calldata); ops->rpc_release(calldata);
unlock_kernel();
}
} }
/* /*
...@@ -626,19 +619,15 @@ static void __rpc_execute(struct rpc_task *task) ...@@ -626,19 +619,15 @@ static void __rpc_execute(struct rpc_task *task)
/* /*
* Execute any pending callback. * Execute any pending callback.
*/ */
if (RPC_DO_CALLBACK(task)) { if (task->tk_callback) {
/* Define a callback save pointer */
void (*save_callback)(struct rpc_task *); void (*save_callback)(struct rpc_task *);
/* /*
* If a callback exists, save it, reset it, * We set tk_callback to NULL before calling it,
* call it. * in case it sets the tk_callback field itself:
* The save is needed to stop from resetting
* another callback set within the callback handler
* - Dave
*/ */
save_callback=task->tk_callback; save_callback = task->tk_callback;
task->tk_callback=NULL; task->tk_callback = NULL;
save_callback(task); save_callback(task);
} }
......
...@@ -690,7 +690,7 @@ static void xprt_connect_status(struct rpc_task *task) ...@@ -690,7 +690,7 @@ static void xprt_connect_status(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_xprt;
if (task->tk_status >= 0) { if (task->tk_status == 0) {
xprt->stat.connect_count++; xprt->stat.connect_count++;
xprt->stat.connect_time += (long)jiffies - xprt->stat.connect_start; xprt->stat.connect_time += (long)jiffies - xprt->stat.connect_start;
dprintk("RPC: %5u xprt_connect_status: connection established\n", dprintk("RPC: %5u xprt_connect_status: connection established\n",
...@@ -699,12 +699,6 @@ static void xprt_connect_status(struct rpc_task *task) ...@@ -699,12 +699,6 @@ static void xprt_connect_status(struct rpc_task *task)
} }
switch (task->tk_status) { switch (task->tk_status) {
case -ECONNREFUSED:
case -ECONNRESET:
dprintk("RPC: %5u xprt_connect_status: server %s refused "
"connection\n", task->tk_pid,
task->tk_client->cl_server);
break;
case -ENOTCONN: case -ENOTCONN:
dprintk("RPC: %5u xprt_connect_status: connection broken\n", dprintk("RPC: %5u xprt_connect_status: connection broken\n",
task->tk_pid); task->tk_pid);
...@@ -878,6 +872,7 @@ void xprt_transmit(struct rpc_task *task) ...@@ -878,6 +872,7 @@ void xprt_transmit(struct rpc_task *task)
return; return;
req->rq_connect_cookie = xprt->connect_cookie; req->rq_connect_cookie = xprt->connect_cookie;
req->rq_xtime = jiffies;
status = xprt->ops->send_request(task); status = xprt->ops->send_request(task);
if (status == 0) { if (status == 0) {
dprintk("RPC: %5u xmit complete\n", task->tk_pid); dprintk("RPC: %5u xmit complete\n", task->tk_pid);
......
...@@ -579,7 +579,6 @@ static int xs_udp_send_request(struct rpc_task *task) ...@@ -579,7 +579,6 @@ static int xs_udp_send_request(struct rpc_task *task)
req->rq_svec->iov_base, req->rq_svec->iov_base,
req->rq_svec->iov_len); req->rq_svec->iov_len);
req->rq_xtime = jiffies;
status = xs_sendpages(transport->sock, status = xs_sendpages(transport->sock,
xs_addr(xprt), xs_addr(xprt),
xprt->addrlen, xdr, xprt->addrlen, xdr,
...@@ -671,7 +670,6 @@ static int xs_tcp_send_request(struct rpc_task *task) ...@@ -671,7 +670,6 @@ static int xs_tcp_send_request(struct rpc_task *task)
* to cope with writespace callbacks arriving _after_ we have * to cope with writespace callbacks arriving _after_ we have
* called sendmsg(). */ * called sendmsg(). */
while (1) { while (1) {
req->rq_xtime = jiffies;
status = xs_sendpages(transport->sock, status = xs_sendpages(transport->sock,
NULL, 0, xdr, req->rq_bytes_sent); NULL, 0, xdr, req->rq_bytes_sent);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment