Merge git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (82 commits) NFSv4: Remove BKL from the nfsv4 state recovery SUNRPC: Remove the BKL from the callback functions NFS: Remove BKL from the readdir code NFS: Remove BKL from the symlink code NFS: Remove BKL from the sillydelete operations NFS: Remove the BKL from the rename, rmdir and unlink operations NFS: Remove BKL from NFS lookup code NFS: Remove the BKL from nfs_link() NFS: Remove the BKL from the inode creation operations NFS: Remove BKL usage from open() NFS: Remove BKL usage from the write path NFS: Remove the BKL from the permission checking code NFS: Remove attribute update related BKL references NFS: Remove BKL requirement from attribute updates NFS: Protect inode->i_nlink updates using inode->i_lock nfs: set correct fl_len in nlmclnt_test() SUNRPC: Support registering IPv6 interfaces with local rpcbind daemon SUNRPC: Refactor rpcb_register to make rpcbindv4 support easier SUNRPC: None of rpcb_create's callers wants a privileged source port SUNRPC: Introduce a specific rpcb_create for contacting localhost ...
This commit is contained in:
commit
8df1b049bc
136
fs/Kconfig
136
fs/Kconfig
@ -1544,10 +1544,6 @@ config UFS_FS
|
||||
The recently released UFS2 variant (used in FreeBSD 5.x) is
|
||||
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
|
||||
good portable way to transport files and directories between unixes
|
||||
(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
|
||||
filesystem-related networking code, such as NFS daemon and
|
||||
RPCSEC security modules.
|
||||
|
||||
This option alone does not add any kernel code.
|
||||
|
||||
If you say N, all options in this submenu will be skipped and
|
||||
@ -1595,76 +1592,92 @@ menuconfig NETWORK_FILESYSTEMS
|
||||
if NETWORK_FILESYSTEMS
|
||||
|
||||
config NFS_FS
|
||||
tristate "NFS file system support"
|
||||
tristate "NFS client support"
|
||||
depends on INET
|
||||
select LOCKD
|
||||
select SUNRPC
|
||||
select NFS_ACL_SUPPORT if NFS_V3_ACL
|
||||
help
|
||||
If you are connected to some other (usually local) Unix computer
|
||||
(using SLIP, PLIP, PPP or Ethernet) and want to mount files residing
|
||||
on that computer (the NFS server) using the Network File Sharing
|
||||
protocol, say Y. "Mounting files" means that the client can access
|
||||
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.
|
||||
Choose Y here if you want to access files residing on other
|
||||
computers using Sun's Network File System protocol. To compile
|
||||
this file system support as a module, choose M here: the module
|
||||
will be called nfs.
|
||||
|
||||
A superior but less widely used alternative to NFS is provided by
|
||||
the Coda file system; see "Coda file system support" below.
|
||||
To mount file systems exported by NFS servers, you also need to
|
||||
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.
|
||||
This option would enlarge your kernel by about 27 KB.
|
||||
Below you can choose which versions of the NFS protocol are
|
||||
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
|
||||
module will be called nfs.
|
||||
To configure a system which mounts its root file system via 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
|
||||
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.
|
||||
If unsure, say N.
|
||||
|
||||
config NFS_V3
|
||||
bool "Provide NFSv3 client support"
|
||||
bool "NFS client support for NFS version 3"
|
||||
depends on NFS_FS
|
||||
help
|
||||
Say Y here if you want your NFS client to be able to speak version
|
||||
3 of the NFS protocol.
|
||||
This option enables support for version 3 of the NFS protocol
|
||||
(RFC 1813) in the kernel's NFS client.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
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
|
||||
help
|
||||
Implement the NFSv3 ACL protocol extension for manipulating POSIX
|
||||
Access Control Lists. The server should also be compiled with
|
||||
the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option.
|
||||
Some NFS servers support an auxiliary NFSv3 ACL protocol that
|
||||
Sun added to Solaris but never became an official part of the
|
||||
NFS version 3 protocol. This protocol extension allows
|
||||
applications on NFS clients to manipulate POSIX Access Control
|
||||
Lists on files residing on NFS servers. NFS servers enforce
|
||||
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.
|
||||
|
||||
config NFS_V4
|
||||
bool "Provide NFSv4 client support (EXPERIMENTAL)"
|
||||
bool "NFS client support for NFS version 4 (EXPERIMENTAL)"
|
||||
depends on NFS_FS && EXPERIMENTAL
|
||||
select RPCSEC_GSS_KRB5
|
||||
help
|
||||
Say Y here if you want your NFS client to be able to speak the newer
|
||||
version 4 of the NFS protocol.
|
||||
This option enables support for version 4 of the NFS protocol
|
||||
(RFC 3530) in the kernel's NFS client.
|
||||
|
||||
Note: Requires auxiliary userspace daemons which may be found on
|
||||
http://www.citi.umich.edu/projects/nfsv4/
|
||||
To mount NFS servers using NFSv4, you also need to install user
|
||||
space programs which can be found in the Linux nfs-utils package,
|
||||
available from http://linux-nfs.org/.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config ROOT_NFS
|
||||
bool "Root file system on NFS"
|
||||
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
|
||||
tristate "NFS server support"
|
||||
depends on INET
|
||||
@ -1746,20 +1759,6 @@ config NFSD_V4
|
||||
|
||||
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
|
||||
tristate
|
||||
|
||||
@ -1800,27 +1799,6 @@ config SUNRPC_XPRT_RDMA
|
||||
|
||||
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
|
||||
tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
|
||||
depends on SUNRPC && EXPERIMENTAL
|
||||
|
@ -224,7 +224,9 @@ void nlm_release_call(struct nlm_rqst *call)
|
||||
|
||||
static void nlmclnt_rpc_release(void *data)
|
||||
{
|
||||
lock_kernel();
|
||||
nlm_release_call(data);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static int nlm_wait_on_grace(wait_queue_head_t *queue)
|
||||
@ -430,7 +432,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
|
||||
* Report the conflicting lock back to the application.
|
||||
*/
|
||||
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_pid = 0;
|
||||
break;
|
||||
@ -710,7 +712,9 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
|
||||
die:
|
||||
return;
|
||||
retry_rebind:
|
||||
lock_kernel();
|
||||
nlm_rebind_host(req->a_host);
|
||||
unlock_kernel();
|
||||
retry_unlock:
|
||||
rpc_restart_call(task);
|
||||
}
|
||||
@ -788,7 +792,9 @@ static void nlmclnt_cancel_callback(struct rpc_task *task, void *data)
|
||||
/* Don't ever retry more than 3 times */
|
||||
if (req->a_retries++ >= NLMCLNT_MAX_RETRIES)
|
||||
goto die;
|
||||
lock_kernel();
|
||||
nlm_rebind_host(req->a_host);
|
||||
unlock_kernel();
|
||||
rpc_restart_call(task);
|
||||
rpc_delay(task, 30 * HZ);
|
||||
}
|
||||
|
@ -248,7 +248,9 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
|
||||
|
||||
static void nlm4svc_callback_release(void *data)
|
||||
{
|
||||
lock_kernel();
|
||||
nlm_release_call(data);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlm4svc_callback_ops = {
|
||||
|
@ -795,6 +795,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
|
||||
|
||||
dprintk("lockd: GRANT_MSG RPC callback\n");
|
||||
|
||||
lock_kernel();
|
||||
/* if the block is not on a list at this point then it has
|
||||
* been invalidated. Don't try to requeue it.
|
||||
*
|
||||
@ -804,7 +805,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
|
||||
* for nlm_blocked?
|
||||
*/
|
||||
if (list_empty(&block->b_list))
|
||||
return;
|
||||
goto out;
|
||||
|
||||
/* Technically, we should down the file semaphore here. Since we
|
||||
* move the block towards the head of the queue only, no harm
|
||||
@ -818,13 +819,17 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
|
||||
}
|
||||
nlmsvc_insert_block(block, timeout);
|
||||
svc_wake_up(block->b_daemon);
|
||||
out:
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static void nlmsvc_grant_release(void *data)
|
||||
{
|
||||
struct nlm_rqst *call = data;
|
||||
|
||||
lock_kernel();
|
||||
nlmsvc_release_block(call->a_block);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlmsvc_grant_ops = {
|
||||
|
@ -278,7 +278,9 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
|
||||
|
||||
static void nlmsvc_callback_release(void *data)
|
||||
{
|
||||
lock_kernel();
|
||||
nlm_release_call(data);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static const struct rpc_call_ops nlmsvc_callback_ops = {
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
struct nfs_callback_data {
|
||||
unsigned int users;
|
||||
struct svc_serv *serv;
|
||||
struct svc_rqst *rqst;
|
||||
struct task_struct *task;
|
||||
};
|
||||
|
||||
@ -91,21 +91,17 @@ nfs_callback_svc(void *vrqstp)
|
||||
svc_process(rqstp);
|
||||
}
|
||||
unlock_kernel();
|
||||
nfs_callback_info.task = NULL;
|
||||
svc_exit_thread(rqstp);
|
||||
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)
|
||||
{
|
||||
struct svc_serv *serv = NULL;
|
||||
struct svc_rqst *rqstp;
|
||||
int ret = 0;
|
||||
|
||||
lock_kernel();
|
||||
mutex_lock(&nfs_callback_mutex);
|
||||
if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
|
||||
goto out;
|
||||
@ -121,22 +117,23 @@ int nfs_callback_up(void)
|
||||
nfs_callback_tcpport = ret;
|
||||
dprintk("Callback port = 0x%x\n", nfs_callback_tcpport);
|
||||
|
||||
rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
|
||||
if (IS_ERR(rqstp)) {
|
||||
ret = PTR_ERR(rqstp);
|
||||
nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
|
||||
if (IS_ERR(nfs_callback_info.rqst)) {
|
||||
ret = PTR_ERR(nfs_callback_info.rqst);
|
||||
nfs_callback_info.rqst = NULL;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
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");
|
||||
if (IS_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;
|
||||
svc_exit_thread(rqstp);
|
||||
goto out_err;
|
||||
}
|
||||
out:
|
||||
@ -149,7 +146,6 @@ int nfs_callback_up(void)
|
||||
if (serv)
|
||||
svc_destroy(serv);
|
||||
mutex_unlock(&nfs_callback_mutex);
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
out_err:
|
||||
dprintk("Couldn't create callback socket or server thread; err = %d\n",
|
||||
@ -159,17 +155,19 @@ int nfs_callback_up(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Kill the server process if it is not already down.
|
||||
* Kill the callback thread if it's no longer being used.
|
||||
*/
|
||||
void nfs_callback_down(void)
|
||||
{
|
||||
lock_kernel();
|
||||
mutex_lock(&nfs_callback_mutex);
|
||||
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);
|
||||
svc_exit_thread(nfs_callback_info.rqst);
|
||||
nfs_callback_info.rqst = NULL;
|
||||
nfs_callback_info.task = NULL;
|
||||
}
|
||||
mutex_unlock(&nfs_callback_mutex);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
static int nfs_callback_authenticate(struct svc_rqst *rqstp)
|
||||
|
@ -431,14 +431,14 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
|
||||
{
|
||||
to->to_initval = timeo * HZ / 10;
|
||||
to->to_retries = retrans;
|
||||
if (!to->to_retries)
|
||||
to->to_retries = 2;
|
||||
|
||||
switch (proto) {
|
||||
case XPRT_TRANSPORT_TCP:
|
||||
case XPRT_TRANSPORT_RDMA:
|
||||
if (to->to_retries == 0)
|
||||
to->to_retries = NFS_DEF_TCP_RETRANS;
|
||||
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)
|
||||
to->to_initval = NFS_MAX_TCP_TIMEOUT;
|
||||
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;
|
||||
break;
|
||||
case XPRT_TRANSPORT_UDP:
|
||||
default:
|
||||
if (to->to_retries == 0)
|
||||
to->to_retries = NFS_DEF_UDP_RETRANS;
|
||||
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)
|
||||
to->to_initval = NFS_MAX_UDP_TIMEOUT;
|
||||
to->to_maxval = NFS_MAX_UDP_TIMEOUT;
|
||||
to->to_exponential = 1;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
|
88
fs/nfs/dir.c
88
fs/nfs/dir.c
@ -133,13 +133,14 @@ nfs_opendir(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int res;
|
||||
|
||||
dfprintk(VFS, "NFS: opendir(%s/%ld)\n",
|
||||
inode->i_sb->s_id, inode->i_ino);
|
||||
dfprintk(FILE, "NFS: open dir(%s/%s)\n",
|
||||
filp->f_path.dentry->d_parent->d_name.name,
|
||||
filp->f_path.dentry->d_name.name);
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSOPEN);
|
||||
|
||||
lock_kernel();
|
||||
/* Call generic open code in order to cache credentials */
|
||||
res = nfs_open(inode, filp);
|
||||
unlock_kernel();
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -528,13 +529,11 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
struct nfs_fattr fattr;
|
||||
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,
|
||||
(long long)filp->f_pos);
|
||||
nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
|
||||
|
||||
lock_kernel();
|
||||
|
||||
/*
|
||||
* filp->f_pos points to the dirent entry number.
|
||||
* *desc->dir_cookie has the cookie for the next entry. We have
|
||||
@ -592,10 +591,9 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
}
|
||||
out:
|
||||
nfs_unblock_sillyrename(dentry);
|
||||
unlock_kernel();
|
||||
if (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,
|
||||
res);
|
||||
return res;
|
||||
@ -603,7 +601,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
|
||||
static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
|
||||
{
|
||||
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) {
|
||||
case 1:
|
||||
offset += filp->f_pos;
|
||||
@ -619,7 +625,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
|
||||
nfs_file_open_context(filp)->dir_cookie = 0;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return offset;
|
||||
}
|
||||
|
||||
@ -629,10 +635,11 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
|
||||
*/
|
||||
static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
|
||||
{
|
||||
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,
|
||||
datasync);
|
||||
|
||||
nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -767,7 +774,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
|
||||
struct nfs_fattr fattr;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
lock_kernel();
|
||||
dir = parent->d_inode;
|
||||
nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
|
||||
inode = dentry->d_inode;
|
||||
@ -805,7 +811,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
|
||||
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
out_valid:
|
||||
unlock_kernel();
|
||||
dput(parent);
|
||||
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
|
||||
__func__, dentry->d_parent->d_name.name,
|
||||
@ -824,7 +829,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
|
||||
shrink_dcache_parent(dentry);
|
||||
}
|
||||
d_drop(dentry);
|
||||
unlock_kernel();
|
||||
dput(parent);
|
||||
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n",
|
||||
__func__, dentry->d_parent->d_name.name,
|
||||
@ -858,6 +862,14 @@ static int nfs_dentry_delete(struct dentry *dentry)
|
||||
|
||||
}
|
||||
|
||||
static void nfs_drop_nlink(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
if (inode->i_nlink > 0)
|
||||
drop_nlink(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the dentry loses inode.
|
||||
* We use it to clean up silly-renamed files.
|
||||
@ -869,10 +881,8 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
|
||||
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
|
||||
|
||||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
|
||||
lock_kernel();
|
||||
drop_nlink(inode);
|
||||
nfs_complete_unlink(dentry, inode);
|
||||
unlock_kernel();
|
||||
}
|
||||
iput(inode);
|
||||
}
|
||||
@ -903,8 +913,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
|
||||
res = ERR_PTR(-ENOMEM);
|
||||
dentry->d_op = NFS_PROTO(dir)->dentry_ops;
|
||||
|
||||
lock_kernel();
|
||||
|
||||
/*
|
||||
* If we're doing an exclusive create, optimize away the lookup
|
||||
* but don't hash the dentry.
|
||||
@ -912,7 +920,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
|
||||
if (nfs_is_exclusive_create(dir, nd)) {
|
||||
d_instantiate(dentry, NULL);
|
||||
res = NULL;
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
|
||||
parent = dentry->d_parent;
|
||||
@ -940,8 +948,6 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
out_unblock_sillyrename:
|
||||
nfs_unblock_sillyrename(parent);
|
||||
out_unlock:
|
||||
unlock_kernel();
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
@ -999,9 +1005,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
|
||||
}
|
||||
|
||||
/* Open the file on the server */
|
||||
lock_kernel();
|
||||
res = nfs4_atomic_open(dir, dentry, nd);
|
||||
unlock_kernel();
|
||||
if (IS_ERR(res)) {
|
||||
error = PTR_ERR(res);
|
||||
switch (error) {
|
||||
@ -1063,9 +1067,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
* operations that change the directory. We therefore save the
|
||||
* change attribute *before* we do the RPC call.
|
||||
*/
|
||||
lock_kernel();
|
||||
ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
|
||||
unlock_kernel();
|
||||
out:
|
||||
dput(parent);
|
||||
if (!ret)
|
||||
@ -1218,14 +1220,11 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||
if ((nd->flags & LOOKUP_CREATE) != 0)
|
||||
open_flags = nd->intent.open.flags;
|
||||
|
||||
lock_kernel();
|
||||
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
|
||||
if (error != 0)
|
||||
goto out_err;
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
out_err:
|
||||
unlock_kernel();
|
||||
d_drop(dentry);
|
||||
return error;
|
||||
}
|
||||
@ -1248,14 +1247,11 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
|
||||
attr.ia_mode = mode;
|
||||
attr.ia_valid = ATTR_MODE;
|
||||
|
||||
lock_kernel();
|
||||
status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
|
||||
if (status != 0)
|
||||
goto out_err;
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
out_err:
|
||||
unlock_kernel();
|
||||
d_drop(dentry);
|
||||
return status;
|
||||
}
|
||||
@ -1274,15 +1270,12 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
attr.ia_valid = ATTR_MODE;
|
||||
attr.ia_mode = mode | S_IFDIR;
|
||||
|
||||
lock_kernel();
|
||||
error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
|
||||
if (error != 0)
|
||||
goto out_err;
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
out_err:
|
||||
d_drop(dentry);
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1299,14 +1292,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
|
||||
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
|
||||
|
||||
lock_kernel();
|
||||
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
|
||||
/* Ensure the VFS deletes this inode */
|
||||
if (error == 0 && dentry->d_inode != NULL)
|
||||
clear_nlink(dentry->d_inode);
|
||||
else if (error == -ENOENT)
|
||||
nfs_dentry_handle_enoent(dentry);
|
||||
unlock_kernel();
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1408,7 +1399,7 @@ static int nfs_safe_remove(struct dentry *dentry)
|
||||
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
|
||||
/* The VFS may want to delete this inode */
|
||||
if (error == 0)
|
||||
drop_nlink(inode);
|
||||
nfs_drop_nlink(inode);
|
||||
nfs_mark_for_revalidate(inode);
|
||||
} else
|
||||
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
|
||||
@ -1431,7 +1422,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
|
||||
dir->i_ino, dentry->d_name.name);
|
||||
|
||||
lock_kernel();
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (atomic_read(&dentry->d_count) > 1) {
|
||||
@ -1440,7 +1430,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
/* Start asynchronous writeout of the inode */
|
||||
write_inode_now(dentry->d_inode, 0);
|
||||
error = nfs_sillyrename(dir, dentry);
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
if (!d_unhashed(dentry)) {
|
||||
@ -1454,7 +1443,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
} else if (need_rehash)
|
||||
d_rehash(dentry);
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1491,13 +1479,9 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
|
||||
attr.ia_mode = S_IFLNK | S_IRWXUGO;
|
||||
attr.ia_valid = ATTR_MODE;
|
||||
|
||||
lock_kernel();
|
||||
|
||||
page = alloc_page(GFP_HIGHUSER);
|
||||
if (!page) {
|
||||
unlock_kernel();
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kaddr = kmap_atomic(page, KM_USER0);
|
||||
memcpy(kaddr, symname, pathlen);
|
||||
@ -1512,7 +1496,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
|
||||
dentry->d_name.name, symname, error);
|
||||
d_drop(dentry);
|
||||
__free_page(page);
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1530,7 +1513,6 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
|
||||
} else
|
||||
__free_page(page);
|
||||
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1544,14 +1526,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
|
||||
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
|
||||
lock_kernel();
|
||||
d_drop(dentry);
|
||||
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
|
||||
if (error == 0) {
|
||||
atomic_inc(&inode->i_count);
|
||||
d_add(dentry, inode);
|
||||
}
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1591,7 +1571,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
* To prevent any new references to the target during the rename,
|
||||
* we unhash the dentry and free the inode in advance.
|
||||
*/
|
||||
lock_kernel();
|
||||
if (!d_unhashed(new_dentry)) {
|
||||
d_drop(new_dentry);
|
||||
rehash = new_dentry;
|
||||
@ -1635,7 +1614,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
/* dentry still busy? */
|
||||
goto out;
|
||||
} else
|
||||
drop_nlink(new_inode);
|
||||
nfs_drop_nlink(new_inode);
|
||||
|
||||
go_ahead:
|
||||
/*
|
||||
@ -1669,7 +1648,6 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
/* new dentry created? */
|
||||
if (dentry)
|
||||
dput(dentry);
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1962,8 +1940,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
|
||||
}
|
||||
|
||||
force_lookup:
|
||||
lock_kernel();
|
||||
|
||||
if (!NFS_PROTO(inode)->access)
|
||||
goto out_notsup;
|
||||
|
||||
@ -1973,7 +1949,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
|
||||
put_rpccred(cred);
|
||||
} else
|
||||
res = PTR_ERR(cred);
|
||||
unlock_kernel();
|
||||
out:
|
||||
dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n",
|
||||
inode->i_sb->s_id, inode->i_ino, mask, res);
|
||||
@ -1982,7 +1957,6 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
|
||||
res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
if (res == 0)
|
||||
res = generic_permission(inode, mask, NULL);
|
||||
unlock_kernel();
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -890,7 +890,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
|
||||
count = iov_length(iov, nr_segs);
|
||||
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_name.name,
|
||||
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);
|
||||
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_name.name,
|
||||
count, (long long) pos);
|
||||
|
155
fs/nfs/file.c
155
fs/nfs/file.c
@ -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,
|
||||
unsigned long nr_segs, loff_t pos);
|
||||
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_lock(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,
|
||||
.flush = nfs_file_flush,
|
||||
.release = nfs_file_release,
|
||||
.fsync = nfs_fsync,
|
||||
.fsync = nfs_file_fsync,
|
||||
.lock = nfs_lock,
|
||||
.flock = nfs_flock,
|
||||
.splice_read = nfs_file_splice_read,
|
||||
@ -119,25 +119,33 @@ nfs_file_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
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);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSOPEN);
|
||||
lock_kernel();
|
||||
res = NFS_PROTO(inode)->file_open(inode, filp);
|
||||
unlock_kernel();
|
||||
res = nfs_open(inode, filp);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
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 */
|
||||
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);
|
||||
return NFS_PROTO(inode)->file_release(inode, filp);
|
||||
return nfs_release(inode, filp);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,6 +179,12 @@ static int nfs_revalidate_file_size(struct inode *inode, struct file *filp)
|
||||
static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
|
||||
{
|
||||
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 */
|
||||
if (origin == SEEK_END) {
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
@ -185,7 +199,7 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* disk, but it retrieves and clears ctx->error after synching, despite
|
||||
@ -211,16 +225,18 @@ static int nfs_do_fsync(struct nfs_open_context *ctx, struct inode *inode)
|
||||
|
||||
/*
|
||||
* Flush all dirty pages, and check for write errors.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
nfs_file_flush(struct file *file, fl_owner_t id)
|
||||
{
|
||||
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;
|
||||
|
||||
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)
|
||||
return 0;
|
||||
@ -245,7 +261,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
|
||||
if (iocb->ki_filp->f_flags & O_DIRECT)
|
||||
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,
|
||||
(unsigned long) count, (unsigned long) pos);
|
||||
|
||||
@ -265,7 +281,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos,
|
||||
struct inode *inode = dentry->d_inode;
|
||||
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,
|
||||
(unsigned long) count, (unsigned long long) *ppos);
|
||||
|
||||
@ -282,7 +298,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
|
||||
struct inode *inode = dentry->d_inode;
|
||||
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);
|
||||
|
||||
status = nfs_revalidate_mapping(inode, file->f_mapping);
|
||||
@ -300,12 +316,14 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
|
||||
* whether any write errors occurred for this process.
|
||||
*/
|
||||
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 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);
|
||||
return nfs_do_fsync(ctx, inode);
|
||||
@ -328,6 +346,11 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
struct page *page;
|
||||
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);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
@ -348,9 +371,32 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
|
||||
unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
|
||||
int status;
|
||||
|
||||
lock_kernel();
|
||||
dfprintk(PAGECACHE, "NFS: write_end(%s/%s(%ld), %u@%lld)\n",
|
||||
file->f_path.dentry->d_parent->d_name.name,
|
||||
file->f_path.dentry->d_name.name,
|
||||
mapping->host->i_ino, len, (long long) pos);
|
||||
|
||||
/*
|
||||
* Zero any uninitialised parts of the page, and then mark the page
|
||||
* as up to date if it turns out that we're extending the file.
|
||||
*/
|
||||
if (!PageUptodate(page)) {
|
||||
unsigned pglen = nfs_page_length(page);
|
||||
unsigned end = offset + len;
|
||||
|
||||
if (pglen == 0) {
|
||||
zero_user_segments(page, 0, offset,
|
||||
end, PAGE_CACHE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
} else if (end >= pglen) {
|
||||
zero_user_segment(page, end, PAGE_CACHE_SIZE);
|
||||
if (offset == 0)
|
||||
SetPageUptodate(page);
|
||||
} else
|
||||
zero_user_segment(page, pglen, PAGE_CACHE_SIZE);
|
||||
}
|
||||
|
||||
status = nfs_updatepage(file, page, offset, copied);
|
||||
unlock_kernel();
|
||||
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
@ -362,6 +408,8 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
|
||||
|
||||
static void nfs_invalidate_page(struct page *page, unsigned long offset)
|
||||
{
|
||||
dfprintk(PAGECACHE, "NFS: invalidate_page(%p, %lu)\n", page, offset);
|
||||
|
||||
if (offset != 0)
|
||||
return;
|
||||
/* Cancel any unstarted writes on this page */
|
||||
@ -370,13 +418,20 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
|
||||
|
||||
static int nfs_release_page(struct page *page, gfp_t gfp)
|
||||
{
|
||||
dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page);
|
||||
|
||||
/* If PagePrivate() is set, then the page is not freeable */
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = {
|
||||
@ -396,13 +451,19 @@ const struct address_space_operations nfs_file_aops = {
|
||||
static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct page *page)
|
||||
{
|
||||
struct file *filp = vma->vm_file;
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
unsigned pagelen;
|
||||
int ret = -EINVAL;
|
||||
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);
|
||||
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;
|
||||
|
||||
ret = 0;
|
||||
@ -450,9 +511,9 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
if (iocb->ki_filp->f_flags & O_DIRECT)
|
||||
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,
|
||||
inode->i_ino, (unsigned long) count, (long long) pos);
|
||||
(unsigned long) count, (long long) pos);
|
||||
|
||||
result = -EBUSY;
|
||||
if (IS_SWAPFILE(inode))
|
||||
@ -586,7 +647,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
|
||||
* This makes locking act as a cache coherency point.
|
||||
*/
|
||||
nfs_sync_mapping(filp->f_mapping);
|
||||
nfs_zap_caches(inode);
|
||||
if (!nfs_have_delegation(inode, FMODE_READ))
|
||||
nfs_zap_caches(inode);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
@ -596,23 +658,35 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
|
||||
*/
|
||||
static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
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",
|
||||
inode->i_sb->s_id, inode->i_ino,
|
||||
dprintk("NFS: lock(%s/%s, t=%x, fl=%x, r=%lld:%lld)\n",
|
||||
filp->f_path.dentry->d_parent->d_name.name,
|
||||
filp->f_path.dentry->d_name.name,
|
||||
fl->fl_type, fl->fl_flags,
|
||||
(long long)fl->fl_start, (long long)fl->fl_end);
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSLOCK);
|
||||
|
||||
/* No mandatory locks over NFS */
|
||||
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))
|
||||
return do_getlk(filp, cmd, fl);
|
||||
if (fl->fl_type == F_UNLCK)
|
||||
return do_unlk(filp, cmd, fl);
|
||||
return do_setlk(filp, cmd, fl);
|
||||
ret = do_getlk(filp, cmd, fl);
|
||||
else if (fl->fl_type == F_UNLCK)
|
||||
ret = do_unlk(filp, cmd, fl);
|
||||
else
|
||||
ret = do_setlk(filp, cmd, fl);
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -620,9 +694,9 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
*/
|
||||
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",
|
||||
filp->f_path.dentry->d_inode->i_sb->s_id,
|
||||
filp->f_path.dentry->d_inode->i_ino,
|
||||
dprintk("NFS: flock(%s/%s, t=%x, fl=%x)\n",
|
||||
filp->f_path.dentry->d_parent->d_name.name,
|
||||
filp->f_path.dentry->d_name.name,
|
||||
fl->fl_type, fl->fl_flags);
|
||||
|
||||
/*
|
||||
@ -645,12 +719,15 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *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)
|
||||
{
|
||||
/*
|
||||
* There is no protocol support for leases, so we have no way
|
||||
* to implement them correctly in the face of opens by other
|
||||
* clients.
|
||||
*/
|
||||
dprintk("NFS: setlease(%s/%s, arg=%ld)\n",
|
||||
file->f_path.dentry->d_parent->d_name.name,
|
||||
file->f_path.dentry->d_name.name, arg);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -57,8 +57,6 @@ static int enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
|
||||
static void nfs_invalidate_inode(struct inode *);
|
||||
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 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 *);
|
||||
|
||||
@ -347,7 +345,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
||||
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
|
||||
nfs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
@ -369,10 +367,9 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
|
||||
/* Optimization: if the end result is no change, don't RPC */
|
||||
attr->ia_valid &= NFS_VALID_ATTRS;
|
||||
if (attr->ia_valid == 0)
|
||||
if ((attr->ia_valid & ~ATTR_FILE) == 0)
|
||||
return 0;
|
||||
|
||||
lock_kernel();
|
||||
/* Write all dirty data */
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
@ -386,10 +383,65 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
|
||||
if (error == 0)
|
||||
nfs_refresh_inode(inode, &fattr);
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_vmtruncate - unmap mappings "freed" by truncate() syscall
|
||||
* @inode: inode of the file used
|
||||
* @offset: file offset to start truncating
|
||||
*
|
||||
* This is a copy of the common vmtruncate, but with the locking
|
||||
* corrected to take into account the fact that NFS requires
|
||||
* inode->i_size to be updated under the inode->i_lock.
|
||||
*/
|
||||
static int nfs_vmtruncate(struct inode * inode, loff_t offset)
|
||||
{
|
||||
if (i_size_read(inode) < offset) {
|
||||
unsigned long limit;
|
||||
|
||||
limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
|
||||
if (limit != RLIM_INFINITY && offset > limit)
|
||||
goto out_sig;
|
||||
if (offset > inode->i_sb->s_maxbytes)
|
||||
goto out_big;
|
||||
spin_lock(&inode->i_lock);
|
||||
i_size_write(inode, offset);
|
||||
spin_unlock(&inode->i_lock);
|
||||
} else {
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
|
||||
/*
|
||||
* truncation of in-use swapfiles is disallowed - it would
|
||||
* cause subsequent swapout to scribble on the now-freed
|
||||
* blocks.
|
||||
*/
|
||||
if (IS_SWAPFILE(inode))
|
||||
return -ETXTBSY;
|
||||
spin_lock(&inode->i_lock);
|
||||
i_size_write(inode, offset);
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
/*
|
||||
* unmap_mapping_range is called twice, first simply for
|
||||
* efficiency so that truncate_inode_pages does fewer
|
||||
* single-page unmaps. However after this first call, and
|
||||
* before truncate_inode_pages finishes, it is possible for
|
||||
* private pages to be COWed, which remain after
|
||||
* truncate_inode_pages finishes, hence the second
|
||||
* unmap_mapping_range call must be made for correctness.
|
||||
*/
|
||||
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
|
||||
truncate_inode_pages(mapping, offset);
|
||||
unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
|
||||
}
|
||||
return 0;
|
||||
out_sig:
|
||||
send_sig(SIGXFSZ, current, 0);
|
||||
out_big:
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_setattr_update_inode - Update inode metadata after a setattr call.
|
||||
* @inode: pointer to struct inode
|
||||
@ -416,8 +468,7 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
|
||||
}
|
||||
if ((attr->ia_valid & ATTR_SIZE) != 0) {
|
||||
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
|
||||
inode->i_size = attr->ia_size;
|
||||
vmtruncate(inode, attr->ia_size);
|
||||
nfs_vmtruncate(inode, attr->ia_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,7 +698,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
|
||||
inode->i_sb->s_id, (long long)NFS_FILEID(inode));
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
|
||||
lock_kernel();
|
||||
if (is_bad_inode(inode))
|
||||
goto out_nowait;
|
||||
if (NFS_STALE(inode))
|
||||
@ -696,7 +746,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
|
||||
nfs_wake_up_inode(inode);
|
||||
|
||||
out_nowait:
|
||||
unlock_kernel();
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -831,9 +880,9 @@ static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_DATA;
|
||||
}
|
||||
if (inode->i_size == nfs_size_to_loff_t(fattr->pre_size) &&
|
||||
if (i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size) &&
|
||||
nfsi->npages == 0)
|
||||
inode->i_size = nfs_size_to_loff_t(fattr->size);
|
||||
i_size_write(inode, nfs_size_to_loff_t(fattr->size));
|
||||
}
|
||||
}
|
||||
|
||||
@ -974,7 +1023,7 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa
|
||||
(fattr->valid & NFS_ATTR_WCC) == 0) {
|
||||
memcpy(&fattr->pre_ctime, &inode->i_ctime, sizeof(fattr->pre_ctime));
|
||||
memcpy(&fattr->pre_mtime, &inode->i_mtime, sizeof(fattr->pre_mtime));
|
||||
fattr->pre_size = inode->i_size;
|
||||
fattr->pre_size = i_size_read(inode);
|
||||
fattr->valid |= NFS_ATTR_WCC;
|
||||
}
|
||||
return nfs_post_op_update_inode(inode, fattr);
|
||||
@ -1059,7 +1108,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
/* Do we perhaps have any outstanding writes, or has
|
||||
* the file grown beyond our last write? */
|
||||
if (nfsi->npages == 0 || new_isize > cur_isize) {
|
||||
inode->i_size = new_isize;
|
||||
i_size_write(inode, new_isize);
|
||||
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
|
||||
}
|
||||
dprintk("NFS: isize change on server for file %s/%ld\n",
|
||||
|
@ -150,6 +150,7 @@ extern void nfs_clear_inode(struct inode *);
|
||||
#ifdef CONFIG_NFS_V4
|
||||
extern void nfs4_clear_inode(struct inode *);
|
||||
#endif
|
||||
void nfs_zap_acl_cache(struct inode *inode);
|
||||
|
||||
/* super.c */
|
||||
extern struct file_system_type nfs_xdev_fs_type;
|
||||
|
119
fs/nfs/iostat.h
119
fs/nfs/iostat.h
@ -5,135 +5,41 @@
|
||||
*
|
||||
* 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
|
||||
#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/cache.h>
|
||||
#include <linux/nfs_iostat.h>
|
||||
|
||||
struct nfs_iostats {
|
||||
unsigned long long bytes[__NFSIOS_BYTESMAX];
|
||||
unsigned long events[__NFSIOS_COUNTSMAX];
|
||||
} ____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;
|
||||
int cpu;
|
||||
|
||||
cpu = get_cpu();
|
||||
iostats = per_cpu_ptr(server->io_stats, cpu);
|
||||
iostats->events[stat] ++;
|
||||
iostats->events[stat]++;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
int cpu;
|
||||
@ -144,7 +50,9 @@ static inline void nfs_add_server_stats(struct nfs_server *server, enum nfs_stat
|
||||
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);
|
||||
}
|
||||
@ -160,5 +68,4 @@ static inline void nfs_free_iostats(struct nfs_iostats *stats)
|
||||
free_percpu(stats);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif /* _NFS_IOSTAT */
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include <linux/nfsacl.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_PROC
|
||||
|
||||
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);
|
||||
if (status < 0)
|
||||
return ERR_PTR(status);
|
||||
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
|
||||
nfs_zap_acl_cache(inode);
|
||||
acl = nfs3_get_cached_acl(inode, type);
|
||||
if (acl != ERR_PTR(-EAGAIN))
|
||||
return acl;
|
||||
@ -319,9 +323,8 @@ static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
|
||||
dprintk("NFS call setacl\n");
|
||||
msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL];
|
||||
status = rpc_call_sync(server->client_acl, &msg, 0);
|
||||
spin_lock(&inode->i_lock);
|
||||
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS;
|
||||
spin_unlock(&inode->i_lock);
|
||||
nfs_access_zap_cache(inode);
|
||||
nfs_zap_acl_cache(inode);
|
||||
dprintk("NFS reply setacl: %d\n", status);
|
||||
|
||||
/* pages may have been allocated at the xdr layer. */
|
||||
|
@ -129,6 +129,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||
int status;
|
||||
|
||||
dprintk("NFS call setattr\n");
|
||||
if (sattr->ia_valid & ATTR_FILE)
|
||||
msg.rpc_cred = nfs_file_cred(sattr->ia_file);
|
||||
nfs_fattr_init(fattr);
|
||||
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
|
||||
if (status == 0)
|
||||
@ -248,6 +250,53 @@ static int nfs3_proc_readlink(struct inode *inode, struct page *page,
|
||||
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.
|
||||
* 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,
|
||||
int flags, struct nameidata *nd)
|
||||
{
|
||||
struct nfs_fh fhandle;
|
||||
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,
|
||||
};
|
||||
struct nfs3_createdata *data;
|
||||
mode_t mode = sattr->ia_mode;
|
||||
int status;
|
||||
int status = -ENOMEM;
|
||||
|
||||
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) {
|
||||
arg.createmode = NFS3_CREATE_EXCLUSIVE;
|
||||
arg.verifier[0] = jiffies;
|
||||
arg.verifier[1] = current->pid;
|
||||
data->arg.create.createmode = NFS3_CREATE_EXCLUSIVE;
|
||||
data->arg.create.verifier[0] = jiffies;
|
||||
data->arg.create.verifier[1] = current->pid;
|
||||
}
|
||||
|
||||
sattr->ia_mode &= ~current->fs->umask;
|
||||
|
||||
again:
|
||||
nfs_fattr_init(&dir_attr);
|
||||
nfs_fattr_init(&fattr);
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
nfs_refresh_inode(dir, &dir_attr);
|
||||
for (;;) {
|
||||
status = nfs3_do_create(dir, dentry, data);
|
||||
|
||||
/* If the server doesn't support the exclusive creation semantics,
|
||||
* try again with simple 'guarded' mode. */
|
||||
if (status == -ENOTSUPP) {
|
||||
switch (arg.createmode) {
|
||||
if (status != -ENOTSUPP)
|
||||
break;
|
||||
/* If the server doesn't support the exclusive creation
|
||||
* semantics, try again with simple 'guarded' mode. */
|
||||
switch (data->arg.create.createmode) {
|
||||
case NFS3_CREATE_EXCLUSIVE:
|
||||
arg.createmode = NFS3_CREATE_GUARDED;
|
||||
data->arg.create.createmode = NFS3_CREATE_GUARDED;
|
||||
break;
|
||||
|
||||
case NFS3_CREATE_GUARDED:
|
||||
arg.createmode = NFS3_CREATE_UNCHECKED;
|
||||
data->arg.create.createmode = NFS3_CREATE_UNCHECKED;
|
||||
break;
|
||||
|
||||
case NFS3_CREATE_UNCHECKED:
|
||||
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)
|
||||
goto out;
|
||||
|
||||
/* When we created the file with exclusive semantics, make
|
||||
* 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");
|
||||
|
||||
if (!(sattr->ia_valid & ATTR_ATIME_SET))
|
||||
@ -330,14 +369,15 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||
/* Note: we could use a guarded setattr here, but I'm
|
||||
* not sure this buys us anything (and I'd have
|
||||
* to revamp the NFSv3 XDR code) */
|
||||
status = nfs3_proc_setattr(dentry, &fattr, sattr);
|
||||
nfs_post_op_update_inode(dentry->d_inode, &fattr);
|
||||
status = nfs3_proc_setattr(dentry, data->res.fattr, sattr);
|
||||
nfs_post_op_update_inode(dentry->d_inode, data->res.fattr);
|
||||
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);
|
||||
out:
|
||||
nfs3_free_createdata(data);
|
||||
dprintk("NFS reply create: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
@ -452,40 +492,28 @@ static int
|
||||
nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
|
||||
unsigned int len, struct iattr *sattr)
|
||||
{
|
||||
struct nfs_fh fhandle;
|
||||
struct nfs_fattr fattr, dir_attr;
|
||||
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;
|
||||
struct nfs3_createdata *data;
|
||||
int status = -ENOMEM;
|
||||
|
||||
if (len > NFS3_MAXPATHLEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
dprintk("NFS call symlink %s\n", dentry->d_name.name);
|
||||
|
||||
nfs_fattr_init(&dir_attr);
|
||||
nfs_fattr_init(&fattr);
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
nfs_post_op_update_inode(dir, &dir_attr);
|
||||
if (status != 0)
|
||||
data = nfs3_alloc_createdata();
|
||||
if (data == NULL)
|
||||
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:
|
||||
dprintk("NFS reply symlink: %d\n", status);
|
||||
return status;
|
||||
@ -494,42 +522,31 @@ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
|
||||
static int
|
||||
nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
|
||||
{
|
||||
struct nfs_fh fhandle;
|
||||
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,
|
||||
};
|
||||
struct nfs3_createdata *data;
|
||||
int mode = sattr->ia_mode;
|
||||
int status;
|
||||
int status = -ENOMEM;
|
||||
|
||||
dprintk("NFS call mkdir %s\n", dentry->d_name.name);
|
||||
|
||||
sattr->ia_mode &= ~current->fs->umask;
|
||||
|
||||
nfs_fattr_init(&dir_attr);
|
||||
nfs_fattr_init(&fattr);
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
nfs_post_op_update_inode(dir, &dir_attr);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
status = nfs_instantiate(dentry, &fhandle, &fattr);
|
||||
data = nfs3_alloc_createdata();
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
|
||||
data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR];
|
||||
data->arg.mkdir.fh = NFS_FH(dir);
|
||||
data->arg.mkdir.name = dentry->d_name.name;
|
||||
data->arg.mkdir.len = dentry->d_name.len;
|
||||
data->arg.mkdir.sattr = sattr;
|
||||
|
||||
status = nfs3_do_create(dir, dentry, data);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
|
||||
status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
|
||||
out:
|
||||
nfs3_free_createdata(data);
|
||||
dprintk("NFS reply mkdir: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
@ -615,52 +632,50 @@ static int
|
||||
nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||
dev_t rdev)
|
||||
{
|
||||
struct nfs_fh fh;
|
||||
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,
|
||||
};
|
||||
struct nfs3_createdata *data;
|
||||
mode_t mode = sattr->ia_mode;
|
||||
int status;
|
||||
|
||||
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;
|
||||
}
|
||||
int status = -ENOMEM;
|
||||
|
||||
dprintk("NFS call mknod %s %u:%u\n", dentry->d_name.name,
|
||||
MAJOR(rdev), MINOR(rdev));
|
||||
|
||||
sattr->ia_mode &= ~current->fs->umask;
|
||||
|
||||
nfs_fattr_init(&dir_attr);
|
||||
nfs_fattr_init(&fattr);
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
nfs_post_op_update_inode(dir, &dir_attr);
|
||||
if (status != 0)
|
||||
data = nfs3_alloc_createdata();
|
||||
if (data == NULL)
|
||||
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)
|
||||
goto out;
|
||||
status = nfs3_proc_set_default_acl(dir, dentry->d_inode, mode);
|
||||
out:
|
||||
nfs3_free_createdata(data);
|
||||
dprintk("NFS reply mknod: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
@ -801,8 +816,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
|
||||
.write_done = nfs3_write_done,
|
||||
.commit_setup = nfs3_proc_commit_setup,
|
||||
.commit_done = nfs3_commit_done,
|
||||
.file_open = nfs_open,
|
||||
.file_release = nfs_release,
|
||||
.lock = nfs3_proc_lock,
|
||||
.clear_acl_cache = nfs3_forget_cached_acls,
|
||||
};
|
||||
|
@ -451,9 +451,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
|
||||
/* Save the delegation */
|
||||
memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
|
||||
rcu_read_unlock();
|
||||
lock_kernel();
|
||||
ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
|
||||
unlock_kernel();
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
ret = -EAGAIN;
|
||||
@ -1139,8 +1137,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
|
||||
struct iattr *sattr, struct nfs4_state *state)
|
||||
static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
||||
struct nfs_fattr *fattr, struct iattr *sattr,
|
||||
struct nfs4_state *state)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
struct nfs_setattrargs arg = {
|
||||
@ -1154,9 +1153,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
|
||||
.server = server,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
|
||||
.rpc_argp = &arg,
|
||||
.rpc_resp = &res,
|
||||
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
|
||||
.rpc_argp = &arg,
|
||||
.rpc_resp = &res,
|
||||
.rpc_cred = cred,
|
||||
};
|
||||
unsigned long timestamp = jiffies;
|
||||
int status;
|
||||
@ -1166,7 +1166,6 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
|
||||
if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) {
|
||||
/* Use that stateid */
|
||||
} else if (state != NULL) {
|
||||
msg.rpc_cred = state->owner->so_cred;
|
||||
nfs4_copy_stateid(&arg.stateid, state, current->files);
|
||||
} else
|
||||
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
|
||||
@ -1177,15 +1176,16 @@ static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
|
||||
struct iattr *sattr, struct nfs4_state *state)
|
||||
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
||||
struct nfs_fattr *fattr, struct iattr *sattr,
|
||||
struct nfs4_state *state)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(inode);
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = nfs4_handle_exception(server,
|
||||
_nfs4_do_setattr(inode, fattr, sattr, state),
|
||||
_nfs4_do_setattr(inode, cred, fattr, sattr, state),
|
||||
&exception);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
@ -1647,29 +1647,25 @@ static int
|
||||
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||
struct iattr *sattr)
|
||||
{
|
||||
struct rpc_cred *cred;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct nfs_open_context *ctx;
|
||||
struct rpc_cred *cred = NULL;
|
||||
struct nfs4_state *state = NULL;
|
||||
int status;
|
||||
|
||||
nfs_fattr_init(fattr);
|
||||
|
||||
cred = rpc_lookup_cred();
|
||||
if (IS_ERR(cred))
|
||||
return PTR_ERR(cred);
|
||||
|
||||
/* Search for an existing open(O_WRITE) file */
|
||||
ctx = nfs_find_open_context(inode, cred, FMODE_WRITE);
|
||||
if (ctx != NULL)
|
||||
state = ctx->state;
|
||||
if (sattr->ia_valid & ATTR_FILE) {
|
||||
struct nfs_open_context *ctx;
|
||||
|
||||
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)
|
||||
nfs_setattr_update_inode(inode, sattr);
|
||||
if (ctx != NULL)
|
||||
put_nfs_open_context(ctx);
|
||||
put_rpccred(cred);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1897,17 +1893,16 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||
goto out;
|
||||
}
|
||||
state = nfs4_do_open(dir, &path, flags, sattr, cred);
|
||||
put_rpccred(cred);
|
||||
d_drop(dentry);
|
||||
if (IS_ERR(state)) {
|
||||
status = PTR_ERR(state);
|
||||
goto out;
|
||||
goto out_putcred;
|
||||
}
|
||||
d_add(dentry, igrab(state->inode));
|
||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||
if (flags & O_EXCL) {
|
||||
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)
|
||||
nfs_setattr_update_inode(state->inode, sattr);
|
||||
nfs_post_op_update_inode(state->inode, &fattr);
|
||||
@ -1916,6 +1911,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||
status = nfs4_intent_set_file(nd, &path, state);
|
||||
else
|
||||
nfs4_close_sync(&path, state, flags);
|
||||
out_putcred:
|
||||
put_rpccred(cred);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
@ -2079,47 +2076,81 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n
|
||||
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,
|
||||
struct page *page, unsigned int len, struct iattr *sattr)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
struct nfs_fh fhandle;
|
||||
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;
|
||||
struct nfs4_createdata *data;
|
||||
int status = -ENAMETOOLONG;
|
||||
|
||||
if (len > NFS4_MAXPATHLEN)
|
||||
return -ENAMETOOLONG;
|
||||
goto out;
|
||||
|
||||
arg.u.symlink.pages = &page;
|
||||
arg.u.symlink.len = len;
|
||||
nfs_fattr_init(&fattr);
|
||||
nfs_fattr_init(&dir_fattr);
|
||||
status = -ENOMEM;
|
||||
data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4LNK);
|
||||
if (data == NULL)
|
||||
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);
|
||||
if (!status) {
|
||||
update_changeattr(dir, &res.dir_cinfo);
|
||||
nfs_post_op_update_inode(dir, res.dir_fattr);
|
||||
status = nfs_instantiate(dentry, &fhandle, &fattr);
|
||||
}
|
||||
status = nfs4_do_create(dir, dentry, data);
|
||||
|
||||
nfs4_free_createdata(data);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -2140,39 +2171,17 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
|
||||
static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
|
||||
struct iattr *sattr)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
struct nfs_fh fhandle;
|
||||
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;
|
||||
struct nfs4_createdata *data;
|
||||
int status = -ENOMEM;
|
||||
|
||||
nfs_fattr_init(&fattr);
|
||||
nfs_fattr_init(&dir_fattr);
|
||||
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
if (!status) {
|
||||
update_changeattr(dir, &res.dir_cinfo);
|
||||
nfs_post_op_update_inode(dir, res.dir_fattr);
|
||||
status = nfs_instantiate(dentry, &fhandle, &fattr);
|
||||
}
|
||||
data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4DIR);
|
||||
if (data == NULL)
|
||||
goto out;
|
||||
|
||||
status = nfs4_do_create(dir, dentry, data);
|
||||
|
||||
nfs4_free_createdata(data);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -2242,56 +2251,34 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
||||
static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
|
||||
struct iattr *sattr, dev_t rdev)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
struct nfs_fh fh;
|
||||
struct nfs_fattr fattr, dir_fattr;
|
||||
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);
|
||||
struct nfs4_createdata *data;
|
||||
int mode = sattr->ia_mode;
|
||||
int status = -ENOMEM;
|
||||
|
||||
BUG_ON(!(sattr->ia_valid & ATTR_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))
|
||||
arg.ftype = NF4FIFO;
|
||||
data->arg.ftype = NF4FIFO;
|
||||
else if (S_ISBLK(mode)) {
|
||||
arg.ftype = NF4BLK;
|
||||
arg.u.device.specdata1 = MAJOR(rdev);
|
||||
arg.u.device.specdata2 = MINOR(rdev);
|
||||
data->arg.ftype = NF4BLK;
|
||||
data->arg.u.device.specdata1 = MAJOR(rdev);
|
||||
data->arg.u.device.specdata2 = MINOR(rdev);
|
||||
}
|
||||
else if (S_ISCHR(mode)) {
|
||||
arg.ftype = NF4CHR;
|
||||
arg.u.device.specdata1 = MAJOR(rdev);
|
||||
arg.u.device.specdata2 = MINOR(rdev);
|
||||
data->arg.ftype = NF4CHR;
|
||||
data->arg.u.device.specdata1 = MAJOR(rdev);
|
||||
data->arg.u.device.specdata2 = MINOR(rdev);
|
||||
}
|
||||
else
|
||||
arg.ftype = NF4SOCK;
|
||||
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
if (status == 0) {
|
||||
update_changeattr(dir, &res.dir_cinfo);
|
||||
nfs_post_op_update_inode(dir, res.dir_fattr);
|
||||
status = nfs_instantiate(dentry, &fh, &fattr);
|
||||
}
|
||||
status = nfs4_do_create(dir, dentry, data);
|
||||
|
||||
nfs4_free_createdata(data);
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -2706,6 +2693,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
|
||||
ret = nfs_revalidate_inode(server, inode);
|
||||
if (ret < 0)
|
||||
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);
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
@ -2733,7 +2722,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
|
||||
nfs_inode_return_delegation(inode);
|
||||
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2767,8 +2757,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
|
||||
task->tk_status = 0;
|
||||
return -EAGAIN;
|
||||
case -NFS4ERR_DELAY:
|
||||
nfs_inc_server_stats((struct nfs_server *) server,
|
||||
NFSIOS_DELAY);
|
||||
nfs_inc_server_stats(server, NFSIOS_DELAY);
|
||||
case -NFS4ERR_GRACE:
|
||||
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
||||
task->tk_status = 0;
|
||||
@ -2933,7 +2922,7 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre
|
||||
|
||||
int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
|
||||
{
|
||||
long timeout;
|
||||
long timeout = 0;
|
||||
int err;
|
||||
do {
|
||||
err = _nfs4_proc_setclientid_confirm(clp, cred);
|
||||
@ -3725,8 +3714,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
|
||||
.write_done = nfs4_write_done,
|
||||
.commit_setup = nfs4_proc_commit_setup,
|
||||
.commit_done = nfs4_commit_done,
|
||||
.file_open = nfs_open,
|
||||
.file_release = nfs_release,
|
||||
.lock = nfs4_proc_lock,
|
||||
.clear_acl_cache = nfs4_zap_acl_attr,
|
||||
};
|
||||
|
@ -940,7 +940,6 @@ static int reclaimer(void *ptr)
|
||||
allow_signal(SIGKILL);
|
||||
|
||||
/* Ensure exclusive access to NFSv4 state */
|
||||
lock_kernel();
|
||||
down_write(&clp->cl_sem);
|
||||
/* Are there any NFS mounts out there? */
|
||||
if (list_empty(&clp->cl_superblocks))
|
||||
@ -1000,7 +999,6 @@ static int reclaimer(void *ptr)
|
||||
nfs_delegation_reap_unclaimed(clp);
|
||||
out:
|
||||
up_write(&clp->cl_sem);
|
||||
unlock_kernel();
|
||||
if (status == -NFS4ERR_CB_PATH_DOWN)
|
||||
nfs_handle_cb_pathdown(clp);
|
||||
nfs4_clear_recover_bit(clp);
|
||||
|
@ -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>
|
||||
*
|
||||
* 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.rsize = NFS_DEF_FILE_IO_SIZE;
|
||||
nfs_data.wsize = NFS_DEF_FILE_IO_SIZE;
|
||||
nfs_data.acregmin = 3;
|
||||
nfs_data.acregmax = 60;
|
||||
nfs_data.acdirmin = 30;
|
||||
nfs_data.acdirmax = 60;
|
||||
nfs_data.acregmin = NFS_DEF_ACREGMIN;
|
||||
nfs_data.acregmax = NFS_DEF_ACREGMAX;
|
||||
nfs_data.acdirmin = NFS_DEF_ACDIRMIN;
|
||||
nfs_data.acdirmax = NFS_DEF_ACDIRMAX;
|
||||
strcpy(buf, NFS_ROOT);
|
||||
|
||||
/* Process options received from the remote server */
|
||||
|
@ -129,6 +129,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||
sattr->ia_mode &= S_IALLUGO;
|
||||
|
||||
dprintk("NFS call setattr\n");
|
||||
if (sattr->ia_valid & ATTR_FILE)
|
||||
msg.rpc_cred = nfs_file_cred(sattr->ia_file);
|
||||
nfs_fattr_init(fattr);
|
||||
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 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);
|
||||
}
|
||||
|
||||
/* 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 = {
|
||||
.version = 2, /* protocol version */
|
||||
@ -630,7 +655,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
|
||||
.write_setup = nfs_proc_write_setup,
|
||||
.write_done = nfs_write_done,
|
||||
.commit_setup = nfs_proc_commit_setup,
|
||||
.file_open = nfs_open,
|
||||
.file_release = nfs_release,
|
||||
.lock = nfs_proc_lock,
|
||||
.lock_check_bounds = nfs_lock_check_bounds,
|
||||
};
|
||||
|
888
fs/nfs/super.c
888
fs/nfs/super.c
File diff suppressed because it is too large
Load Diff
322
fs/nfs/write.c
322
fs/nfs/write.c
@ -34,9 +34,6 @@
|
||||
/*
|
||||
* 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,
|
||||
struct inode *inode, int ioflags);
|
||||
static void nfs_redirty_request(struct nfs_page *req);
|
||||
@ -136,16 +133,21 @@ static struct nfs_page *nfs_page_find_request(struct page *page)
|
||||
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
loff_t end, i_size = i_size_read(inode);
|
||||
pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
|
||||
loff_t end, i_size;
|
||||
pgoff_t end_index;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
i_size = i_size_read(inode);
|
||||
end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
|
||||
if (i_size > 0 && page->index < end_index)
|
||||
return;
|
||||
goto out;
|
||||
end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
|
||||
if (i_size >= end)
|
||||
return;
|
||||
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
|
||||
goto out;
|
||||
i_size_write(inode, end);
|
||||
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
|
||||
out:
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
/* A writeback failed: mark the page as bad, and invalidate the page cache */
|
||||
@ -169,29 +171,6 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int
|
||||
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)
|
||||
{
|
||||
if (wbc->for_reclaim)
|
||||
@ -268,12 +247,9 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
|
||||
return ret;
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
|
||||
/* This request is marked for commit */
|
||||
if (test_bit(PG_CLEAN, &req->wb_flags)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
nfs_clear_page_tag_locked(req);
|
||||
nfs_pageio_complete(pgio);
|
||||
return 0;
|
||||
BUG();
|
||||
}
|
||||
if (nfs_set_page_writeback(page) != 0) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
@ -355,11 +331,19 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
|
||||
/*
|
||||
* 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);
|
||||
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);
|
||||
BUG_ON(error);
|
||||
if (!nfsi->npages) {
|
||||
@ -373,6 +357,10 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
|
||||
kref_get(&req->wb_kref);
|
||||
radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
|
||||
NFS_PAGE_TAG_LOCKED);
|
||||
spin_unlock(&inode->i_lock);
|
||||
radix_tree_preload_end();
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -405,19 +393,6 @@ nfs_mark_request_dirty(struct nfs_page *req)
|
||||
__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)
|
||||
/*
|
||||
* Add a request to the inode's commit list.
|
||||
@ -430,7 +405,7 @@ nfs_mark_request_commit(struct nfs_page *req)
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
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,
|
||||
req->wb_index,
|
||||
NFS_PAGE_TAG_COMMIT);
|
||||
@ -440,6 +415,19 @@ nfs_mark_request_commit(struct nfs_page *req)
|
||||
__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
|
||||
int nfs_write_need_commit(struct nfs_write_data *data)
|
||||
{
|
||||
@ -449,7 +437,7 @@ int nfs_write_need_commit(struct nfs_write_data *data)
|
||||
static inline
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
@ -465,6 +453,12 @@ nfs_mark_request_commit(struct nfs_page *req)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int
|
||||
nfs_clear_request_commit(struct nfs_page *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
int nfs_write_need_commit(struct nfs_write_data *data)
|
||||
{
|
||||
@ -522,11 +516,8 @@ static void nfs_cancel_commit_list(struct list_head *head)
|
||||
|
||||
while(!list_empty(head)) {
|
||||
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);
|
||||
clear_bit(PG_NEED_COMMIT, &(req)->wb_flags);
|
||||
nfs_clear_request_commit(req);
|
||||
nfs_inode_remove_request(req);
|
||||
nfs_unlock_request(req);
|
||||
}
|
||||
@ -564,108 +555,122 @@ static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pg
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Try to update any existing write request, or create one if there is none.
|
||||
* In order to match, the request's credentials must match those of
|
||||
* the calling process.
|
||||
* Search for an existing write request, and attempt to update
|
||||
* it to reflect a new dirty region on a given page.
|
||||
*
|
||||
* 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,
|
||||
struct page *page, unsigned int offset, unsigned int bytes)
|
||||
static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
|
||||
struct page *page,
|
||||
unsigned int offset,
|
||||
unsigned int bytes)
|
||||
{
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct nfs_page *req, *new = NULL;
|
||||
pgoff_t rqend, end;
|
||||
struct nfs_page *req;
|
||||
unsigned int rqend;
|
||||
unsigned int end;
|
||||
int error;
|
||||
|
||||
if (!PagePrivate(page))
|
||||
return NULL;
|
||||
|
||||
end = offset + bytes;
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
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);
|
||||
if (req) {
|
||||
if (!nfs_set_page_tag_locked(req)) {
|
||||
int error;
|
||||
if (req == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
spin_unlock(&inode->i_lock);
|
||||
error = nfs_wait_on_request(req);
|
||||
nfs_release_request(req);
|
||||
if (error < 0) {
|
||||
if (new) {
|
||||
radix_tree_preload_end();
|
||||
nfs_release_request(new);
|
||||
}
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (new) {
|
||||
radix_tree_preload_end();
|
||||
nfs_release_request(new);
|
||||
}
|
||||
rqend = req->wb_offset + req->wb_bytes;
|
||||
/*
|
||||
* Tell the caller to flush out the request if
|
||||
* the offsets are non-contiguous.
|
||||
* Note: nfs_flush_incompatible() will already
|
||||
* have flushed out requests having wrong owners.
|
||||
*/
|
||||
if (offset > rqend
|
||||
|| end < req->wb_offset)
|
||||
goto out_flushme;
|
||||
|
||||
if (nfs_set_page_tag_locked(req))
|
||||
break;
|
||||
}
|
||||
|
||||
if (new) {
|
||||
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;
|
||||
}
|
||||
/* The request is locked, so wait and then retry */
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
new = nfs_create_request(ctx, inode, page, offset, bytes);
|
||||
if (IS_ERR(new))
|
||||
return new;
|
||||
error = nfs_wait_on_request(req);
|
||||
nfs_release_request(req);
|
||||
if (error != 0)
|
||||
goto out_err;
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
|
||||
/* We have a request for our page.
|
||||
* If the creds don't match, or the
|
||||
* page addresses don't match,
|
||||
* 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);
|
||||
}
|
||||
if (nfs_clear_request_commit(req))
|
||||
radix_tree_tag_clear(&NFS_I(inode)->nfs_page_tree,
|
||||
req->wb_index, NFS_PAGE_TAG_COMMIT);
|
||||
|
||||
/* Okay, the request matches. Update the region */
|
||||
if (offset < req->wb_offset) {
|
||||
req->wb_offset = offset;
|
||||
req->wb_pgbase = offset;
|
||||
req->wb_bytes = max(end, rqend) - req->wb_offset;
|
||||
goto zero_page;
|
||||
}
|
||||
|
||||
if (end > rqend)
|
||||
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;
|
||||
zero_page:
|
||||
/* If this page might potentially be marked as up to date,
|
||||
* then we need to zero any uninitalised data. */
|
||||
if (req->wb_pgbase == 0 && req->wb_bytes != PAGE_CACHE_SIZE
|
||||
&& !PageUptodate(req->wb_page))
|
||||
zero_user_segment(req->wb_page, req->wb_bytes, PAGE_CACHE_SIZE);
|
||||
return req;
|
||||
}
|
||||
|
||||
static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
|
||||
unsigned int offset, unsigned int count)
|
||||
{
|
||||
struct nfs_page *req;
|
||||
|
||||
req = nfs_setup_write_request(ctx, page, offset, count);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
/* Update file length */
|
||||
nfs_grow_file(page, offset, count);
|
||||
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
|
||||
nfs_clear_page_tag_locked(req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfs_flush_incompatible(struct file *file, struct page *page)
|
||||
@ -685,8 +690,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
|
||||
req = nfs_page_find_request(page);
|
||||
if (req == NULL)
|
||||
return 0;
|
||||
do_flush = req->wb_page != page || req->wb_context != ctx
|
||||
|| !nfs_dirty_request(req);
|
||||
do_flush = req->wb_page != page || req->wb_context != ctx;
|
||||
nfs_release_request(req);
|
||||
if (!do_flush)
|
||||
return 0;
|
||||
@ -721,10 +725,10 @@ int nfs_updatepage(struct file *file, struct page *page,
|
||||
|
||||
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_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
|
||||
* is up to date, it may be more efficient to extend the write
|
||||
@ -744,7 +748,7 @@ int nfs_updatepage(struct file *file, struct page *page,
|
||||
else
|
||||
__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));
|
||||
return status;
|
||||
}
|
||||
@ -752,12 +756,7 @@ int nfs_updatepage(struct file *file, struct page *page,
|
||||
static void nfs_writepage_release(struct nfs_page *req)
|
||||
{
|
||||
|
||||
if (PageError(req->wb_page)) {
|
||||
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);
|
||||
if (PageError(req->wb_page) || !nfs_reschedule_unstable_write(req)) {
|
||||
nfs_end_page_writeback(req->wb_page);
|
||||
nfs_inode_remove_request(req);
|
||||
} else
|
||||
@ -834,7 +833,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req,
|
||||
NFS_PROTO(inode)->write_setup(data, &msg);
|
||||
|
||||
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,
|
||||
inode->i_sb->s_id,
|
||||
(long long)NFS_FILEID(inode),
|
||||
@ -978,13 +977,13 @@ static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
|
||||
static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
struct nfs_write_data *data = calldata;
|
||||
struct nfs_page *req = data->req;
|
||||
|
||||
dprintk("NFS: write (%s/%Ld %d@%Ld)",
|
||||
req->wb_context->path.dentry->d_inode->i_sb->s_id,
|
||||
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
|
||||
req->wb_bytes,
|
||||
(long long)req_offset(req));
|
||||
dprintk("NFS: %5u write(%s/%lld %d@%lld)",
|
||||
task->tk_pid,
|
||||
data->req->wb_context->path.dentry->d_inode->i_sb->s_id,
|
||||
(long long)
|
||||
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);
|
||||
}
|
||||
@ -1058,7 +1057,8 @@ static void nfs_writeback_release_full(void *calldata)
|
||||
|
||||
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,
|
||||
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
|
||||
req->wb_bytes,
|
||||
@ -1078,8 +1078,6 @@ static void nfs_writeback_release_full(void *calldata)
|
||||
dprintk(" marked for commit\n");
|
||||
goto next;
|
||||
}
|
||||
/* Set the PG_uptodate flag? */
|
||||
nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
|
||||
dprintk(" OK\n");
|
||||
remove_request:
|
||||
nfs_end_page_writeback(page);
|
||||
@ -1133,7 +1131,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
|
||||
static unsigned long complain;
|
||||
|
||||
if (time_before(complain, jiffies)) {
|
||||
dprintk("NFS: faulty NFS server %s:"
|
||||
dprintk("NFS: faulty NFS server %s:"
|
||||
" (committed = %d) != (stable = %d)\n",
|
||||
NFS_SERVER(data->inode)->nfs_client->cl_hostname,
|
||||
resp->verf->committed, argp->stable);
|
||||
@ -1297,12 +1295,9 @@ static void nfs_commit_release(void *calldata)
|
||||
while (!list_empty(&data->pages)) {
|
||||
req = nfs_list_entry(data->pages.next);
|
||||
nfs_list_remove_request(req);
|
||||
clear_bit(PG_NEED_COMMIT, &(req)->wb_flags);
|
||||
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
|
||||
dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
|
||||
BDI_RECLAIMABLE);
|
||||
nfs_clear_request_commit(req);
|
||||
|
||||
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,
|
||||
(long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
|
||||
req->wb_bytes,
|
||||
@ -1318,9 +1313,6 @@ static void nfs_commit_release(void *calldata)
|
||||
* returned by the server against all stored verfs. */
|
||||
if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
|
||||
/* 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);
|
||||
dprintk(" OK\n");
|
||||
goto next;
|
||||
@ -1479,7 +1471,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
|
||||
req = nfs_page_find_request(page);
|
||||
if (req == NULL)
|
||||
goto out;
|
||||
if (test_bit(PG_NEED_COMMIT, &req->wb_flags)) {
|
||||
if (test_bit(PG_CLEAN, &req->wb_flags)) {
|
||||
nfs_release_request(req);
|
||||
break;
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ static int do_probe_callback(void *data)
|
||||
.program = &cb_program,
|
||||
.version = nfs_cb_version[1]->number,
|
||||
.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 = {
|
||||
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
|
||||
|
@ -44,6 +44,13 @@
|
||||
|
||||
#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 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);
|
||||
|
@ -12,9 +12,19 @@
|
||||
#include <linux/magic.h>
|
||||
|
||||
/* 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_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
|
||||
* strategies:
|
||||
|
119
include/linux/nfs_iostat.h
Normal file
119
include/linux/nfs_iostat.h
Normal 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 */
|
@ -27,9 +27,12 @@
|
||||
/*
|
||||
* Valid flags for a dirty buffer
|
||||
*/
|
||||
#define PG_BUSY 0
|
||||
#define PG_NEED_COMMIT 1
|
||||
#define PG_NEED_RESCHED 2
|
||||
enum {
|
||||
PG_BUSY = 0,
|
||||
PG_CLEAN,
|
||||
PG_NEED_COMMIT,
|
||||
PG_NEED_RESCHED,
|
||||
};
|
||||
|
||||
struct nfs_inode;
|
||||
struct nfs_page {
|
||||
|
@ -829,9 +829,8 @@ struct nfs_rpc_ops {
|
||||
int (*write_done) (struct rpc_task *, struct nfs_write_data *);
|
||||
void (*commit_setup) (struct nfs_write_data *, struct rpc_message *);
|
||||
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_check_bounds)(const struct file_lock *);
|
||||
void (*clear_acl_cache)(struct inode *);
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,8 @@ struct rpc_clnt {
|
||||
|
||||
unsigned int cl_softrtry : 1,/* soft timeouts */
|
||||
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 */
|
||||
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_NOPING (1UL << 4)
|
||||
#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_bind_new_program(struct rpc_clnt *,
|
||||
@ -123,6 +125,9 @@ void rpc_shutdown_client(struct rpc_clnt *);
|
||||
void rpc_release_client(struct rpc_clnt *);
|
||||
|
||||
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);
|
||||
void rpcb_getport_async(struct rpc_task *);
|
||||
|
||||
|
@ -135,7 +135,6 @@ struct rpc_task_setup {
|
||||
#define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER)
|
||||
#define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS)
|
||||
#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_TASK_RUNNING 0
|
||||
|
@ -63,22 +63,11 @@ static const struct rpc_credops gss_nullops;
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
#define NFS_NGROUPS 16
|
||||
|
||||
#define GSS_CRED_SLACK 1024 /* XXX: unused */
|
||||
#define GSS_CRED_SLACK 1024
|
||||
/* length of a krb5 verifier (48), plus data added before arguments when
|
||||
* using integrity (two 4-byte integers): */
|
||||
#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 kref kref;
|
||||
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);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
dest->data = kmemdup(p, len, GFP_KERNEL);
|
||||
dest->data = kmemdup(p, len, GFP_NOFS);
|
||||
if (unlikely(dest->data == NULL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
dest->len = len;
|
||||
@ -171,7 +160,7 @@ gss_alloc_context(void)
|
||||
{
|
||||
struct gss_cl_ctx *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_NOFS);
|
||||
if (ctx != NULL) {
|
||||
ctx->gc_proc = RPC_GSS_PROC_DATA;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* 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;
|
||||
|
||||
gss_msg = kzalloc(sizeof(*gss_msg), GFP_KERNEL);
|
||||
gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
|
||||
if (gss_msg != NULL) {
|
||||
INIT_LIST_HEAD(&gss_msg->list);
|
||||
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;
|
||||
void *buf;
|
||||
struct rpc_clnt *clnt;
|
||||
struct gss_upcall_msg *gss_msg;
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
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)
|
||||
goto out;
|
||||
err = -ENOMEM;
|
||||
buf = kmalloc(mlen, GFP_KERNEL);
|
||||
buf = kmalloc(mlen, GFP_NOFS);
|
||||
if (!buf)
|
||||
goto out;
|
||||
|
||||
clnt = RPC_I(inode)->private;
|
||||
err = -EFAULT;
|
||||
if (copy_from_user(buf, src, mlen))
|
||||
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",
|
||||
acred->uid, auth->au_flavor);
|
||||
|
||||
if (!(cred = kzalloc(sizeof(*cred), GFP_KERNEL)))
|
||||
if (!(cred = kzalloc(sizeof(*cred), GFP_NOFS)))
|
||||
goto out_err;
|
||||
|
||||
rpcauth_init_cred(&cred->gc_base, acred, auth, &gss_credops);
|
||||
|
@ -70,7 +70,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
|
||||
q = (const void *)((const char *)p + len);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
res->data = kmemdup(p, len, GFP_KERNEL);
|
||||
res->data = kmemdup(p, len, GFP_NOFS);
|
||||
if (unlikely(res->data == NULL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
res->len = len;
|
||||
@ -131,7 +131,7 @@ gss_import_sec_context_kerberos(const void *p,
|
||||
struct krb5_ctx *ctx;
|
||||
int tmp;
|
||||
|
||||
if (!(ctx = kzalloc(sizeof(*ctx), GFP_KERNEL)))
|
||||
if (!(ctx = kzalloc(sizeof(*ctx), GFP_NOFS)))
|
||||
goto out_err;
|
||||
|
||||
p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate));
|
||||
|
@ -76,7 +76,7 @@ simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
|
||||
q = (const void *)((const char *)p + len);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
res->data = kmemdup(p, len, GFP_KERNEL);
|
||||
res->data = kmemdup(p, len, GFP_NOFS);
|
||||
if (unlikely(res->data == NULL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return q;
|
||||
@ -90,7 +90,7 @@ gss_import_sec_context_spkm3(const void *p, size_t len,
|
||||
struct spkm3_ctx *ctx;
|
||||
int version;
|
||||
|
||||
if (!(ctx = kzalloc(sizeof(*ctx), GFP_KERNEL)))
|
||||
if (!(ctx = kzalloc(sizeof(*ctx), GFP_NOFS)))
|
||||
goto out_err;
|
||||
|
||||
p = simple_get_bytes(p, end, &version, sizeof(version));
|
||||
|
@ -90,7 +90,7 @@ asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
|
||||
int
|
||||
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;
|
||||
out->len = explen;
|
||||
memcpy(out->data, in, enclen);
|
||||
|
@ -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",
|
||||
acred->uid, acred->gid);
|
||||
|
||||
if (!(cred = kmalloc(sizeof(*cred), GFP_KERNEL)))
|
||||
if (!(cred = kmalloc(sizeof(*cred), GFP_NOFS)))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops);
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.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_reserveresult(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_bind(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_connect(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 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;
|
||||
if (args->flags & RPC_CLNT_CREATE_DISCRTRY)
|
||||
clnt->cl_discrtry = 1;
|
||||
if (!(args->flags & RPC_CLNT_CREATE_QUIET))
|
||||
clnt->cl_chatty = 1;
|
||||
|
||||
return clnt;
|
||||
}
|
||||
@ -690,6 +692,21 @@ rpc_restart_call(struct rpc_task *task)
|
||||
}
|
||||
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
|
||||
*
|
||||
@ -701,9 +718,9 @@ call_start(struct rpc_task *task)
|
||||
{
|
||||
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,
|
||||
task->tk_msg.rpc_proc->p_proc,
|
||||
rpc_proc_name(task),
|
||||
(RPC_IS_ASYNC(task) ? "async" : "sync"));
|
||||
|
||||
/* 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
|
||||
*/
|
||||
static void
|
||||
call_encode(struct rpc_task *task)
|
||||
rpc_xdr_encode(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_rqst *req = task->tk_rqstp;
|
||||
kxdrproc_t encode;
|
||||
@ -876,23 +893,19 @@ call_encode(struct rpc_task *task)
|
||||
(char *)req->rq_buffer + req->rq_callsize,
|
||||
req->rq_rcvsize);
|
||||
|
||||
/* Encode header and provided arguments */
|
||||
encode = task->tk_msg.rpc_proc->p_encode;
|
||||
if (!(p = call_header(task))) {
|
||||
printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
|
||||
p = rpc_encode_header(task);
|
||||
if (p == NULL) {
|
||||
printk(KERN_INFO "RPC: couldn't encode RPC header, exit EIO\n");
|
||||
rpc_exit(task, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
encode = task->tk_msg.rpc_proc->p_encode;
|
||||
if (encode == NULL)
|
||||
return;
|
||||
|
||||
task->tk_status = rpcauth_wrap_req(task, encode, req, p,
|
||||
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) {
|
||||
case -EAGAIN:
|
||||
dprintk("RPC: %5u rpcbind waiting for another request "
|
||||
"to finish\n", task->tk_pid);
|
||||
/* avoid busy-waiting here -- could be a network outage. */
|
||||
rpc_delay(task, 5*HZ);
|
||||
case -ENOMEM:
|
||||
dprintk("RPC: %5u rpcbind out of memory\n", task->tk_pid);
|
||||
rpc_delay(task, HZ >> 2);
|
||||
goto retry_timeout;
|
||||
case -EACCES:
|
||||
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. */
|
||||
if (rpc_task_need_encode(task)) {
|
||||
BUG_ON(task->tk_rqstp->rq_bytes_sent != 0);
|
||||
call_encode(task);
|
||||
rpc_xdr_encode(task);
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
xprt_transmit(task);
|
||||
if (task->tk_status < 0)
|
||||
@ -1132,7 +1149,8 @@ call_status(struct rpc_task *task)
|
||||
rpc_exit(task, status);
|
||||
break;
|
||||
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);
|
||||
rpc_exit(task, status);
|
||||
}
|
||||
@ -1157,7 +1175,8 @@ call_timeout(struct rpc_task *task)
|
||||
task->tk_timeouts++;
|
||||
|
||||
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);
|
||||
rpc_exit(task, -EIO);
|
||||
return;
|
||||
@ -1165,7 +1184,8 @@ call_timeout(struct rpc_task *task)
|
||||
|
||||
if (!(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);
|
||||
}
|
||||
rpc_force_rebind(clnt);
|
||||
@ -1196,8 +1216,9 @@ call_decode(struct rpc_task *task)
|
||||
task->tk_pid, task->tk_status);
|
||||
|
||||
if (task->tk_flags & RPC_CALL_MAJORSEEN) {
|
||||
printk(KERN_NOTICE "%s: server %s OK\n",
|
||||
clnt->cl_protname, clnt->cl_server);
|
||||
if (clnt->cl_chatty)
|
||||
printk(KERN_NOTICE "%s: server %s OK\n",
|
||||
clnt->cl_protname, clnt->cl_server);
|
||||
task->tk_flags &= ~RPC_CALL_MAJORSEEN;
|
||||
}
|
||||
|
||||
@ -1224,8 +1245,7 @@ call_decode(struct rpc_task *task)
|
||||
goto out_retry;
|
||||
}
|
||||
|
||||
/* Verify the RPC header */
|
||||
p = call_verify(task);
|
||||
p = rpc_verify_header(task);
|
||||
if (IS_ERR(p)) {
|
||||
if (p == ERR_PTR(-EAGAIN))
|
||||
goto out_retry;
|
||||
@ -1243,7 +1263,7 @@ call_decode(struct rpc_task *task)
|
||||
return;
|
||||
out_retry:
|
||||
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) {
|
||||
req->rq_received = req->rq_rcv_buf.len = 0;
|
||||
if (task->tk_client->cl_discrtry)
|
||||
@ -1290,11 +1310,8 @@ call_refreshresult(struct rpc_task *task)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call header serialization
|
||||
*/
|
||||
static __be32 *
|
||||
call_header(struct rpc_task *task)
|
||||
rpc_encode_header(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_clnt *clnt = task->tk_client;
|
||||
struct rpc_rqst *req = task->tk_rqstp;
|
||||
@ -1314,11 +1331,8 @@ call_header(struct rpc_task *task)
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reply header verification
|
||||
*/
|
||||
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];
|
||||
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;
|
||||
goto out_retry;
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
@ -1431,10 +1445,10 @@ call_verify(struct rpc_task *task)
|
||||
error = -EPROTONOSUPPORT;
|
||||
goto out_err;
|
||||
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",
|
||||
task->tk_pid, __func__,
|
||||
task->tk_msg.rpc_proc,
|
||||
rpc_proc_name(task),
|
||||
task->tk_client->cl_prog,
|
||||
task->tk_client->cl_vers,
|
||||
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);
|
||||
|
||||
#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)
|
||||
{
|
||||
struct rpc_clnt *clnt;
|
||||
struct rpc_task *t;
|
||||
struct rpc_task *task;
|
||||
int header = 0;
|
||||
|
||||
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) {
|
||||
if (list_empty(&clnt->cl_tasks))
|
||||
continue;
|
||||
spin_lock(&clnt->cl_lock);
|
||||
list_for_each_entry(t, &clnt->cl_tasks, tk_task) {
|
||||
const char *rpc_waitq = "none";
|
||||
int proc;
|
||||
|
||||
if (t->tk_msg.rpc_proc)
|
||||
proc = t->tk_msg.rpc_proc->p_proc;
|
||||
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);
|
||||
list_for_each_entry(task, &clnt->cl_tasks, tk_task) {
|
||||
if (!header) {
|
||||
rpc_show_header();
|
||||
header++;
|
||||
}
|
||||
rpc_show_task(clnt, task);
|
||||
}
|
||||
spin_unlock(&clnt->cl_lock);
|
||||
}
|
||||
out:
|
||||
spin_unlock(&rpc_client_lock);
|
||||
}
|
||||
#endif
|
||||
|
@ -32,6 +32,10 @@
|
||||
#define RPCBIND_PROGRAM (100000u)
|
||||
#define RPCBIND_PORT (111u)
|
||||
|
||||
#define RPCBVERS_2 (2u)
|
||||
#define RPCBVERS_3 (3u)
|
||||
#define RPCBVERS_4 (4u)
|
||||
|
||||
enum {
|
||||
RPCBPROC_NULL,
|
||||
RPCBPROC_SET,
|
||||
@ -64,6 +68,7 @@ enum {
|
||||
#define RPCB_MAXOWNERLEN sizeof(RPCB_OWNER_STRING)
|
||||
|
||||
static void rpcb_getport_done(struct rpc_task *, void *);
|
||||
static void rpcb_map_release(void *data);
|
||||
static struct rpc_program rpcb_program;
|
||||
|
||||
struct rpcbind_args {
|
||||
@ -76,27 +81,22 @@ struct rpcbind_args {
|
||||
const char * r_netid;
|
||||
const char * r_addr;
|
||||
const char * r_owner;
|
||||
|
||||
int r_status;
|
||||
};
|
||||
|
||||
static struct rpc_procinfo rpcb_procedures2[];
|
||||
static struct rpc_procinfo rpcb_procedures3[];
|
||||
static struct rpc_procinfo rpcb_procedures4[];
|
||||
|
||||
struct rpcb_info {
|
||||
int rpc_vers;
|
||||
u32 rpc_vers;
|
||||
struct rpc_procinfo * rpc_proc;
|
||||
};
|
||||
|
||||
static struct rpcb_info rpcb_next_version[];
|
||||
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 = {
|
||||
.rpc_call_done = rpcb_getport_done,
|
||||
.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);
|
||||
}
|
||||
|
||||
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,
|
||||
size_t salen, int proto, u32 version,
|
||||
int privileged)
|
||||
size_t salen, int proto, u32 version)
|
||||
{
|
||||
struct rpc_create_args args = {
|
||||
.protocol = proto,
|
||||
@ -120,7 +157,8 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
|
||||
.program = &rpcb_program,
|
||||
.version = version,
|
||||
.authflavor = RPC_AUTH_UNIX,
|
||||
.flags = RPC_CLNT_CREATE_NOPING,
|
||||
.flags = (RPC_CLNT_CREATE_NOPING |
|
||||
RPC_CLNT_CREATE_NONPRIVPORT),
|
||||
};
|
||||
|
||||
switch (srvaddr->sa_family) {
|
||||
@ -134,29 +172,72 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!privileged)
|
||||
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
|
||||
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
|
||||
* @prog: RPC program 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
|
||||
* @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)
|
||||
{
|
||||
struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
|
||||
};
|
||||
struct rpcbind_args map = {
|
||||
.r_prog = prog,
|
||||
.r_vers = vers,
|
||||
@ -164,32 +245,159 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
|
||||
.r_port = port,
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &rpcb_procedures2[port ?
|
||||
RPCBPROC_SET : RPCBPROC_UNSET],
|
||||
.rpc_argp = &map,
|
||||
.rpc_resp = okay,
|
||||
};
|
||||
struct rpc_clnt *rpcb_clnt;
|
||||
int error = 0;
|
||||
|
||||
dprintk("RPC: %sregistering (%u, %u, %d, %u) with local "
|
||||
"rpcbind\n", (port ? "" : "un"),
|
||||
prog, vers, prot, port);
|
||||
|
||||
rpcb_clnt = rpcb_create("localhost", (struct sockaddr *) &sin,
|
||||
sizeof(sin), XPRT_TRANSPORT_UDP, 2, 1);
|
||||
if (IS_ERR(rpcb_clnt))
|
||||
return PTR_ERR(rpcb_clnt);
|
||||
msg.rpc_proc = &rpcb_procedures2[RPCBPROC_UNSET];
|
||||
if (port)
|
||||
msg.rpc_proc = &rpcb_procedures2[RPCBPROC_SET];
|
||||
|
||||
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)
|
||||
printk(KERN_WARNING "RPC: failed to contact local rpcbind "
|
||||
"server (errno %d).\n", -error);
|
||||
dprintk("RPC: registration status %d/%d\n", error, *okay);
|
||||
/*
|
||||
* Fill in AF_INET family-specific arguments to register
|
||||
*/
|
||||
static int rpcb_register_netid4(struct sockaddr_in *address_to_register,
|
||||
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);
|
||||
|
||||
rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin,
|
||||
sizeof(*sin), prot, 2, 0);
|
||||
sizeof(*sin), prot, RPCBVERS_2);
|
||||
if (IS_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 */
|
||||
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)) {
|
||||
status = -EAGAIN; /* tell caller to check again */
|
||||
dprintk("RPC: %5u %s: waiting for another binder\n",
|
||||
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 */
|
||||
if (xprt_bound(xprt)) {
|
||||
status = 0;
|
||||
@ -338,7 +545,7 @@ void rpcb_getport_async(struct rpc_task *task)
|
||||
task->tk_pid, __func__, bind_version);
|
||||
|
||||
rpcb_clnt = rpcb_create(clnt->cl_server, sap, salen, xprt->prot,
|
||||
bind_version, 0);
|
||||
bind_version);
|
||||
if (IS_ERR(rpcb_clnt)) {
|
||||
status = PTR_ERR(rpcb_clnt);
|
||||
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_addr = rpc_peeraddr2str(rpcb_clnt, RPC_DISPLAY_UNIVERSAL_ADDR);
|
||||
map->r_owner = RPCB_OWNER_STRING; /* ignored for GETADDR */
|
||||
map->r_status = -EIO;
|
||||
|
||||
child = rpcb_call_async(rpcb_clnt, map, proc);
|
||||
rpc_release_client(rpcb_clnt);
|
||||
if (IS_ERR(child)) {
|
||||
status = -EIO;
|
||||
/* rpcb_map_release() has freed the arguments */
|
||||
dprintk("RPC: %5u %s: rpc_run_task failed\n",
|
||||
task->tk_pid, __func__);
|
||||
goto bailout_nofree;
|
||||
return;
|
||||
}
|
||||
rpc_put_task(child);
|
||||
|
||||
@ -378,7 +585,6 @@ void rpcb_getport_async(struct rpc_task *task)
|
||||
|
||||
bailout_nofree:
|
||||
rpcb_wake_rpcbind_waiters(xprt, status);
|
||||
bailout_nowake:
|
||||
task->tk_status = status;
|
||||
}
|
||||
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",
|
||||
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,
|
||||
struct rpcbind_args *rpcb)
|
||||
{
|
||||
@ -438,7 +648,7 @@ static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p,
|
||||
unsigned short *portp)
|
||||
{
|
||||
*portp = (unsigned short) ntohl(*p++);
|
||||
dprintk("RPC: rpcb_decode_getport result %u\n",
|
||||
dprintk("RPC: rpcb_decode_getport result %u\n",
|
||||
*portp);
|
||||
return 0;
|
||||
}
|
||||
@ -447,8 +657,8 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p,
|
||||
unsigned int *boolp)
|
||||
{
|
||||
*boolp = (unsigned int) ntohl(*p++);
|
||||
dprintk("RPC: rpcb_decode_set result %u\n",
|
||||
*boolp);
|
||||
dprintk("RPC: rpcb_decode_set: call %s\n",
|
||||
(*boolp ? "succeeded" : "failed"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -571,52 +781,60 @@ static int rpcb_decode_getaddr(struct rpc_rqst *req, __be32 *p,
|
||||
static struct rpc_procinfo rpcb_procedures2[] = {
|
||||
PROC(SET, mapping, set),
|
||||
PROC(UNSET, mapping, set),
|
||||
PROC(GETADDR, mapping, getport),
|
||||
PROC(GETPORT, mapping, getport),
|
||||
};
|
||||
|
||||
static struct rpc_procinfo rpcb_procedures3[] = {
|
||||
PROC(SET, mapping, set),
|
||||
PROC(UNSET, mapping, set),
|
||||
PROC(SET, getaddr, set),
|
||||
PROC(UNSET, getaddr, set),
|
||||
PROC(GETADDR, getaddr, getaddr),
|
||||
};
|
||||
|
||||
static struct rpc_procinfo rpcb_procedures4[] = {
|
||||
PROC(SET, mapping, set),
|
||||
PROC(UNSET, mapping, set),
|
||||
PROC(SET, getaddr, set),
|
||||
PROC(UNSET, getaddr, set),
|
||||
PROC(GETADDR, getaddr, getaddr),
|
||||
PROC(GETVERSADDR, getaddr, getaddr),
|
||||
};
|
||||
|
||||
static struct rpcb_info rpcb_next_version[] = {
|
||||
#ifdef CONFIG_SUNRPC_BIND34
|
||||
{ 4, &rpcb_procedures4[RPCBPROC_GETVERSADDR] },
|
||||
{ 3, &rpcb_procedures3[RPCBPROC_GETADDR] },
|
||||
#endif
|
||||
{ 2, &rpcb_procedures2[RPCBPROC_GETPORT] },
|
||||
{ 0, NULL },
|
||||
{
|
||||
.rpc_vers = RPCBVERS_2,
|
||||
.rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT],
|
||||
},
|
||||
{
|
||||
.rpc_proc = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct rpcb_info rpcb_next_version6[] = {
|
||||
#ifdef CONFIG_SUNRPC_BIND34
|
||||
{ 4, &rpcb_procedures4[RPCBPROC_GETVERSADDR] },
|
||||
{ 3, &rpcb_procedures3[RPCBPROC_GETADDR] },
|
||||
#endif
|
||||
{ 0, NULL },
|
||||
{
|
||||
.rpc_vers = RPCBVERS_4,
|
||||
.rpc_proc = &rpcb_procedures4[RPCBPROC_GETADDR],
|
||||
},
|
||||
{
|
||||
.rpc_vers = RPCBVERS_3,
|
||||
.rpc_proc = &rpcb_procedures3[RPCBPROC_GETADDR],
|
||||
},
|
||||
{
|
||||
.rpc_proc = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct rpc_version rpcb_version2 = {
|
||||
.number = 2,
|
||||
.number = RPCBVERS_2,
|
||||
.nrprocs = RPCB_HIGHPROC_2,
|
||||
.procs = rpcb_procedures2
|
||||
};
|
||||
|
||||
static struct rpc_version rpcb_version3 = {
|
||||
.number = 3,
|
||||
.number = RPCBVERS_3,
|
||||
.nrprocs = RPCB_HIGHPROC_3,
|
||||
.procs = rpcb_procedures3
|
||||
};
|
||||
|
||||
static struct rpc_version rpcb_version4 = {
|
||||
.number = 4,
|
||||
.number = RPCBVERS_4,
|
||||
.nrprocs = RPCB_HIGHPROC_4,
|
||||
.procs = rpcb_procedures4
|
||||
};
|
||||
|
@ -576,9 +576,7 @@ EXPORT_SYMBOL_GPL(rpc_delay);
|
||||
*/
|
||||
static void rpc_prepare_task(struct rpc_task *task)
|
||||
{
|
||||
lock_kernel();
|
||||
task->tk_ops->rpc_call_prepare(task, task->tk_calldata);
|
||||
unlock_kernel();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -588,9 +586,7 @@ void rpc_exit_task(struct rpc_task *task)
|
||||
{
|
||||
task->tk_action = NULL;
|
||||
if (task->tk_ops->rpc_call_done != NULL) {
|
||||
lock_kernel();
|
||||
task->tk_ops->rpc_call_done(task, task->tk_calldata);
|
||||
unlock_kernel();
|
||||
if (task->tk_action != NULL) {
|
||||
WARN_ON(RPC_ASSASSINATED(task));
|
||||
/* Always release the RPC slot and buffer memory */
|
||||
@ -602,11 +598,8 @@ EXPORT_SYMBOL_GPL(rpc_exit_task);
|
||||
|
||||
void rpc_release_calldata(const struct rpc_call_ops *ops, void *calldata)
|
||||
{
|
||||
if (ops->rpc_release != NULL) {
|
||||
lock_kernel();
|
||||
if (ops->rpc_release != NULL)
|
||||
ops->rpc_release(calldata);
|
||||
unlock_kernel();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -626,19 +619,15 @@ static void __rpc_execute(struct rpc_task *task)
|
||||
/*
|
||||
* Execute any pending callback.
|
||||
*/
|
||||
if (RPC_DO_CALLBACK(task)) {
|
||||
/* Define a callback save pointer */
|
||||
if (task->tk_callback) {
|
||||
void (*save_callback)(struct rpc_task *);
|
||||
|
||||
/*
|
||||
* If a callback exists, save it, reset it,
|
||||
* call it.
|
||||
* The save is needed to stop from resetting
|
||||
* another callback set within the callback handler
|
||||
* - Dave
|
||||
* We set tk_callback to NULL before calling it,
|
||||
* in case it sets the tk_callback field itself:
|
||||
*/
|
||||
save_callback=task->tk_callback;
|
||||
task->tk_callback=NULL;
|
||||
save_callback = task->tk_callback;
|
||||
task->tk_callback = NULL;
|
||||
save_callback(task);
|
||||
}
|
||||
|
||||
|
@ -690,7 +690,7 @@ static void xprt_connect_status(struct rpc_task *task)
|
||||
{
|
||||
struct rpc_xprt *xprt = task->tk_xprt;
|
||||
|
||||
if (task->tk_status >= 0) {
|
||||
if (task->tk_status == 0) {
|
||||
xprt->stat.connect_count++;
|
||||
xprt->stat.connect_time += (long)jiffies - xprt->stat.connect_start;
|
||||
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) {
|
||||
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:
|
||||
dprintk("RPC: %5u xprt_connect_status: connection broken\n",
|
||||
task->tk_pid);
|
||||
@ -878,6 +872,7 @@ void xprt_transmit(struct rpc_task *task)
|
||||
return;
|
||||
|
||||
req->rq_connect_cookie = xprt->connect_cookie;
|
||||
req->rq_xtime = jiffies;
|
||||
status = xprt->ops->send_request(task);
|
||||
if (status == 0) {
|
||||
dprintk("RPC: %5u xmit complete\n", task->tk_pid);
|
||||
|
@ -579,7 +579,6 @@ static int xs_udp_send_request(struct rpc_task *task)
|
||||
req->rq_svec->iov_base,
|
||||
req->rq_svec->iov_len);
|
||||
|
||||
req->rq_xtime = jiffies;
|
||||
status = xs_sendpages(transport->sock,
|
||||
xs_addr(xprt),
|
||||
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
|
||||
* called sendmsg(). */
|
||||
while (1) {
|
||||
req->rq_xtime = jiffies;
|
||||
status = xs_sendpages(transport->sock,
|
||||
NULL, 0, xdr, req->rq_bytes_sent);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user