Merge branch 'devel' into next

Conflicts:

	fs/nfs/file.c

Fix up the conflict with Jon Corbet's bkl-removal tree
This commit is contained in:
Trond Myklebust 2008-07-15 18:34:16 -04:00
commit e89e896d31
35 changed files with 1851 additions and 1166 deletions

View File

@ -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
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
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 To mount file systems exported by NFS servers, you also need to
the Coda file system; see "Coda file system support" below. 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.
If you say Y here, you should have said Y to TCP/IP networking also. Below you can choose which versions of the NFS protocol are
This option would enlarge your kernel by about 27 KB. available in the kernel to mount NFS servers. Support for NFS
version 2 (RFC 1094) is always available when NFS_FS is selected.
To compile this file system support as a module, choose M here: the To configure a system which mounts its root file system via NFS
module will be called nfs. at boot time, say Y here, select "Kernel level IP
autoconfiguration" in the NETWORK menu, and select "Root file
system on NFS" below. You cannot compile this file system as a
module in this case.
If you are configuring a diskless machine which will mount its root If unsure, say N.
file system over NFS at boot time, say Y here and to "Kernel
level IP autoconfiguration" above and to "Root file system on NFS"
below. You cannot compile this driver as a module in this case.
There are two packages designed for booting diskless machines over
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.
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
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
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

View File

@ -430,7 +430,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;

View File

@ -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)
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)
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 @@ out:
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 @@ out_err:
} }
/* /*
* 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)

View File

@ -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,
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();
} }
} }

View File

@ -133,8 +133,11 @@ 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(); lock_kernel();
/* Call generic open code in order to cache credentials */ /* Call generic open code in order to cache credentials */
@ -528,7 +531,7 @@ 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);
@ -595,7 +598,7 @@ out:
unlock_kernel(); 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 +606,15 @@ out:
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 +630,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 +640,11 @@ out:
*/ */
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;
} }

View File

@ -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,
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);

View File

@ -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 = {
.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,13 +119,17 @@ 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(); lock_kernel();
res = NFS_PROTO(inode)->file_open(inode, filp); res = nfs_open(inode, filp);
unlock_kernel(); unlock_kernel();
return res; return res;
} }
@ -133,11 +137,17 @@ nfs_file_open(struct inode *inode, struct file *filp)
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 +181,12 @@ force_reval:
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 +201,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 +227,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 +263,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 +283,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 +300,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 +318,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 +348,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,6 +373,31 @@ 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;
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);
}
lock_kernel(); lock_kernel();
status = nfs_updatepage(file, page, offset, copied); status = nfs_updatepage(file, page, offset, copied);
unlock_kernel(); unlock_kernel();
@ -362,6 +412,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 +422,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 +455,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 +515,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 +651,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 +662,35 @@ out:
*/ */
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 +698,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 +723,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;
} }

View File

@ -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)
} }
} }
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 @@ out_no_inode:
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,7 +367,7 @@ 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(); lock_kernel();

View File

@ -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;

View File

@ -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
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)
free_percpu(stats); free_percpu(stats);
} }
#endif #endif /* _NFS_IOSTAT */
#endif

View File

@ -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)
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,
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. */

View File

@ -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,
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
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 @@ again:
/* 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
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 @@ out:
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); goto out;
nfs_post_op_update_inode(dir, &dir_attr);
if (status != 0) data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR];
goto out; data->arg.mkdir.fh = NFS_FH(dir);
status = nfs_instantiate(dentry, &fhandle, &fattr); 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
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 = {
.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,
}; };

View File

@ -1139,8 +1139,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 +1155,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 +1168,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 +1178,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 +1649,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;
state = ctx->state;
status = nfs4_do_setattr(inode, fattr, sattr, state); ctx = nfs_file_open_context(sattr->ia_file);
cred = ctx->cred;
state = ctx->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 +1895,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 +1913,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 +2078,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 +2173,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 +2253,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 +2695,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 +2724,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 +2759,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 +2924,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 +3716,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,
}; };

View File

@ -1,6 +1,4 @@
/* /*
* $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)
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 */

View File

@ -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)
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 = {
.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,
}; };

File diff suppressed because it is too large Load Diff

View File

@ -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);
@ -169,29 +166,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 +242,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 +326,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 +352,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 +388,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 +400,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 +410,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 +432,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 +448,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 +511,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,108 +550,122 @@ 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
* A request for the page we wish to update
*/
if (new) {
if (radix_tree_preload(GFP_NOFS)) {
nfs_release_request(new);
return ERR_PTR(-ENOMEM);
}
}
spin_lock(&inode->i_lock);
req = nfs_page_find_request_locked(page); req = nfs_page_find_request_locked(page);
if (req) { if (req == NULL)
if (!nfs_set_page_tag_locked(req)) { goto out_unlock;
int error;
spin_unlock(&inode->i_lock); rqend = req->wb_offset + req->wb_bytes;
error = nfs_wait_on_request(req); /*
nfs_release_request(req); * Tell the caller to flush out the request if
if (error < 0) { * the offsets are non-contiguous.
if (new) { * Note: nfs_flush_incompatible() will already
radix_tree_preload_end(); * have flushed out requests having wrong owners.
nfs_release_request(new); */
} if (offset > rqend
return ERR_PTR(error); || end < req->wb_offset)
} goto out_flushme;
continue;
} if (nfs_set_page_tag_locked(req))
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;
out_flushme:
spin_unlock(&inode->i_lock);
nfs_release_request(req);
error = nfs_wb_page(inode, page);
out_err:
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;
zero_page: }
/* If this page might potentially be marked as up to date,
* then we need to zero any uninitalised data. */ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
if (req->wb_pgbase == 0 && req->wb_bytes != PAGE_CACHE_SIZE unsigned int offset, unsigned int count)
&& !PageUptodate(req->wb_page)) {
zero_user_segment(req->wb_page, req->wb_bytes, PAGE_CACHE_SIZE); struct nfs_page *req;
return 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)
@ -685,8 +685,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 +720,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 +743,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 +751,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 +828,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 +972,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 +1052,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 +1073,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 +1126,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 +1290,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 +1308,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 +1466,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;
} }

View File

@ -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],

View File

@ -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);

View File

@ -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:

119
include/linux/nfs_iostat.h Normal file
View File

@ -0,0 +1,119 @@
/*
* 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 */

View File

@ -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 {

View File

@ -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 *);
}; };

View File

@ -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 {
#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 *);
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 *);

View File

@ -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

View File

@ -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)
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)
{ {
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)
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)
{ {
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)
{ {
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)
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)
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);

View File

@ -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,
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));

View File

@ -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,
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));

View File

@ -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);

View File

@ -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);

View File

@ -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);
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);
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)
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)
} }
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)
{ {
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)
* 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)
(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)
} }
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)
/* 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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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
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

View File

@ -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 {
#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,27 +81,22 @@ 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 void rpcb_map_release(void *data)
{
struct rpcbind_args *map = data;
xprt_put(map->r_xprt);
kfree(map);
}
static const struct rpc_call_ops rpcb_getport_ops = { static const struct rpc_call_ops rpcb_getport_ops = {
.rpc_call_done = rpcb_getport_done, .rpc_call_done = rpcb_getport_done,
.rpc_release = rpcb_map_release, .rpc_release = rpcb_map_release,
@ -108,9 +108,46 @@ static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status)
rpc_wake_up_status(&xprt->binding, status); rpc_wake_up_status(&xprt->binding, status);
} }
static void rpcb_map_release(void *data)
{
struct rpcbind_args *map = data;
rpcb_wake_rpcbind_waiters(map->r_xprt, map->r_status);
xprt_put(map->r_xprt);
kfree(map);
}
static const struct sockaddr_in rpcb_inaddr_loopback = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
.sin_port = htons(RPCBIND_PORT),
};
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)
{
struct rpc_create_args args = {
.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,
.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,
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
* *
* port == 0 means unregister, port != 0 means register. * 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.
* *
* This routine supports only rpcbind version 2. * 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.
*
* This function uses rpcbind protocol version 2 to contact the
* local rpcbind daemon.
*
* 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)
.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];
return error; /* 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);
}
/*
* 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)
__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)
/* 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)
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)
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)
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)
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,
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,
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 @@ out_err:
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
}; };

View File

@ -626,19 +626,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);
} }

View File

@ -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)
} }
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)
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);

View File

@ -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)
* 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);