2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* fs/nfs/nfs4proc.c
|
|
|
|
*
|
|
|
|
* Client-side procedure declarations for NFSv4.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2002 The Regents of the University of Michigan.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Kendrick Smith <kmsmith@umich.edu>
|
|
|
|
* Andy Adamson <andros@umich.edu>
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of the University nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/string.h>
|
2011-12-10 07:05:58 +07:00
|
|
|
#include <linux/ratelimit.h>
|
|
|
|
#include <linux/printk.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/sunrpc/clnt.h>
|
|
|
|
#include <linux/nfs.h>
|
|
|
|
#include <linux/nfs4.h>
|
|
|
|
#include <linux/nfs_fs.h>
|
|
|
|
#include <linux/nfs_page.h>
|
2011-04-14 01:31:30 +07:00
|
|
|
#include <linux/nfs_mount.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/namei.h>
|
2005-10-19 04:20:17 +07:00
|
|
|
#include <linux/mount.h>
|
2009-04-01 20:22:29 +07:00
|
|
|
#include <linux/module.h>
|
2012-01-08 01:22:46 +07:00
|
|
|
#include <linux/nfs_idmap.h>
|
2010-12-09 18:35:25 +07:00
|
|
|
#include <linux/xattr.h>
|
2011-01-26 07:15:32 +07:00
|
|
|
#include <linux/utsname.h>
|
2011-12-02 04:44:39 +07:00
|
|
|
#include <linux/freezer.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-06-23 00:16:21 +07:00
|
|
|
#include "nfs4_fs.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
#include "delegation.h"
|
2008-02-20 08:04:23 +07:00
|
|
|
#include "internal.h"
|
2006-03-21 01:44:14 +07:00
|
|
|
#include "iostat.h"
|
2009-04-01 20:22:31 +07:00
|
|
|
#include "callback.h"
|
2010-10-20 11:18:03 +07:00
|
|
|
#include "pnfs.h"
|
NFS: Always use the same SETCLIENTID boot verifier
Currently our NFS client assigns a unique SETCLIENTID boot verifier
for each server IP address it knows about. It's set to CURRENT_TIME
when the struct nfs_client for that server IP is created.
During the SETCLIENTID operation, our client also presents an
nfs_client_id4 string to servers, as an identifier on which the server
can hang all of this client's NFSv4 state. Our client's
nfs_client_id4 string is unique for each server IP address.
An NFSv4 server is obligated to wipe all NFSv4 state associated with
an nfs_client_id4 string when the client presents the same
nfs_client_id4 string along with a changed SETCLIENTID boot verifier.
When our client unmounts the last of a server's shares, it destroys
that server's struct nfs_client. The next time the client mounts that
NFS server, it creates a fresh struct nfs_client with a fresh boot
verifier. On seeing the fresh verifer, the server wipes any previous
NFSv4 state associated with that nfs_client_id4.
However, NFSv4.1 clients are supposed to present the same
nfs_client_id4 string to all servers. And, to support Transparent
State Migration, the same nfs_client_id4 string should be presented
to all NFSv4.0 servers so they recognize that migrated state for this
client belongs with state a server may already have for this client.
(This is known as the Uniform Client String model).
If the nfs_client_id4 string is the same but the boot verifier changes
for each server IP address, SETCLIENTID and EXCHANGE_ID operations
from such a client could unintentionally result in a server wiping a
client's previously obtained lease.
Thus, if our NFS client is going to use a fixed nfs_client_id4 string,
either for NFSv4.0 or NFSv4.1 mounts, our NFS client should use a
boot verifier that does not change depending on server IP address.
Replace our current per-nfs_client boot verifier with a per-nfs_net
boot verifier.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-05-22 09:45:41 +07:00
|
|
|
#include "netns.h"
|
2012-11-27 00:49:34 +07:00
|
|
|
#include "nfs4session.h"
|
2012-12-21 04:52:38 +07:00
|
|
|
#include "fscache.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#define NFSDBG_FACILITY NFSDBG_PROC
|
|
|
|
|
2006-09-15 19:30:46 +07:00
|
|
|
#define NFS4_POLL_RETRY_MIN (HZ/10)
|
2005-04-17 05:20:36 +07:00
|
|
|
#define NFS4_POLL_RETRY_MAX (15*HZ)
|
|
|
|
|
2006-01-03 15:55:12 +07:00
|
|
|
struct nfs4_opendata;
|
2006-01-03 15:55:15 +07:00
|
|
|
static int _nfs4_proc_open(struct nfs4_opendata *data);
|
2009-12-15 12:27:57 +07:00
|
|
|
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
|
2005-04-17 05:20:36 +07:00
|
|
|
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
|
2008-12-24 03:21:46 +07:00
|
|
|
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
|
2012-03-02 05:01:57 +07:00
|
|
|
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
|
2012-05-11 02:07:31 +07:00
|
|
|
static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);
|
2007-07-18 08:52:41 +07:00
|
|
|
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
|
2010-04-17 03:22:51 +07:00
|
|
|
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
|
|
|
struct nfs_fattr *fattr, struct iattr *sattr,
|
|
|
|
struct nfs4_state *state);
|
2011-06-03 01:59:10 +07:00
|
|
|
#ifdef CONFIG_NFS_V4_1
|
2013-05-20 22:20:27 +07:00
|
|
|
static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
|
|
|
|
struct rpc_cred *);
|
|
|
|
static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
|
|
|
|
struct rpc_cred *);
|
2011-06-03 01:59:10 +07:00
|
|
|
#endif
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Prevent leaks of NFSv4 errors into userland */
|
2008-12-31 04:35:55 +07:00
|
|
|
static int nfs4_map_errors(int err)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-10-24 01:46:42 +07:00
|
|
|
if (err >= -1000)
|
|
|
|
return err;
|
|
|
|
switch (err) {
|
|
|
|
case -NFS4ERR_RESOURCE:
|
2013-03-01 08:30:10 +07:00
|
|
|
case -NFS4ERR_LAYOUTTRYLATER:
|
|
|
|
case -NFS4ERR_RECALLCONFLICT:
|
2009-10-24 01:46:42 +07:00
|
|
|
return -EREMOTEIO;
|
2011-03-25 00:12:30 +07:00
|
|
|
case -NFS4ERR_WRONGSEC:
|
|
|
|
return -EPERM;
|
2011-02-23 06:44:31 +07:00
|
|
|
case -NFS4ERR_BADOWNER:
|
|
|
|
case -NFS4ERR_BADNAME:
|
|
|
|
return -EINVAL;
|
2012-05-28 22:36:28 +07:00
|
|
|
case -NFS4ERR_SHARE_DENIED:
|
|
|
|
return -EACCES;
|
2012-06-07 01:12:07 +07:00
|
|
|
case -NFS4ERR_MINOR_VERS_MISMATCH:
|
|
|
|
return -EPROTONOSUPPORT;
|
2012-09-11 01:00:46 +07:00
|
|
|
case -NFS4ERR_ACCESS:
|
|
|
|
return -EACCES;
|
2013-03-24 02:22:45 +07:00
|
|
|
case -NFS4ERR_FILE_OPEN:
|
|
|
|
return -EBUSY;
|
2009-10-24 01:46:42 +07:00
|
|
|
default:
|
2005-04-17 05:20:36 +07:00
|
|
|
dprintk("%s could not handle NFSv4 error %d\n",
|
2008-05-03 03:42:44 +07:00
|
|
|
__func__, -err);
|
2009-10-24 01:46:42 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-10-24 01:46:42 +07:00
|
|
|
return -EIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is our standard bitmap for GETATTR requests.
|
|
|
|
*/
|
2012-06-05 20:16:47 +07:00
|
|
|
const u32 nfs4_fattr_bitmap[3] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
FATTR4_WORD0_TYPE
|
|
|
|
| FATTR4_WORD0_CHANGE
|
|
|
|
| FATTR4_WORD0_SIZE
|
|
|
|
| FATTR4_WORD0_FSID
|
|
|
|
| FATTR4_WORD0_FILEID,
|
|
|
|
FATTR4_WORD1_MODE
|
|
|
|
| FATTR4_WORD1_NUMLINKS
|
|
|
|
| FATTR4_WORD1_OWNER
|
|
|
|
| FATTR4_WORD1_OWNER_GROUP
|
|
|
|
| FATTR4_WORD1_RAWDEV
|
|
|
|
| FATTR4_WORD1_SPACE_USED
|
|
|
|
| FATTR4_WORD1_TIME_ACCESS
|
|
|
|
| FATTR4_WORD1_TIME_METADATA
|
|
|
|
| FATTR4_WORD1_TIME_MODIFY
|
|
|
|
};
|
|
|
|
|
2012-06-05 20:16:47 +07:00
|
|
|
static const u32 nfs4_pnfs_open_bitmap[3] = {
|
|
|
|
FATTR4_WORD0_TYPE
|
|
|
|
| FATTR4_WORD0_CHANGE
|
|
|
|
| FATTR4_WORD0_SIZE
|
|
|
|
| FATTR4_WORD0_FSID
|
|
|
|
| FATTR4_WORD0_FILEID,
|
|
|
|
FATTR4_WORD1_MODE
|
|
|
|
| FATTR4_WORD1_NUMLINKS
|
|
|
|
| FATTR4_WORD1_OWNER
|
|
|
|
| FATTR4_WORD1_OWNER_GROUP
|
|
|
|
| FATTR4_WORD1_RAWDEV
|
|
|
|
| FATTR4_WORD1_SPACE_USED
|
|
|
|
| FATTR4_WORD1_TIME_ACCESS
|
|
|
|
| FATTR4_WORD1_TIME_METADATA
|
|
|
|
| FATTR4_WORD1_TIME_MODIFY,
|
|
|
|
FATTR4_WORD2_MDSTHRESHOLD
|
|
|
|
};
|
|
|
|
|
2012-10-03 08:07:32 +07:00
|
|
|
static const u32 nfs4_open_noattr_bitmap[3] = {
|
|
|
|
FATTR4_WORD0_TYPE
|
|
|
|
| FATTR4_WORD0_CHANGE
|
|
|
|
| FATTR4_WORD0_FILEID,
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
const u32 nfs4_statfs_bitmap[2] = {
|
|
|
|
FATTR4_WORD0_FILES_AVAIL
|
|
|
|
| FATTR4_WORD0_FILES_FREE
|
|
|
|
| FATTR4_WORD0_FILES_TOTAL,
|
|
|
|
FATTR4_WORD1_SPACE_AVAIL
|
|
|
|
| FATTR4_WORD1_SPACE_FREE
|
|
|
|
| FATTR4_WORD1_SPACE_TOTAL
|
|
|
|
};
|
|
|
|
|
2005-06-23 00:16:21 +07:00
|
|
|
const u32 nfs4_pathconf_bitmap[2] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
FATTR4_WORD0_MAXLINK
|
|
|
|
| FATTR4_WORD0_MAXNAME,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2011-07-31 07:52:37 +07:00
|
|
|
const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
|
2005-04-17 05:20:36 +07:00
|
|
|
| FATTR4_WORD0_MAXREAD
|
|
|
|
| FATTR4_WORD0_MAXWRITE
|
|
|
|
| FATTR4_WORD0_LEASE_TIME,
|
2010-10-13 06:30:06 +07:00
|
|
|
FATTR4_WORD1_TIME_DELTA
|
2011-07-31 07:52:37 +07:00
|
|
|
| FATTR4_WORD1_FS_LAYOUT_TYPES,
|
|
|
|
FATTR4_WORD2_LAYOUT_BLKSIZE
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-06-09 20:34:25 +07:00
|
|
|
const u32 nfs4_fs_locations_bitmap[2] = {
|
|
|
|
FATTR4_WORD0_TYPE
|
|
|
|
| FATTR4_WORD0_CHANGE
|
|
|
|
| FATTR4_WORD0_SIZE
|
|
|
|
| FATTR4_WORD0_FSID
|
|
|
|
| FATTR4_WORD0_FILEID
|
|
|
|
| FATTR4_WORD0_FS_LOCATIONS,
|
|
|
|
FATTR4_WORD1_MODE
|
|
|
|
| FATTR4_WORD1_NUMLINKS
|
|
|
|
| FATTR4_WORD1_OWNER
|
|
|
|
| FATTR4_WORD1_OWNER_GROUP
|
|
|
|
| FATTR4_WORD1_RAWDEV
|
|
|
|
| FATTR4_WORD1_SPACE_USED
|
|
|
|
| FATTR4_WORD1_TIME_ACCESS
|
|
|
|
| FATTR4_WORD1_TIME_METADATA
|
|
|
|
| FATTR4_WORD1_TIME_MODIFY
|
|
|
|
| FATTR4_WORD1_MOUNTED_ON_FILEID
|
|
|
|
};
|
|
|
|
|
2006-10-20 13:28:51 +07:00
|
|
|
static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_readdir_arg *readdir)
|
|
|
|
{
|
2006-10-20 13:28:49 +07:00
|
|
|
__be32 *start, *p;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (cookie > 2) {
|
2005-06-23 00:16:28 +07:00
|
|
|
readdir->cookie = cookie;
|
2005-04-17 05:20:36 +07:00
|
|
|
memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
readdir->cookie = 0;
|
|
|
|
memset(&readdir->verifier, 0, sizeof(readdir->verifier));
|
|
|
|
if (cookie == 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NFSv4 servers do not return entries for '.' and '..'
|
|
|
|
* Therefore, we fake these entries here. We let '.'
|
|
|
|
* have cookie 0 and '..' have cookie 1. Note that
|
|
|
|
* when talking to the server, we always send cookie 0
|
|
|
|
* instead of 1 or 2.
|
|
|
|
*/
|
2011-11-25 22:14:33 +07:00
|
|
|
start = p = kmap_atomic(*readdir->pages);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (cookie == 0) {
|
|
|
|
*p++ = xdr_one; /* next */
|
|
|
|
*p++ = xdr_zero; /* cookie, first word */
|
|
|
|
*p++ = xdr_one; /* cookie, second word */
|
|
|
|
*p++ = xdr_one; /* entry len */
|
|
|
|
memcpy(p, ".\0\0\0", 4); /* entry */
|
|
|
|
p++;
|
|
|
|
*p++ = xdr_one; /* bitmap length */
|
|
|
|
*p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */
|
|
|
|
*p++ = htonl(8); /* attribute buffer length */
|
2007-08-04 02:07:10 +07:00
|
|
|
p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode));
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
*p++ = xdr_one; /* next */
|
|
|
|
*p++ = xdr_zero; /* cookie, first word */
|
|
|
|
*p++ = xdr_two; /* cookie, second word */
|
|
|
|
*p++ = xdr_two; /* entry len */
|
|
|
|
memcpy(p, "..\0\0", 4); /* entry */
|
|
|
|
p++;
|
|
|
|
*p++ = xdr_one; /* bitmap length */
|
|
|
|
*p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */
|
|
|
|
*p++ = htonl(8); /* attribute buffer length */
|
2007-08-04 02:07:10 +07:00
|
|
|
p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
readdir->pgbase = (char *)p - (char *)start;
|
|
|
|
readdir->count -= readdir->pgbase;
|
2011-11-25 22:14:33 +07:00
|
|
|
kunmap_atomic(start);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:44 +07:00
|
|
|
static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
might_sleep();
|
|
|
|
|
|
|
|
if (*timeout <= 0)
|
|
|
|
*timeout = NFS4_POLL_RETRY_MIN;
|
|
|
|
if (*timeout > NFS4_POLL_RETRY_MAX)
|
|
|
|
*timeout = NFS4_POLL_RETRY_MAX;
|
2011-12-02 04:44:39 +07:00
|
|
|
freezable_schedule_timeout_killable(*timeout);
|
2008-12-24 03:21:44 +07:00
|
|
|
if (fatal_signal_pending(current))
|
|
|
|
res = -ERESTARTSYS;
|
|
|
|
*timeout <<= 1;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is the error handling routine for processes that are allowed
|
|
|
|
* to sleep.
|
|
|
|
*/
|
2011-02-23 06:44:32 +07:00
|
|
|
static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
|
2008-12-24 03:21:44 +07:00
|
|
|
{
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
2008-12-24 03:21:46 +07:00
|
|
|
struct nfs4_state *state = exception->state;
|
2012-03-08 04:39:06 +07:00
|
|
|
struct inode *inode = exception->inode;
|
2008-12-24 03:21:44 +07:00
|
|
|
int ret = errorcode;
|
|
|
|
|
|
|
|
exception->retry = 0;
|
|
|
|
switch(errorcode) {
|
|
|
|
case 0:
|
|
|
|
return 0;
|
2012-03-08 04:39:06 +07:00
|
|
|
case -NFS4ERR_OPENMODE:
|
2012-06-21 02:53:43 +07:00
|
|
|
if (inode && nfs4_have_delegation(inode, FMODE_READ)) {
|
2012-06-21 02:53:44 +07:00
|
|
|
nfs4_inode_return_delegation(inode);
|
2012-03-08 04:39:06 +07:00
|
|
|
exception->retry = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (state == NULL)
|
|
|
|
break;
|
2013-03-15 03:57:48 +07:00
|
|
|
ret = nfs4_schedule_stateid_recovery(server, state);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
2012-03-08 04:39:06 +07:00
|
|
|
goto wait_on_recovery;
|
2012-03-06 07:56:44 +07:00
|
|
|
case -NFS4ERR_DELEG_REVOKED:
|
2008-12-24 03:21:46 +07:00
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
2013-04-13 02:04:51 +07:00
|
|
|
if (inode != NULL && nfs4_have_delegation(inode, FMODE_READ)) {
|
|
|
|
nfs_remove_bad_delegation(inode);
|
|
|
|
exception->retry = 1;
|
|
|
|
break;
|
|
|
|
}
|
2008-12-24 03:21:46 +07:00
|
|
|
if (state == NULL)
|
|
|
|
break;
|
2013-03-15 03:57:48 +07:00
|
|
|
ret = nfs4_schedule_stateid_recovery(server, state);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
2011-03-10 04:00:53 +07:00
|
|
|
goto wait_on_recovery;
|
2011-05-27 01:26:35 +07:00
|
|
|
case -NFS4ERR_EXPIRED:
|
2013-03-15 03:57:48 +07:00
|
|
|
if (state != NULL) {
|
|
|
|
ret = nfs4_schedule_stateid_recovery(server, state);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
}
|
2008-12-24 03:21:44 +07:00
|
|
|
case -NFS4ERR_STALE_STATEID:
|
2010-01-27 03:42:47 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_lease_recovery(clp);
|
|
|
|
goto wait_on_recovery;
|
2010-01-27 03:42:38 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
nfs41: kick start nfs41 session recovery when handling errors
Remove checking for any errors that the SEQUENCE operation does not return.
-NFS4ERR_STALE_CLIENTID, NFS4ERR_EXPIRED, NFS4ERR_CB_PATH_DOWN, NFS4ERR_BACK_CHAN_BUSY, NFS4ERR_OP_NOT_IN_SESSION.
SEQUENCE operation error recovery is very primative, we only reset the session.
Remove checking for any errors that are returned by the SEQUENCE operation, but
that resetting the session won't address.
NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_SEQUENCE_POS,NFS4ERR_TOO_MANY_OPS.
Add error checking for missing SEQUENCE errors that a session reset will
address.
NFS4ERR_BAD_HIGH_SLOT, NFS4ERR_DEADSESSION, NFS4ERR_SEQ_FALSE_RETRY.
A reset of the session is currently our only response to a SEQUENCE operation
error. Don't reset the session on errors where a new session won't help.
Don't reset the session on errors where a new session won't help.
[nfs41: nfs4_async_handle_error update error checking]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[nfs41: trigger the state manager for session reset]
Replace session state bit with nfs_client state bit. Set the
NFS4CLNT_SESSION_SETUP bit upon a session related error in the sync/async
error handlers.
[nfs41: _nfs4_async_handle_error fix session reset error list]
Sequence operation errors that session reset could help.
NFS4ERR_BADSESSION
NFS4ERR_BADSLOT
NFS4ERR_BAD_HIGH_SLOT
NFS4ERR_DEADSESSION
NFS4ERR_CONN_NOT_BOUND_TO_SESSION
NFS4ERR_SEQ_FALSE_RETRY
NFS4ERR_SEQ_MISORDERED
Sequence operation errors that a session reset would not help
NFS4ERR_BADXDR
NFS4ERR_DELAY
NFS4ERR_REP_TOO_BIG
NFS4ERR_REP_TOO_BIG_TO_CACHE
NFS4ERR_REQ_TOO_BIG
NFS4ERR_RETRY_UNCACHED_REP
NFS4ERR_SEQUENCE_POS
NFS4ERR_TOO_MANY_OPS
Signed-off-by: Andy Adamson <andros@netapp.com>
[nfs41 nfs4_handle_exception fix session reset error list]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[moved nfs41_sequece_call_done code to nfs41: sequence operation]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-04-01 20:22:42 +07:00
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
case -NFS4ERR_BAD_HIGH_SLOT:
|
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
|
|
|
case -NFS4ERR_DEADSESSION:
|
|
|
|
case -NFS4ERR_SEQ_FALSE_RETRY:
|
|
|
|
case -NFS4ERR_SEQ_MISORDERED:
|
|
|
|
dprintk("%s ERROR: %d Reset session\n", __func__,
|
|
|
|
errorcode);
|
2012-05-28 00:02:53 +07:00
|
|
|
nfs4_schedule_session_recovery(clp->cl_session, errorcode);
|
2012-10-31 03:06:35 +07:00
|
|
|
goto wait_on_recovery;
|
2010-01-27 03:42:38 +07:00
|
|
|
#endif /* defined(CONFIG_NFS_V4_1) */
|
2008-12-24 03:21:44 +07:00
|
|
|
case -NFS4ERR_FILE_OPEN:
|
2009-12-04 03:58:56 +07:00
|
|
|
if (exception->timeout > HZ) {
|
|
|
|
/* We have retried a decent amount, time to
|
|
|
|
* fail
|
|
|
|
*/
|
|
|
|
ret = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
2008-12-24 03:21:44 +07:00
|
|
|
case -NFS4ERR_GRACE:
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
ret = nfs4_delay(server->client, &exception->timeout);
|
|
|
|
if (ret != 0)
|
|
|
|
break;
|
2011-05-04 00:43:03 +07:00
|
|
|
case -NFS4ERR_RETRY_UNCACHED_REP:
|
2008-12-24 03:21:44 +07:00
|
|
|
case -NFS4ERR_OLD_STATEID:
|
|
|
|
exception->retry = 1;
|
2011-02-23 06:44:32 +07:00
|
|
|
break;
|
|
|
|
case -NFS4ERR_BADOWNER:
|
|
|
|
/* The following works around a Linux server bug! */
|
|
|
|
case -NFS4ERR_BADNAME:
|
|
|
|
if (server->caps & NFS_CAP_UIDGID_NOMAP) {
|
|
|
|
server->caps &= ~NFS_CAP_UIDGID_NOMAP;
|
|
|
|
exception->retry = 1;
|
|
|
|
printk(KERN_WARNING "NFS: v4 server %s "
|
|
|
|
"does not accept raw "
|
|
|
|
"uid/gids. "
|
|
|
|
"Reenabling the idmapper.\n",
|
|
|
|
server->nfs_client->cl_hostname);
|
|
|
|
}
|
2008-12-24 03:21:44 +07:00
|
|
|
}
|
|
|
|
/* We failed to handle the error */
|
|
|
|
return nfs4_map_errors(ret);
|
2011-03-10 04:00:53 +07:00
|
|
|
wait_on_recovery:
|
2010-01-27 03:42:47 +07:00
|
|
|
ret = nfs4_wait_clnt_recover(clp);
|
|
|
|
if (ret == 0)
|
|
|
|
exception->retry = 1;
|
|
|
|
return ret;
|
2008-12-24 03:21:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
if (time_before(clp->cl_last_renewal,timestamp))
|
|
|
|
clp->cl_last_renewal = timestamp;
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
}
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
|
|
|
|
{
|
|
|
|
do_renew_lease(server->nfs_client, timestamp);
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:03 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
|
2010-06-16 20:52:25 +07:00
|
|
|
static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
|
2009-04-01 20:22:17 +07:00
|
|
|
{
|
2012-11-17 00:25:01 +07:00
|
|
|
struct nfs4_session *session;
|
2009-04-01 20:22:17 +07:00
|
|
|
struct nfs4_slot_table *tbl;
|
2012-11-27 04:16:54 +07:00
|
|
|
bool send_new_highest_used_slotid = false;
|
2009-04-01 20:22:17 +07:00
|
|
|
|
2010-09-24 20:17:01 +07:00
|
|
|
if (!res->sr_slot) {
|
2009-04-01 20:22:17 +07:00
|
|
|
/* just wake up the next guy waiting since
|
|
|
|
* we may have not consumed a slot after all */
|
2009-12-05 03:55:39 +07:00
|
|
|
dprintk("%s: No slot\n", __func__);
|
2009-12-06 07:32:19 +07:00
|
|
|
return;
|
2009-04-01 20:22:17 +07:00
|
|
|
}
|
2012-11-17 00:25:01 +07:00
|
|
|
tbl = res->sr_slot->table;
|
|
|
|
session = tbl->session;
|
2009-12-05 03:55:38 +07:00
|
|
|
|
2009-12-06 07:32:19 +07:00
|
|
|
spin_lock(&tbl->slot_tbl_lock);
|
2012-11-27 04:16:54 +07:00
|
|
|
/* Be nice to the server: try to ensure that the last transmitted
|
|
|
|
* value for highest_user_slotid <= target_highest_slotid
|
|
|
|
*/
|
|
|
|
if (tbl->highest_used_slotid > tbl->target_highest_slotid)
|
|
|
|
send_new_highest_used_slotid = true;
|
|
|
|
|
2012-11-30 05:27:47 +07:00
|
|
|
if (nfs41_wake_and_assign_slot(tbl, res->sr_slot)) {
|
|
|
|
send_new_highest_used_slotid = false;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2012-11-21 02:17:32 +07:00
|
|
|
nfs4_free_slot(tbl, res->sr_slot);
|
2012-11-27 04:16:54 +07:00
|
|
|
|
|
|
|
if (tbl->highest_used_slotid != NFS4_NO_SLOT)
|
|
|
|
send_new_highest_used_slotid = false;
|
2012-11-30 05:27:47 +07:00
|
|
|
out_unlock:
|
2009-12-06 07:32:19 +07:00
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
2010-09-24 20:17:01 +07:00
|
|
|
res->sr_slot = NULL;
|
2012-11-27 04:16:54 +07:00
|
|
|
if (send_new_highest_used_slotid)
|
|
|
|
nfs41_server_notify_highest_slotid_update(session->clp);
|
2009-04-01 20:22:17 +07:00
|
|
|
}
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
|
2009-04-01 20:22:18 +07:00
|
|
|
{
|
2012-11-17 00:25:01 +07:00
|
|
|
struct nfs4_session *session;
|
2012-11-17 00:12:38 +07:00
|
|
|
struct nfs4_slot *slot;
|
2010-08-01 01:29:06 +07:00
|
|
|
struct nfs_client *clp;
|
2012-12-16 03:36:07 +07:00
|
|
|
bool interrupted = false;
|
2012-12-11 22:31:12 +07:00
|
|
|
int ret = 1;
|
2009-04-01 20:22:18 +07:00
|
|
|
|
2011-04-19 02:57:32 +07:00
|
|
|
/* don't increment the sequence number if the task wasn't sent */
|
|
|
|
if (!RPC_WAS_SENT(task))
|
2009-04-01 20:22:18 +07:00
|
|
|
goto out;
|
|
|
|
|
2012-11-17 00:12:38 +07:00
|
|
|
slot = res->sr_slot;
|
2012-11-17 00:25:01 +07:00
|
|
|
session = slot->table->session;
|
2012-11-17 00:12:38 +07:00
|
|
|
|
2012-12-16 03:36:07 +07:00
|
|
|
if (slot->interrupted) {
|
|
|
|
slot->interrupted = 0;
|
|
|
|
interrupted = true;
|
|
|
|
}
|
|
|
|
|
2009-12-05 03:55:39 +07:00
|
|
|
/* Check the SEQUENCE operation status */
|
2010-08-01 01:29:06 +07:00
|
|
|
switch (res->sr_status) {
|
|
|
|
case 0:
|
2009-04-01 20:22:18 +07:00
|
|
|
/* Update the slot's sequence and clientid lease timer */
|
2012-11-17 00:12:38 +07:00
|
|
|
++slot->seq_nr;
|
2012-11-17 00:25:01 +07:00
|
|
|
clp = session->clp;
|
2012-12-16 03:21:52 +07:00
|
|
|
do_renew_lease(clp, res->sr_timestamp);
|
2009-12-06 01:46:14 +07:00
|
|
|
/* Check sequence flags */
|
2011-03-10 04:00:55 +07:00
|
|
|
if (res->sr_status_flags != 0)
|
|
|
|
nfs4_schedule_lease_recovery(clp);
|
2012-11-21 00:49:27 +07:00
|
|
|
nfs41_update_target_slotid(slot->table, slot, res);
|
2010-08-01 01:29:06 +07:00
|
|
|
break;
|
2012-12-16 03:36:07 +07:00
|
|
|
case 1:
|
|
|
|
/*
|
|
|
|
* sr_status remains 1 if an RPC level error occurred.
|
|
|
|
* The server may or may not have processed the sequence
|
|
|
|
* operation..
|
|
|
|
* Mark the slot as having hosted an interrupted RPC call.
|
|
|
|
*/
|
|
|
|
slot->interrupted = 1;
|
|
|
|
goto out;
|
2010-08-01 01:29:06 +07:00
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
/* The server detected a resend of the RPC call and
|
|
|
|
* returned NFS4ERR_DELAY as per Section 2.10.6.2
|
|
|
|
* of RFC5661.
|
|
|
|
*/
|
2012-11-17 00:45:06 +07:00
|
|
|
dprintk("%s: slot=%u seq=%u: Operation in progress\n",
|
2010-09-24 20:17:01 +07:00
|
|
|
__func__,
|
2012-11-17 00:45:06 +07:00
|
|
|
slot->slot_nr,
|
2012-11-17 00:12:38 +07:00
|
|
|
slot->seq_nr);
|
2010-08-01 01:29:06 +07:00
|
|
|
goto out_retry;
|
2012-12-11 22:31:12 +07:00
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
/*
|
|
|
|
* The slot id we used was probably retired. Try again
|
|
|
|
* using a different slot id.
|
|
|
|
*/
|
2012-12-16 01:56:18 +07:00
|
|
|
goto retry_nowait;
|
|
|
|
case -NFS4ERR_SEQ_MISORDERED:
|
2012-12-16 03:36:07 +07:00
|
|
|
/*
|
|
|
|
* Was the last operation on this sequence interrupted?
|
|
|
|
* If so, retry after bumping the sequence number.
|
|
|
|
*/
|
|
|
|
if (interrupted) {
|
|
|
|
++slot->seq_nr;
|
|
|
|
goto retry_nowait;
|
|
|
|
}
|
2012-12-16 01:56:18 +07:00
|
|
|
/*
|
|
|
|
* Could this slot have been previously retired?
|
|
|
|
* If so, then the server may be expecting seq_nr = 1!
|
|
|
|
*/
|
2012-12-16 03:21:52 +07:00
|
|
|
if (slot->seq_nr != 1) {
|
|
|
|
slot->seq_nr = 1;
|
|
|
|
goto retry_nowait;
|
|
|
|
}
|
|
|
|
break;
|
2012-12-16 01:56:18 +07:00
|
|
|
case -NFS4ERR_SEQ_FALSE_RETRY:
|
|
|
|
++slot->seq_nr;
|
|
|
|
goto retry_nowait;
|
2010-08-01 01:29:06 +07:00
|
|
|
default:
|
|
|
|
/* Just update the slot sequence no. */
|
2012-11-17 00:12:38 +07:00
|
|
|
++slot->seq_nr;
|
2009-04-01 20:22:18 +07:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
/* The session may be reset by one of the error handlers. */
|
|
|
|
dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
|
2010-06-16 20:52:25 +07:00
|
|
|
nfs41_sequence_free_slot(res);
|
2012-12-11 22:31:12 +07:00
|
|
|
return ret;
|
2012-12-16 01:56:18 +07:00
|
|
|
retry_nowait:
|
|
|
|
if (rpc_restart_call_prepare(task)) {
|
|
|
|
task->tk_status = 0;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
goto out;
|
2010-08-01 01:29:06 +07:00
|
|
|
out_retry:
|
2010-08-01 01:29:07 +07:00
|
|
|
if (!rpc_restart_call(task))
|
2010-08-01 01:29:06 +07:00
|
|
|
goto out;
|
|
|
|
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
|
|
|
return 0;
|
2009-04-01 20:22:18 +07:00
|
|
|
}
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
static int nfs4_sequence_done(struct rpc_task *task,
|
|
|
|
struct nfs4_sequence_res *res)
|
2010-06-16 20:52:26 +07:00
|
|
|
{
|
2012-11-17 00:25:01 +07:00
|
|
|
if (res->sr_slot == NULL)
|
2010-08-01 01:29:06 +07:00
|
|
|
return 1;
|
|
|
|
return nfs41_sequence_done(task, res);
|
2010-06-16 20:52:26 +07:00
|
|
|
}
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
static void nfs41_init_sequence(struct nfs4_sequence_args *args,
|
|
|
|
struct nfs4_sequence_res *res, int cache_reply)
|
|
|
|
{
|
2012-11-17 00:58:36 +07:00
|
|
|
args->sa_slot = NULL;
|
2012-01-18 10:04:25 +07:00
|
|
|
args->sa_cache_this = 0;
|
2012-10-30 06:02:20 +07:00
|
|
|
args->sa_privileged = 0;
|
2012-01-18 10:04:25 +07:00
|
|
|
if (cache_reply)
|
|
|
|
args->sa_cache_this = 1;
|
|
|
|
res->sr_slot = NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-30 06:02:20 +07:00
|
|
|
static void nfs4_set_sequence_privileged(struct nfs4_sequence_args *args)
|
|
|
|
{
|
|
|
|
args->sa_privileged = 1;
|
|
|
|
}
|
|
|
|
|
2011-03-01 08:34:19 +07:00
|
|
|
int nfs41_setup_sequence(struct nfs4_session *session,
|
2009-04-01 20:22:13 +07:00
|
|
|
struct nfs4_sequence_args *args,
|
|
|
|
struct nfs4_sequence_res *res,
|
|
|
|
struct rpc_task *task)
|
|
|
|
{
|
2009-04-01 20:22:15 +07:00
|
|
|
struct nfs4_slot *slot;
|
|
|
|
struct nfs4_slot_table *tbl;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2009-04-01 20:22:13 +07:00
|
|
|
/* slot already allocated? */
|
2010-09-24 20:17:01 +07:00
|
|
|
if (res->sr_slot != NULL)
|
2012-10-23 07:28:44 +07:00
|
|
|
goto out_success;
|
2009-04-01 20:22:13 +07:00
|
|
|
|
2009-04-01 20:22:15 +07:00
|
|
|
tbl = &session->fc_slot_table;
|
|
|
|
|
2012-11-23 01:21:02 +07:00
|
|
|
task->tk_timeout = 0;
|
|
|
|
|
2009-04-01 20:22:15 +07:00
|
|
|
spin_lock(&tbl->slot_tbl_lock);
|
2013-05-21 01:13:50 +07:00
|
|
|
if (test_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state) &&
|
2012-10-30 06:02:20 +07:00
|
|
|
!args->sa_privileged) {
|
2011-11-10 01:58:26 +07:00
|
|
|
/* The state manager will wait until the slot table is empty */
|
|
|
|
dprintk("%s session is draining\n", __func__);
|
2012-11-02 02:19:46 +07:00
|
|
|
goto out_sleep;
|
2009-12-15 12:27:56 +07:00
|
|
|
}
|
|
|
|
|
2012-11-17 04:10:11 +07:00
|
|
|
slot = nfs4_alloc_slot(tbl);
|
2012-11-23 01:21:02 +07:00
|
|
|
if (IS_ERR(slot)) {
|
|
|
|
/* If out of memory, try again in 1/4 second */
|
|
|
|
if (slot == ERR_PTR(-ENOMEM))
|
|
|
|
task->tk_timeout = HZ >> 2;
|
2009-04-01 20:22:15 +07:00
|
|
|
dprintk("<-- %s: no free slots\n", __func__);
|
2012-11-02 02:19:46 +07:00
|
|
|
goto out_sleep;
|
2009-04-01 20:22:15 +07:00
|
|
|
}
|
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
|
|
|
|
2012-11-17 00:58:36 +07:00
|
|
|
args->sa_slot = slot;
|
2009-04-01 20:22:15 +07:00
|
|
|
|
2012-11-17 04:10:11 +07:00
|
|
|
dprintk("<-- %s slotid=%d seqid=%d\n", __func__,
|
|
|
|
slot->slot_nr, slot->seq_nr);
|
2009-04-01 20:22:15 +07:00
|
|
|
|
2010-09-24 20:17:01 +07:00
|
|
|
res->sr_slot = slot;
|
2012-12-16 03:21:52 +07:00
|
|
|
res->sr_timestamp = jiffies;
|
2010-06-16 20:52:25 +07:00
|
|
|
res->sr_status_flags = 0;
|
2009-04-01 20:22:15 +07:00
|
|
|
/*
|
|
|
|
* sr_status is only set in decode_sequence, and so will remain
|
|
|
|
* set to 1 if an rpc level failure occurs.
|
|
|
|
*/
|
|
|
|
res->sr_status = 1;
|
2012-10-23 07:28:44 +07:00
|
|
|
out_success:
|
|
|
|
rpc_call_start(task);
|
2009-04-01 20:22:13 +07:00
|
|
|
return 0;
|
2012-11-02 02:19:46 +07:00
|
|
|
out_sleep:
|
2012-10-30 06:02:20 +07:00
|
|
|
/* Privileged tasks are queued with top priority */
|
|
|
|
if (args->sa_privileged)
|
2012-11-02 03:44:05 +07:00
|
|
|
rpc_sleep_on_priority(&tbl->slot_tbl_waitq, task,
|
|
|
|
NULL, RPC_PRIORITY_PRIVILEGED);
|
|
|
|
else
|
|
|
|
rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
|
2012-11-02 02:19:46 +07:00
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
|
|
|
return -EAGAIN;
|
2009-04-01 20:22:13 +07:00
|
|
|
}
|
2011-03-01 08:34:19 +07:00
|
|
|
EXPORT_SYMBOL_GPL(nfs41_setup_sequence);
|
2009-04-01 20:22:13 +07:00
|
|
|
|
2010-06-16 20:52:26 +07:00
|
|
|
int nfs4_setup_sequence(const struct nfs_server *server,
|
2009-04-01 20:22:13 +07:00
|
|
|
struct nfs4_sequence_args *args,
|
|
|
|
struct nfs4_sequence_res *res,
|
|
|
|
struct rpc_task *task)
|
|
|
|
{
|
2010-06-16 20:52:26 +07:00
|
|
|
struct nfs4_session *session = nfs4_get_session(server);
|
2009-04-01 20:22:13 +07:00
|
|
|
int ret = 0;
|
|
|
|
|
2012-10-23 07:28:44 +07:00
|
|
|
if (session == NULL) {
|
|
|
|
rpc_call_start(task);
|
2010-06-16 20:52:26 +07:00
|
|
|
goto out;
|
2012-10-23 07:28:44 +07:00
|
|
|
}
|
2010-06-16 20:52:26 +07:00
|
|
|
|
2012-11-17 00:45:06 +07:00
|
|
|
dprintk("--> %s clp %p session %p sr_slot %d\n",
|
2010-09-24 20:17:01 +07:00
|
|
|
__func__, session->clp, session, res->sr_slot ?
|
2012-11-17 00:45:06 +07:00
|
|
|
res->sr_slot->slot_nr : -1);
|
2009-04-01 20:22:13 +07:00
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
ret = nfs41_setup_sequence(session, args, res, task);
|
2009-04-01 20:22:13 +07:00
|
|
|
out:
|
|
|
|
dprintk("<-- %s status=%d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nfs41_call_sync_data {
|
2010-06-16 20:52:26 +07:00
|
|
|
const struct nfs_server *seq_server;
|
2009-04-01 20:22:13 +07:00
|
|
|
struct nfs4_sequence_args *seq_args;
|
|
|
|
struct nfs4_sequence_res *seq_res;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs41_call_sync_data *data = calldata;
|
2012-10-23 07:07:20 +07:00
|
|
|
struct nfs4_session *session = nfs4_get_session(data->seq_server);
|
2009-04-01 20:22:13 +07:00
|
|
|
|
2010-06-16 20:52:26 +07:00
|
|
|
dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server);
|
|
|
|
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs41_setup_sequence(session, data->seq_args, data->seq_res, task);
|
2009-04-01 20:22:13 +07:00
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:19 +07:00
|
|
|
static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs41_call_sync_data *data = calldata;
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
nfs41_sequence_done(task, data->seq_res);
|
2009-04-01 20:22:19 +07:00
|
|
|
}
|
|
|
|
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct rpc_call_ops nfs41_call_sync_ops = {
|
2009-04-01 20:22:13 +07:00
|
|
|
.rpc_call_prepare = nfs41_call_sync_prepare,
|
2009-04-01 20:22:19 +07:00
|
|
|
.rpc_call_done = nfs41_call_sync_done,
|
2009-04-01 20:22:13 +07:00
|
|
|
};
|
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
|
|
|
|
struct nfs_server *server,
|
2009-04-01 20:22:13 +07:00
|
|
|
struct rpc_message *msg,
|
|
|
|
struct nfs4_sequence_args *args,
|
2012-10-30 06:02:20 +07:00
|
|
|
struct nfs4_sequence_res *res)
|
2009-04-01 20:22:13 +07:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct rpc_task *task;
|
|
|
|
struct nfs41_call_sync_data data = {
|
2010-06-16 20:52:26 +07:00
|
|
|
.seq_server = server,
|
2009-04-01 20:22:13 +07:00
|
|
|
.seq_args = args,
|
|
|
|
.seq_res = res,
|
|
|
|
};
|
|
|
|
struct rpc_task_setup task_setup = {
|
2011-03-25 00:12:24 +07:00
|
|
|
.rpc_client = clnt,
|
2009-04-01 20:22:13 +07:00
|
|
|
.rpc_message = msg,
|
|
|
|
.callback_ops = &nfs41_call_sync_ops,
|
|
|
|
.callback_data = &data
|
|
|
|
};
|
|
|
|
|
|
|
|
task = rpc_run_task(&task_setup);
|
|
|
|
if (IS_ERR(task))
|
|
|
|
ret = PTR_ERR(task);
|
|
|
|
else {
|
|
|
|
ret = task->tk_status;
|
|
|
|
rpc_put_task(task);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-16 20:52:26 +07:00
|
|
|
#else
|
2012-10-30 06:02:20 +07:00
|
|
|
static
|
2012-01-18 10:04:25 +07:00
|
|
|
void nfs41_init_sequence(struct nfs4_sequence_args *args,
|
|
|
|
struct nfs4_sequence_res *res, int cache_reply)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-10-30 06:02:20 +07:00
|
|
|
static void nfs4_set_sequence_privileged(struct nfs4_sequence_args *args)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
static int nfs4_sequence_done(struct rpc_task *task,
|
|
|
|
struct nfs4_sequence_res *res)
|
2010-06-16 20:52:26 +07:00
|
|
|
{
|
2010-08-01 01:29:06 +07:00
|
|
|
return 1;
|
2010-06-16 20:52:26 +07:00
|
|
|
}
|
2009-04-01 20:22:03 +07:00
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
2012-11-02 01:43:38 +07:00
|
|
|
static
|
2011-03-25 00:12:24 +07:00
|
|
|
int _nfs4_call_sync(struct rpc_clnt *clnt,
|
|
|
|
struct nfs_server *server,
|
2009-04-01 20:22:03 +07:00
|
|
|
struct rpc_message *msg,
|
|
|
|
struct nfs4_sequence_args *args,
|
2012-11-02 01:43:38 +07:00
|
|
|
struct nfs4_sequence_res *res)
|
2009-04-01 20:22:03 +07:00
|
|
|
{
|
2011-03-25 00:12:24 +07:00
|
|
|
return rpc_call_sync(clnt, msg, 0);
|
2009-04-01 20:22:03 +07:00
|
|
|
}
|
|
|
|
|
2012-11-02 01:43:38 +07:00
|
|
|
static
|
2011-03-25 00:12:24 +07:00
|
|
|
int nfs4_call_sync(struct rpc_clnt *clnt,
|
|
|
|
struct nfs_server *server,
|
2011-03-25 00:12:23 +07:00
|
|
|
struct rpc_message *msg,
|
|
|
|
struct nfs4_sequence_args *args,
|
|
|
|
struct nfs4_sequence_res *res,
|
|
|
|
int cache_reply)
|
|
|
|
{
|
2012-11-02 01:43:38 +07:00
|
|
|
nfs41_init_sequence(args, res, cache_reply);
|
2011-03-25 00:12:24 +07:00
|
|
|
return server->nfs_client->cl_mvops->call_sync(clnt, server, msg,
|
2012-11-02 01:43:38 +07:00
|
|
|
args, res);
|
2011-03-25 00:12:23 +07:00
|
|
|
}
|
2009-04-01 20:22:03 +07:00
|
|
|
|
2006-05-25 12:40:57 +07:00
|
|
|
static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-05-25 12:40:57 +07:00
|
|
|
struct nfs_inode *nfsi = NFS_I(dir);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-05-25 12:40:57 +07:00
|
|
|
spin_lock(&dir->i_lock);
|
2012-05-28 21:01:34 +07:00
|
|
|
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
|
2011-10-18 06:08:46 +07:00
|
|
|
if (!cinfo->atomic || cinfo->before != dir->i_version)
|
2007-10-16 05:18:29 +07:00
|
|
|
nfs_force_lookup_revalidate(dir);
|
2011-10-18 06:08:46 +07:00
|
|
|
dir->i_version = cinfo->after;
|
2012-12-21 04:52:38 +07:00
|
|
|
nfs_fscache_invalidate(dir);
|
2006-05-25 12:40:57 +07:00
|
|
|
spin_unlock(&dir->i_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:08 +07:00
|
|
|
struct nfs4_opendata {
|
2007-06-18 03:02:44 +07:00
|
|
|
struct kref kref;
|
2006-01-03 15:55:08 +07:00
|
|
|
struct nfs_openargs o_arg;
|
|
|
|
struct nfs_openres o_res;
|
2006-01-03 15:55:12 +07:00
|
|
|
struct nfs_open_confirmargs c_arg;
|
|
|
|
struct nfs_open_confirmres c_res;
|
2012-01-08 01:22:46 +07:00
|
|
|
struct nfs4_string owner_name;
|
|
|
|
struct nfs4_string group_name;
|
2006-01-03 15:55:08 +07:00
|
|
|
struct nfs_fattr f_attr;
|
|
|
|
struct dentry *dir;
|
2011-06-23 05:30:55 +07:00
|
|
|
struct dentry *dentry;
|
2006-01-03 15:55:08 +07:00
|
|
|
struct nfs4_state_owner *owner;
|
2007-07-06 06:02:21 +07:00
|
|
|
struct nfs4_state *state;
|
2006-01-03 15:55:08 +07:00
|
|
|
struct iattr attrs;
|
2006-01-03 15:55:21 +07:00
|
|
|
unsigned long timestamp;
|
2007-07-08 00:19:59 +07:00
|
|
|
unsigned int rpc_done : 1;
|
2013-04-24 01:52:44 +07:00
|
|
|
unsigned int is_recover : 1;
|
2006-01-03 15:55:11 +07:00
|
|
|
int rpc_status;
|
|
|
|
int cancelled;
|
2006-01-03 15:55:08 +07:00
|
|
|
};
|
|
|
|
|
2013-03-16 03:44:28 +07:00
|
|
|
static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
|
|
|
|
int err, struct nfs4_exception *exception)
|
|
|
|
{
|
|
|
|
if (err != -EINVAL)
|
|
|
|
return false;
|
|
|
|
if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
|
|
|
|
return false;
|
|
|
|
server->caps &= ~NFS_CAP_ATOMIC_OPEN_V1;
|
|
|
|
exception->retry = 1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum open_claim_type4
|
|
|
|
nfs4_map_atomic_open_claim(struct nfs_server *server,
|
|
|
|
enum open_claim_type4 claim)
|
|
|
|
{
|
|
|
|
if (server->caps & NFS_CAP_ATOMIC_OPEN_V1)
|
|
|
|
return claim;
|
|
|
|
switch (claim) {
|
|
|
|
default:
|
|
|
|
return claim;
|
|
|
|
case NFS4_OPEN_CLAIM_FH:
|
|
|
|
return NFS4_OPEN_CLAIM_NULL;
|
|
|
|
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
|
|
|
return NFS4_OPEN_CLAIM_DELEGATE_CUR;
|
|
|
|
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
|
|
|
return NFS4_OPEN_CLAIM_DELEGATE_PREV;
|
|
|
|
}
|
|
|
|
}
|
2007-07-04 10:48:13 +07:00
|
|
|
|
|
|
|
static void nfs4_init_opendata_res(struct nfs4_opendata *p)
|
|
|
|
{
|
|
|
|
p->o_res.f_attr = &p->f_attr;
|
2008-04-08 00:20:54 +07:00
|
|
|
p->o_res.seqid = p->o_arg.seqid;
|
|
|
|
p->c_res.seqid = p->c_arg.seqid;
|
2007-07-04 10:48:13 +07:00
|
|
|
p->o_res.server = p->o_arg.server;
|
2012-10-03 13:39:34 +07:00
|
|
|
p->o_res.access_request = p->o_arg.access;
|
2007-07-04 10:48:13 +07:00
|
|
|
nfs_fattr_init(&p->f_attr);
|
2012-01-08 01:22:46 +07:00
|
|
|
nfs_fattr_init_names(&p->f_attr, &p->owner_name, &p->group_name);
|
2007-07-04 10:48:13 +07:00
|
|
|
}
|
|
|
|
|
2011-06-23 05:30:55 +07:00
|
|
|
static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
|
2008-12-24 03:21:56 +07:00
|
|
|
struct nfs4_state_owner *sp, fmode_t fmode, int flags,
|
2010-05-13 23:51:01 +07:00
|
|
|
const struct iattr *attrs,
|
2013-03-16 01:57:33 +07:00
|
|
|
enum open_claim_type4 claim,
|
2010-05-13 23:51:01 +07:00
|
|
|
gfp_t gfp_mask)
|
2006-01-03 15:55:08 +07:00
|
|
|
{
|
2011-06-23 05:30:55 +07:00
|
|
|
struct dentry *parent = dget_parent(dentry);
|
2006-01-03 15:55:08 +07:00
|
|
|
struct inode *dir = parent->d_inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
|
|
struct nfs4_opendata *p;
|
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
p = kzalloc(sizeof(*p), gfp_mask);
|
2006-01-03 15:55:08 +07:00
|
|
|
if (p == NULL)
|
|
|
|
goto err;
|
2010-05-13 23:51:01 +07:00
|
|
|
p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
|
2006-01-03 15:55:08 +07:00
|
|
|
if (p->o_arg.seqid == NULL)
|
|
|
|
goto err_free;
|
2011-06-23 05:30:55 +07:00
|
|
|
nfs_sb_active(dentry->d_sb);
|
|
|
|
p->dentry = dget(dentry);
|
2006-01-03 15:55:08 +07:00
|
|
|
p->dir = parent;
|
|
|
|
p->owner = sp;
|
|
|
|
atomic_inc(&sp->so_count);
|
2008-12-24 03:21:56 +07:00
|
|
|
p->o_arg.open_flags = flags;
|
|
|
|
p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
|
2012-10-03 04:49:52 +07:00
|
|
|
/* don't put an ACCESS op in OPEN compound if O_EXCL, because ACCESS
|
|
|
|
* will return permission denied for all bits until close */
|
|
|
|
if (!(flags & O_EXCL)) {
|
|
|
|
/* ask server to check for all possible rights as results
|
|
|
|
* are cached */
|
|
|
|
p->o_arg.access = NFS4_ACCESS_READ | NFS4_ACCESS_MODIFY |
|
|
|
|
NFS4_ACCESS_EXTEND | NFS4_ACCESS_EXECUTE;
|
|
|
|
}
|
2006-08-23 07:06:09 +07:00
|
|
|
p->o_arg.clientid = server->nfs_client->cl_clientid;
|
2012-04-21 06:24:51 +07:00
|
|
|
p->o_arg.id.create_time = ktime_to_ns(sp->so_seqid.create_time);
|
|
|
|
p->o_arg.id.uniquifier = sp->so_seqid.owner_id;
|
2011-06-23 05:30:55 +07:00
|
|
|
p->o_arg.name = &dentry->d_name;
|
2006-01-03 15:55:08 +07:00
|
|
|
p->o_arg.server = server;
|
|
|
|
p->o_arg.bitmask = server->attr_bitmask;
|
2012-06-05 20:16:47 +07:00
|
|
|
p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
|
2013-03-16 03:44:28 +07:00
|
|
|
p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
|
|
|
|
switch (p->o_arg.claim) {
|
2013-03-16 01:57:33 +07:00
|
|
|
case NFS4_OPEN_CLAIM_NULL:
|
|
|
|
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
|
|
|
|
case NFS4_OPEN_CLAIM_DELEGATE_PREV:
|
|
|
|
p->o_arg.fh = NFS_FH(dir);
|
|
|
|
break;
|
|
|
|
case NFS4_OPEN_CLAIM_PREVIOUS:
|
|
|
|
case NFS4_OPEN_CLAIM_FH:
|
|
|
|
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
|
|
|
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
|
|
|
p->o_arg.fh = NFS_FH(dentry->d_inode);
|
|
|
|
}
|
2012-01-18 10:04:26 +07:00
|
|
|
if (attrs != NULL && attrs->ia_valid != 0) {
|
2012-03-03 05:14:31 +07:00
|
|
|
__be32 verf[2];
|
2010-06-16 20:52:27 +07:00
|
|
|
|
2006-01-03 15:55:08 +07:00
|
|
|
p->o_arg.u.attrs = &p->attrs;
|
|
|
|
memcpy(&p->attrs, attrs, sizeof(p->attrs));
|
2012-03-03 05:14:31 +07:00
|
|
|
|
|
|
|
verf[0] = jiffies;
|
|
|
|
verf[1] = current->pid;
|
|
|
|
memcpy(p->o_arg.u.verifier.data, verf,
|
|
|
|
sizeof(p->o_arg.u.verifier.data));
|
2006-01-03 15:55:08 +07:00
|
|
|
}
|
2006-01-03 15:55:12 +07:00
|
|
|
p->c_arg.fh = &p->o_res.fh;
|
|
|
|
p->c_arg.stateid = &p->o_res.stateid;
|
|
|
|
p->c_arg.seqid = p->o_arg.seqid;
|
2007-07-04 10:48:13 +07:00
|
|
|
nfs4_init_opendata_res(p);
|
2007-06-18 03:02:44 +07:00
|
|
|
kref_init(&p->kref);
|
2006-01-03 15:55:08 +07:00
|
|
|
return p;
|
|
|
|
err_free:
|
|
|
|
kfree(p);
|
|
|
|
err:
|
|
|
|
dput(parent);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-06-18 03:02:44 +07:00
|
|
|
static void nfs4_opendata_free(struct kref *kref)
|
2006-01-03 15:55:08 +07:00
|
|
|
{
|
2007-06-18 03:02:44 +07:00
|
|
|
struct nfs4_opendata *p = container_of(kref,
|
|
|
|
struct nfs4_opendata, kref);
|
2011-06-23 05:30:55 +07:00
|
|
|
struct super_block *sb = p->dentry->d_sb;
|
2007-06-18 03:02:44 +07:00
|
|
|
|
|
|
|
nfs_free_seqid(p->o_arg.seqid);
|
2007-07-06 06:02:21 +07:00
|
|
|
if (p->state != NULL)
|
|
|
|
nfs4_put_open_state(p->state);
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_put_state_owner(p->owner);
|
|
|
|
dput(p->dir);
|
2011-06-23 05:30:55 +07:00
|
|
|
dput(p->dentry);
|
|
|
|
nfs_sb_deactive(sb);
|
2012-01-08 01:22:46 +07:00
|
|
|
nfs_fattr_free_names(&p->f_attr);
|
2007-06-18 03:02:44 +07:00
|
|
|
kfree(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_opendata_put(struct nfs4_opendata *p)
|
|
|
|
{
|
|
|
|
if (p != NULL)
|
|
|
|
kref_put(&p->kref, nfs4_opendata_free);
|
2006-01-03 15:55:08 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:07 +07:00
|
|
|
static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = rpc_wait_for_completion_task(task);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
|
2007-07-09 01:11:36 +07:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2008-12-24 03:21:56 +07:00
|
|
|
|
2012-01-18 10:04:26 +07:00
|
|
|
if (open_mode & (O_EXCL|O_TRUNC))
|
2008-12-24 03:21:56 +07:00
|
|
|
goto out;
|
|
|
|
switch (mode & (FMODE_READ|FMODE_WRITE)) {
|
2007-07-09 01:11:36 +07:00
|
|
|
case FMODE_READ:
|
2009-12-08 20:33:16 +07:00
|
|
|
ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0
|
|
|
|
&& state->n_rdonly != 0;
|
2007-07-09 01:11:36 +07:00
|
|
|
break;
|
|
|
|
case FMODE_WRITE:
|
2009-12-08 20:33:16 +07:00
|
|
|
ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0
|
|
|
|
&& state->n_wronly != 0;
|
2007-07-09 01:11:36 +07:00
|
|
|
break;
|
|
|
|
case FMODE_READ|FMODE_WRITE:
|
2009-12-08 20:33:16 +07:00
|
|
|
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0
|
|
|
|
&& state->n_rdwr != 0;
|
2007-07-09 01:11:36 +07:00
|
|
|
}
|
2008-12-24 03:21:56 +07:00
|
|
|
out:
|
2007-07-09 01:11:36 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
|
2007-07-06 06:02:21 +07:00
|
|
|
{
|
2011-12-10 07:05:58 +07:00
|
|
|
if (delegation == NULL)
|
|
|
|
return 0;
|
2008-12-24 03:21:56 +07:00
|
|
|
if ((delegation->type & fmode) != fmode)
|
2007-07-06 06:02:21 +07:00
|
|
|
return 0;
|
2008-12-24 03:21:39 +07:00
|
|
|
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
|
2007-07-06 06:02:21 +07:00
|
|
|
return 0;
|
2013-02-05 23:43:28 +07:00
|
|
|
if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
|
|
|
|
return 0;
|
2008-12-24 03:21:52 +07:00
|
|
|
nfs_mark_delegation_referenced(delegation);
|
2007-07-06 06:02:21 +07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
|
2006-01-03 15:55:13 +07:00
|
|
|
{
|
2008-12-24 03:21:56 +07:00
|
|
|
switch (fmode) {
|
2006-01-03 15:55:13 +07:00
|
|
|
case FMODE_WRITE:
|
|
|
|
state->n_wronly++;
|
|
|
|
break;
|
|
|
|
case FMODE_READ:
|
|
|
|
state->n_rdonly++;
|
|
|
|
break;
|
|
|
|
case FMODE_READ|FMODE_WRITE:
|
|
|
|
state->n_rdwr++;
|
|
|
|
}
|
2008-12-24 03:21:56 +07:00
|
|
|
nfs4_state_set_mode_locked(state, state->state | fmode);
|
2007-07-06 05:07:55 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
|
2007-07-06 05:07:55 +07:00
|
|
|
{
|
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&state->stateid, stateid);
|
|
|
|
nfs4_stateid_copy(&state->open_stateid, stateid);
|
2013-04-20 12:25:45 +07:00
|
|
|
set_bit(NFS_OPEN_STATE, &state->flags);
|
2008-12-24 03:21:56 +07:00
|
|
|
switch (fmode) {
|
2007-07-06 05:07:55 +07:00
|
|
|
case FMODE_READ:
|
|
|
|
set_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
break;
|
|
|
|
case FMODE_WRITE:
|
|
|
|
set_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
break;
|
|
|
|
case FMODE_READ|FMODE_WRITE:
|
|
|
|
set_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
}
|
2006-01-03 15:55:13 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
|
2007-07-06 05:07:55 +07:00
|
|
|
{
|
2007-07-09 21:45:42 +07:00
|
|
|
write_seqlock(&state->seqlock);
|
2008-12-24 03:21:56 +07:00
|
|
|
nfs_set_open_stateid_locked(state, stateid, fmode);
|
2007-07-09 21:45:42 +07:00
|
|
|
write_sequnlock(&state->seqlock);
|
2007-07-06 05:07:55 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-09 21:45:42 +07:00
|
|
|
/*
|
|
|
|
* Protect the call to nfs4_state_set_mode_locked and
|
|
|
|
* serialise the stateid update
|
|
|
|
*/
|
|
|
|
write_seqlock(&state->seqlock);
|
2007-07-06 05:07:55 +07:00
|
|
|
if (deleg_stateid != NULL) {
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&state->stateid, deleg_stateid);
|
2007-07-06 05:07:55 +07:00
|
|
|
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
}
|
|
|
|
if (open_stateid != NULL)
|
2008-12-24 03:21:56 +07:00
|
|
|
nfs_set_open_stateid_locked(state, open_stateid, fmode);
|
2007-07-09 21:45:42 +07:00
|
|
|
write_sequnlock(&state->seqlock);
|
|
|
|
spin_lock(&state->owner->so_lock);
|
2008-12-24 03:21:56 +07:00
|
|
|
update_open_stateflags(state, fmode);
|
2005-10-21 04:22:47 +07:00
|
|
|
spin_unlock(&state->owner->so_lock);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
|
2008-12-24 03:21:38 +07:00
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(state->inode);
|
|
|
|
struct nfs_delegation *deleg_cur;
|
|
|
|
int ret = 0;
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
fmode &= (FMODE_READ|FMODE_WRITE);
|
2008-12-24 03:21:38 +07:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
deleg_cur = rcu_dereference(nfsi->delegation);
|
|
|
|
if (deleg_cur == NULL)
|
|
|
|
goto no_delegation;
|
|
|
|
|
|
|
|
spin_lock(&deleg_cur->lock);
|
|
|
|
if (nfsi->delegation != deleg_cur ||
|
2013-02-05 23:43:28 +07:00
|
|
|
test_bit(NFS_DELEGATION_RETURNING, &deleg_cur->flags) ||
|
2008-12-24 03:21:56 +07:00
|
|
|
(deleg_cur->type & fmode) != fmode)
|
2008-12-24 03:21:38 +07:00
|
|
|
goto no_delegation_unlock;
|
|
|
|
|
|
|
|
if (delegation == NULL)
|
|
|
|
delegation = &deleg_cur->stateid;
|
2012-03-05 06:13:56 +07:00
|
|
|
else if (!nfs4_stateid_match(&deleg_cur->stateid, delegation))
|
2008-12-24 03:21:38 +07:00
|
|
|
goto no_delegation_unlock;
|
|
|
|
|
2008-12-24 03:21:52 +07:00
|
|
|
nfs_mark_delegation_referenced(deleg_cur);
|
2008-12-24 03:21:56 +07:00
|
|
|
__update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
|
2008-12-24 03:21:38 +07:00
|
|
|
ret = 1;
|
|
|
|
no_delegation_unlock:
|
|
|
|
spin_unlock(&deleg_cur->lock);
|
|
|
|
no_delegation:
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
if (!ret && open_stateid != NULL) {
|
2008-12-24 03:21:56 +07:00
|
|
|
__update_open_stateid(state, open_stateid, NULL, fmode);
|
2008-12-24 03:21:38 +07:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
|
2007-07-06 06:02:21 +07:00
|
|
|
{
|
|
|
|
struct nfs_delegation *delegation;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
delegation = rcu_dereference(NFS_I(inode)->delegation);
|
2008-12-24 03:21:56 +07:00
|
|
|
if (delegation == NULL || (delegation->type & fmode) == fmode) {
|
2007-07-06 06:02:21 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2012-06-21 02:53:44 +07:00
|
|
|
nfs4_inode_return_delegation(inode);
|
2007-07-06 06:02:21 +07:00
|
|
|
}
|
|
|
|
|
2007-07-09 01:11:36 +07:00
|
|
|
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
|
2007-07-06 06:02:21 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state *state = opendata->state;
|
|
|
|
struct nfs_inode *nfsi = NFS_I(state->inode);
|
|
|
|
struct nfs_delegation *delegation;
|
2013-05-30 02:36:40 +07:00
|
|
|
int open_mode = opendata->o_arg.open_flags;
|
2008-12-24 03:21:56 +07:00
|
|
|
fmode_t fmode = opendata->o_arg.fmode;
|
2007-07-06 06:02:21 +07:00
|
|
|
nfs4_stateid stateid;
|
|
|
|
int ret = -EAGAIN;
|
|
|
|
|
|
|
|
for (;;) {
|
2008-12-24 03:21:56 +07:00
|
|
|
if (can_open_cached(state, fmode, open_mode)) {
|
2007-07-09 01:11:36 +07:00
|
|
|
spin_lock(&state->owner->so_lock);
|
2008-12-24 03:21:56 +07:00
|
|
|
if (can_open_cached(state, fmode, open_mode)) {
|
|
|
|
update_open_stateflags(state, fmode);
|
2007-07-09 01:11:36 +07:00
|
|
|
spin_unlock(&state->owner->so_lock);
|
|
|
|
goto out_return_state;
|
|
|
|
}
|
|
|
|
spin_unlock(&state->owner->so_lock);
|
|
|
|
}
|
2008-12-24 03:21:38 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
delegation = rcu_dereference(nfsi->delegation);
|
2011-12-10 07:05:58 +07:00
|
|
|
if (!can_open_delegated(delegation, fmode)) {
|
2008-12-24 03:21:38 +07:00
|
|
|
rcu_read_unlock();
|
2007-07-06 06:02:21 +07:00
|
|
|
break;
|
2008-12-24 03:21:38 +07:00
|
|
|
}
|
2007-07-06 06:02:21 +07:00
|
|
|
/* Save the delegation */
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&stateid, &delegation->stateid);
|
2007-07-06 06:02:21 +07:00
|
|
|
rcu_read_unlock();
|
2013-04-09 23:56:52 +07:00
|
|
|
nfs_release_seqid(opendata->o_arg.seqid);
|
2013-04-24 01:52:44 +07:00
|
|
|
if (!opendata->is_recover) {
|
|
|
|
ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2007-07-06 06:02:21 +07:00
|
|
|
ret = -EAGAIN;
|
2008-12-24 03:21:38 +07:00
|
|
|
|
|
|
|
/* Try to update the stateid using the delegation */
|
2008-12-24 03:21:56 +07:00
|
|
|
if (update_open_stateid(state, NULL, &stateid, fmode))
|
2008-12-24 03:21:38 +07:00
|
|
|
goto out_return_state;
|
2007-07-06 06:02:21 +07:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
out_return_state:
|
|
|
|
atomic_inc(&state->count);
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2012-10-03 08:07:32 +07:00
|
|
|
static void
|
|
|
|
nfs4_opendata_check_deleg(struct nfs4_opendata *data, struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct nfs_client *clp = NFS_SERVER(state->inode)->nfs_client;
|
|
|
|
struct nfs_delegation *delegation;
|
|
|
|
int delegation_flags = 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
|
|
|
|
if (delegation)
|
|
|
|
delegation_flags = delegation->flags;
|
|
|
|
rcu_read_unlock();
|
|
|
|
if (data->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR) {
|
|
|
|
pr_err_ratelimited("NFS: Broken NFSv4 server %s is "
|
|
|
|
"returning a delegation for "
|
|
|
|
"OPEN(CLAIM_DELEGATE_CUR)\n",
|
|
|
|
clp->cl_hostname);
|
|
|
|
} else if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
|
|
|
|
nfs_inode_set_delegation(state->inode,
|
|
|
|
data->owner->so_cred,
|
|
|
|
&data->o_res);
|
|
|
|
else
|
|
|
|
nfs_inode_reclaim_delegation(state->inode,
|
|
|
|
data->owner->so_cred,
|
|
|
|
&data->o_res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the inode attributes against the CLAIM_PREVIOUS returned attributes
|
|
|
|
* and update the nfs4_state.
|
|
|
|
*/
|
|
|
|
static struct nfs4_state *
|
|
|
|
_nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
|
|
|
|
{
|
|
|
|
struct inode *inode = data->state->inode;
|
|
|
|
struct nfs4_state *state = data->state;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!data->rpc_done) {
|
|
|
|
ret = data->rpc_status;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -ESTALE;
|
|
|
|
if (!(data->f_attr.valid & NFS_ATTR_FATTR_TYPE) ||
|
|
|
|
!(data->f_attr.valid & NFS_ATTR_FATTR_FILEID) ||
|
|
|
|
!(data->f_attr.valid & NFS_ATTR_FATTR_CHANGE))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
state = nfs4_get_open_state(inode, data->owner);
|
|
|
|
if (state == NULL)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
ret = nfs_refresh_inode(inode, &data->f_attr);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (data->o_res.delegation_type != 0)
|
|
|
|
nfs4_opendata_check_deleg(data, state);
|
|
|
|
update_open_stateid(state, &data->o_res.stateid, NULL,
|
|
|
|
data->o_arg.fmode);
|
|
|
|
|
|
|
|
return state;
|
|
|
|
err:
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nfs4_state *
|
|
|
|
_nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
|
2006-01-03 15:55:11 +07:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct nfs4_state *state = NULL;
|
2007-07-07 19:04:47 +07:00
|
|
|
int ret;
|
2006-01-03 15:55:11 +07:00
|
|
|
|
2007-07-06 06:02:21 +07:00
|
|
|
if (!data->rpc_done) {
|
2007-07-09 01:11:36 +07:00
|
|
|
state = nfs4_try_open_cached(data);
|
2007-07-06 06:02:21 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2007-07-07 19:04:47 +07:00
|
|
|
ret = -EAGAIN;
|
2006-01-03 15:55:11 +07:00
|
|
|
if (!(data->f_attr.valid & NFS_ATTR_FATTR))
|
2007-07-07 19:04:47 +07:00
|
|
|
goto err;
|
2006-01-03 15:55:11 +07:00
|
|
|
inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
|
2007-07-07 19:04:47 +07:00
|
|
|
ret = PTR_ERR(inode);
|
2006-03-21 01:44:48 +07:00
|
|
|
if (IS_ERR(inode))
|
2007-07-07 19:04:47 +07:00
|
|
|
goto err;
|
|
|
|
ret = -ENOMEM;
|
2006-01-03 15:55:11 +07:00
|
|
|
state = nfs4_get_open_state(inode, data->owner);
|
|
|
|
if (state == NULL)
|
2007-07-07 19:04:47 +07:00
|
|
|
goto err_put_inode;
|
2012-10-03 08:07:32 +07:00
|
|
|
if (data->o_res.delegation_type != 0)
|
|
|
|
nfs4_opendata_check_deleg(data, state);
|
2008-12-24 03:21:38 +07:00
|
|
|
update_open_stateid(state, &data->o_res.stateid, NULL,
|
2008-12-24 03:21:56 +07:00
|
|
|
data->o_arg.fmode);
|
2006-01-03 15:55:11 +07:00
|
|
|
iput(inode);
|
2007-07-06 06:02:21 +07:00
|
|
|
out:
|
2013-03-01 07:19:59 +07:00
|
|
|
nfs_release_seqid(data->o_arg.seqid);
|
2006-01-03 15:55:11 +07:00
|
|
|
return state;
|
2007-07-07 19:04:47 +07:00
|
|
|
err_put_inode:
|
|
|
|
iput(inode);
|
|
|
|
err:
|
|
|
|
return ERR_PTR(ret);
|
2006-01-03 15:55:11 +07:00
|
|
|
}
|
|
|
|
|
2012-10-03 08:07:32 +07:00
|
|
|
static struct nfs4_state *
|
|
|
|
nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
|
|
|
|
{
|
|
|
|
if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
|
|
|
|
return _nfs4_opendata_reclaim_to_nfs4_state(data);
|
|
|
|
return _nfs4_opendata_to_nfs4_state(data);
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:15 +07:00
|
|
|
static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(state->inode);
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
|
|
|
|
spin_lock(&state->inode->i_lock);
|
|
|
|
list_for_each_entry(ctx, &nfsi->open_files, list) {
|
|
|
|
if (ctx->state != state)
|
|
|
|
continue;
|
|
|
|
get_nfs_open_context(ctx);
|
|
|
|
spin_unlock(&state->inode->i_lock);
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
spin_unlock(&state->inode->i_lock);
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
}
|
|
|
|
|
2013-03-16 01:57:33 +07:00
|
|
|
static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx,
|
|
|
|
struct nfs4_state *state, enum open_claim_type4 claim)
|
2007-07-18 08:50:45 +07:00
|
|
|
{
|
|
|
|
struct nfs4_opendata *opendata;
|
|
|
|
|
2013-03-16 01:57:33 +07:00
|
|
|
opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
|
|
|
|
NULL, claim, GFP_NOFS);
|
2007-07-18 08:50:45 +07:00
|
|
|
if (opendata == NULL)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
opendata->state = state;
|
|
|
|
atomic_inc(&state->count);
|
|
|
|
return opendata;
|
|
|
|
}
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
|
2006-01-03 15:55:15 +07:00
|
|
|
{
|
2007-07-04 10:48:13 +07:00
|
|
|
struct nfs4_state *newstate;
|
2006-01-03 15:55:15 +07:00
|
|
|
int ret;
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
opendata->o_arg.open_flags = 0;
|
|
|
|
opendata->o_arg.fmode = fmode;
|
2007-07-04 10:48:13 +07:00
|
|
|
memset(&opendata->o_res, 0, sizeof(opendata->o_res));
|
|
|
|
memset(&opendata->c_res, 0, sizeof(opendata->c_res));
|
|
|
|
nfs4_init_opendata_res(opendata);
|
2009-12-15 12:27:57 +07:00
|
|
|
ret = _nfs4_recover_proc_open(opendata);
|
2006-01-03 15:55:15 +07:00
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
2007-07-04 10:48:13 +07:00
|
|
|
newstate = nfs4_opendata_to_nfs4_state(opendata);
|
2007-07-07 19:04:47 +07:00
|
|
|
if (IS_ERR(newstate))
|
|
|
|
return PTR_ERR(newstate);
|
2011-06-23 05:20:23 +07:00
|
|
|
nfs4_close_state(newstate, fmode);
|
2007-07-04 10:48:13 +07:00
|
|
|
*res = newstate;
|
2006-01-03 15:55:15 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct nfs4_state *newstate;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* memory barrier prior to reading state->n_* */
|
2007-07-04 10:48:13 +07:00
|
|
|
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
2013-04-22 22:29:51 +07:00
|
|
|
clear_bit(NFS_OPEN_STATE, &state->flags);
|
2006-01-03 15:55:15 +07:00
|
|
|
smp_rmb();
|
|
|
|
if (state->n_rdwr != 0) {
|
2010-10-05 04:59:08 +07:00
|
|
|
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
2007-07-04 10:48:13 +07:00
|
|
|
ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate);
|
2006-01-03 15:55:15 +07:00
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
2007-07-04 10:48:13 +07:00
|
|
|
if (newstate != state)
|
|
|
|
return -ESTALE;
|
2006-01-03 15:55:15 +07:00
|
|
|
}
|
|
|
|
if (state->n_wronly != 0) {
|
2010-10-05 04:59:08 +07:00
|
|
|
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
2007-07-04 10:48:13 +07:00
|
|
|
ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate);
|
2006-01-03 15:55:15 +07:00
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
2007-07-04 10:48:13 +07:00
|
|
|
if (newstate != state)
|
|
|
|
return -ESTALE;
|
2006-01-03 15:55:15 +07:00
|
|
|
}
|
|
|
|
if (state->n_rdonly != 0) {
|
2010-10-05 04:59:08 +07:00
|
|
|
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
2007-07-04 10:48:13 +07:00
|
|
|
ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate);
|
2006-01-03 15:55:15 +07:00
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
2007-07-04 10:48:13 +07:00
|
|
|
if (newstate != state)
|
|
|
|
return -ESTALE;
|
2006-01-03 15:55:15 +07:00
|
|
|
}
|
2007-07-09 08:04:15 +07:00
|
|
|
/*
|
|
|
|
* We may have performed cached opens for all three recoveries.
|
|
|
|
* Check if we need to update the current stateid.
|
|
|
|
*/
|
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0 &&
|
2012-03-05 06:13:56 +07:00
|
|
|
!nfs4_stateid_match(&state->stateid, &state->open_stateid)) {
|
2007-07-09 21:45:42 +07:00
|
|
|
write_seqlock(&state->seqlock);
|
2007-07-09 08:04:15 +07:00
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&state->stateid, &state->open_stateid);
|
2007-07-09 21:45:42 +07:00
|
|
|
write_sequnlock(&state->seqlock);
|
2007-07-09 08:04:15 +07:00
|
|
|
}
|
2006-01-03 15:55:15 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* OPEN_RECLAIM:
|
|
|
|
* reclaim state on the server after a reboot.
|
|
|
|
*/
|
2007-06-05 22:46:42 +07:00
|
|
|
static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-09 08:04:15 +07:00
|
|
|
struct nfs_delegation *delegation;
|
2006-01-03 15:55:15 +07:00
|
|
|
struct nfs4_opendata *opendata;
|
2008-12-24 03:21:56 +07:00
|
|
|
fmode_t delegation_type = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
int status;
|
|
|
|
|
2013-03-16 01:57:33 +07:00
|
|
|
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
|
|
|
NFS4_OPEN_CLAIM_PREVIOUS);
|
2007-07-18 08:50:45 +07:00
|
|
|
if (IS_ERR(opendata))
|
|
|
|
return PTR_ERR(opendata);
|
2007-07-09 08:04:15 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
|
2008-12-24 03:21:39 +07:00
|
|
|
if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
|
2007-08-27 20:57:46 +07:00
|
|
|
delegation_type = delegation->type;
|
2007-07-09 08:04:15 +07:00
|
|
|
rcu_read_unlock();
|
2006-01-03 15:55:15 +07:00
|
|
|
opendata->o_arg.u.delegation_type = delegation_type;
|
|
|
|
status = nfs4_open_recover(opendata, state);
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_opendata_put(opendata);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2007-06-05 22:46:42 +07:00
|
|
|
static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
2007-06-05 22:46:42 +07:00
|
|
|
err = _nfs4_do_open_reclaim(ctx, state);
|
2013-03-16 03:44:28 +07:00
|
|
|
if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
|
|
|
|
continue;
|
2010-10-20 06:47:49 +07:00
|
|
|
if (err != -NFS4ERR_DELAY)
|
2005-06-23 00:16:29 +07:00
|
|
|
break;
|
|
|
|
nfs4_handle_exception(server, err, &exception);
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:15 +07:00
|
|
|
static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = nfs4_state_find_open_context(state);
|
|
|
|
if (IS_ERR(ctx))
|
2013-03-29 01:01:33 +07:00
|
|
|
return -EAGAIN;
|
2007-06-05 22:46:42 +07:00
|
|
|
ret = nfs4_do_open_reclaim(ctx, state);
|
2006-01-03 15:55:15 +07:00
|
|
|
put_nfs_open_context(ctx);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-04-02 02:56:46 +07:00
|
|
|
static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, int err)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-04-02 02:40:44 +07:00
|
|
|
switch (err) {
|
|
|
|
default:
|
|
|
|
printk(KERN_ERR "NFS: %s: unhandled error "
|
|
|
|
"%d.\n", __func__, err);
|
|
|
|
case 0:
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESTALE:
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
case -NFS4ERR_BAD_HIGH_SLOT:
|
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
|
|
|
case -NFS4ERR_DEADSESSION:
|
|
|
|
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
|
|
|
|
return -EAGAIN;
|
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
/* Don't recall a delegation if it was lost */
|
|
|
|
nfs4_schedule_lease_recovery(server->nfs_client);
|
|
|
|
return -EAGAIN;
|
|
|
|
case -NFS4ERR_DELEG_REVOKED:
|
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
2013-04-02 02:56:46 +07:00
|
|
|
case -NFS4ERR_OPENMODE:
|
2013-04-02 02:40:44 +07:00
|
|
|
nfs_inode_find_state_and_recover(state->inode,
|
|
|
|
stateid);
|
|
|
|
nfs4_schedule_stateid_recovery(server, state);
|
|
|
|
return 0;
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
case -NFS4ERR_GRACE:
|
|
|
|
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
ssleep(1);
|
|
|
|
return -EAGAIN;
|
2013-04-02 02:56:46 +07:00
|
|
|
case -ENOMEM:
|
|
|
|
case -NFS4ERR_DENIED:
|
|
|
|
/* kill_proc(fl->fl_pid, SIGLOST, 1); */
|
|
|
|
return 0;
|
2013-04-02 02:40:44 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-04-02 02:56:46 +07:00
|
|
|
int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
|
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
|
|
struct nfs4_opendata *opendata;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
|
|
|
NFS4_OPEN_CLAIM_DELEG_CUR_FH);
|
|
|
|
if (IS_ERR(opendata))
|
|
|
|
return PTR_ERR(opendata);
|
|
|
|
nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
|
|
|
|
err = nfs4_open_recover(opendata, state);
|
|
|
|
nfs4_opendata_put(opendata);
|
|
|
|
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:12 +07:00
|
|
|
static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_opendata *data = calldata;
|
|
|
|
|
|
|
|
data->rpc_status = task->tk_status;
|
2006-01-03 15:55:21 +07:00
|
|
|
if (data->rpc_status == 0) {
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&data->o_res.stateid, &data->c_res.stateid);
|
2008-01-03 03:19:18 +07:00
|
|
|
nfs_confirm_seqid(&data->owner->so_seqid, 0);
|
2006-01-03 15:55:21 +07:00
|
|
|
renew_lease(data->o_res.server, data->timestamp);
|
2007-07-08 00:19:59 +07:00
|
|
|
data->rpc_done = 1;
|
2006-01-03 15:55:21 +07:00
|
|
|
}
|
2006-01-03 15:55:12 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_open_confirm_release(void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_opendata *data = calldata;
|
|
|
|
struct nfs4_state *state = NULL;
|
|
|
|
|
|
|
|
/* If this request hasn't been cancelled, do nothing */
|
|
|
|
if (data->cancelled == 0)
|
|
|
|
goto out_free;
|
|
|
|
/* In case of error, no cleanup! */
|
2007-07-08 00:19:59 +07:00
|
|
|
if (!data->rpc_done)
|
2006-01-03 15:55:12 +07:00
|
|
|
goto out_free;
|
|
|
|
state = nfs4_opendata_to_nfs4_state(data);
|
2007-07-07 19:04:47 +07:00
|
|
|
if (!IS_ERR(state))
|
2011-06-23 05:20:23 +07:00
|
|
|
nfs4_close_state(state, data->o_arg.fmode);
|
2006-01-03 15:55:12 +07:00
|
|
|
out_free:
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_opendata_put(data);
|
2006-01-03 15:55:12 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_open_confirm_ops = {
|
|
|
|
.rpc_call_done = nfs4_open_confirm_done,
|
|
|
|
.rpc_release = nfs4_open_confirm_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: On error, nfs4_proc_open_confirm will free the struct nfs4_opendata
|
|
|
|
*/
|
|
|
|
static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
|
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(data->dir->d_inode);
|
|
|
|
struct rpc_task *task;
|
2007-07-15 02:40:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
|
|
|
|
.rpc_argp = &data->c_arg,
|
|
|
|
.rpc_resp = &data->c_res,
|
|
|
|
.rpc_cred = data->owner->so_cred,
|
|
|
|
};
|
2007-07-15 02:39:59 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = server->client,
|
2007-07-15 02:40:01 +07:00
|
|
|
.rpc_message = &msg,
|
2007-07-15 02:39:59 +07:00
|
|
|
.callback_ops = &nfs4_open_confirm_ops,
|
|
|
|
.callback_data = data,
|
2008-02-20 08:04:23 +07:00
|
|
|
.workqueue = nfsiod_workqueue,
|
2007-07-15 02:39:59 +07:00
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
int status;
|
|
|
|
|
2007-06-18 03:02:44 +07:00
|
|
|
kref_get(&data->kref);
|
2007-07-08 00:19:59 +07:00
|
|
|
data->rpc_done = 0;
|
|
|
|
data->rpc_status = 0;
|
2007-07-15 02:40:01 +07:00
|
|
|
data->timestamp = jiffies;
|
2007-07-15 02:39:59 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
2006-03-21 06:11:10 +07:00
|
|
|
if (IS_ERR(task))
|
2006-01-03 15:55:12 +07:00
|
|
|
return PTR_ERR(task);
|
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
|
|
|
if (status != 0) {
|
|
|
|
data->cancelled = 1;
|
|
|
|
smp_wmb();
|
|
|
|
} else
|
|
|
|
status = data->rpc_status;
|
2006-11-12 10:18:03 +07:00
|
|
|
rpc_put_task(task);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:11 +07:00
|
|
|
static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-03 15:55:11 +07:00
|
|
|
struct nfs4_opendata *data = calldata;
|
|
|
|
struct nfs4_state_owner *sp = data->owner;
|
2013-04-17 05:42:34 +07:00
|
|
|
struct nfs_client *clp = sp->so_server->nfs_client;
|
2007-07-15 02:40:01 +07:00
|
|
|
|
2006-01-03 15:55:11 +07:00
|
|
|
if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_wait;
|
2007-07-06 06:02:21 +07:00
|
|
|
/*
|
|
|
|
* Check if we still need to send an OPEN call, or if we can use
|
|
|
|
* a delegation instead.
|
|
|
|
*/
|
|
|
|
if (data->state != NULL) {
|
|
|
|
struct nfs_delegation *delegation;
|
|
|
|
|
2008-12-24 03:21:56 +07:00
|
|
|
if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
|
2007-07-09 01:11:36 +07:00
|
|
|
goto out_no_action;
|
2007-07-06 06:02:21 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
|
2011-12-10 07:05:58 +07:00
|
|
|
if (data->o_arg.claim != NFS4_OPEN_CLAIM_DELEGATE_CUR &&
|
2013-04-24 01:46:25 +07:00
|
|
|
data->o_arg.claim != NFS4_OPEN_CLAIM_DELEG_CUR_FH &&
|
2011-12-10 07:05:58 +07:00
|
|
|
can_open_delegated(delegation, data->o_arg.fmode))
|
|
|
|
goto unlock_no_action;
|
2007-07-06 06:02:21 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2012-04-21 06:24:51 +07:00
|
|
|
/* Update client id. */
|
2013-04-17 05:42:34 +07:00
|
|
|
data->o_arg.clientid = clp->cl_clientid;
|
2013-04-24 01:31:19 +07:00
|
|
|
switch (data->o_arg.claim) {
|
|
|
|
case NFS4_OPEN_CLAIM_PREVIOUS:
|
|
|
|
case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
|
|
|
|
case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
|
2012-10-03 08:07:32 +07:00
|
|
|
data->o_arg.open_bitmap = &nfs4_open_noattr_bitmap[0];
|
2013-04-24 01:31:19 +07:00
|
|
|
case NFS4_OPEN_CLAIM_FH:
|
|
|
|
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
|
2007-07-18 08:50:45 +07:00
|
|
|
nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
|
|
|
|
}
|
2006-01-03 15:55:21 +07:00
|
|
|
data->timestamp = jiffies;
|
2010-06-16 20:52:26 +07:00
|
|
|
if (nfs4_setup_sequence(data->o_arg.server,
|
2009-04-01 20:22:21 +07:00
|
|
|
&data->o_arg.seq_args,
|
2012-10-30 05:37:40 +07:00
|
|
|
&data->o_res.seq_res,
|
|
|
|
task) != 0)
|
|
|
|
nfs_release_seqid(data->o_arg.seqid);
|
2013-04-17 05:42:34 +07:00
|
|
|
|
|
|
|
/* Set the create mode (note dependency on the session type) */
|
|
|
|
data->o_arg.createmode = NFS4_CREATE_UNCHECKED;
|
|
|
|
if (data->o_arg.open_flags & O_EXCL) {
|
|
|
|
data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE;
|
|
|
|
if (nfs4_has_persistent_session(clp))
|
|
|
|
data->o_arg.createmode = NFS4_CREATE_GUARDED;
|
|
|
|
else if (clp->cl_mvops->minor_version > 0)
|
|
|
|
data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE4_1;
|
|
|
|
}
|
2007-07-09 01:11:36 +07:00
|
|
|
return;
|
2011-12-10 07:05:58 +07:00
|
|
|
unlock_no_action:
|
|
|
|
rcu_read_unlock();
|
2007-07-09 01:11:36 +07:00
|
|
|
out_no_action:
|
|
|
|
task->tk_action = NULL;
|
2013-02-12 07:01:21 +07:00
|
|
|
out_wait:
|
2012-11-30 05:27:47 +07:00
|
|
|
nfs4_sequence_done(task, &data->o_res.seq_res);
|
2009-12-15 12:27:57 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:11 +07:00
|
|
|
static void nfs4_open_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_opendata *data = calldata;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-03 15:55:11 +07:00
|
|
|
data->rpc_status = task->tk_status;
|
2009-04-01 20:22:21 +07:00
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs4_sequence_done(task, &data->o_res.seq_res))
|
|
|
|
return;
|
2009-04-01 20:22:21 +07:00
|
|
|
|
2006-01-03 15:55:11 +07:00
|
|
|
if (task->tk_status == 0) {
|
2012-10-03 07:09:00 +07:00
|
|
|
if (data->o_res.f_attr->valid & NFS_ATTR_FATTR_TYPE) {
|
|
|
|
switch (data->o_res.f_attr->mode & S_IFMT) {
|
2005-10-19 04:20:18 +07:00
|
|
|
case S_IFREG:
|
|
|
|
break;
|
|
|
|
case S_IFLNK:
|
2006-01-03 15:55:11 +07:00
|
|
|
data->rpc_status = -ELOOP;
|
2005-10-19 04:20:18 +07:00
|
|
|
break;
|
|
|
|
case S_IFDIR:
|
2006-01-03 15:55:11 +07:00
|
|
|
data->rpc_status = -EISDIR;
|
2005-10-19 04:20:18 +07:00
|
|
|
break;
|
|
|
|
default:
|
2006-01-03 15:55:11 +07:00
|
|
|
data->rpc_status = -ENOTDIR;
|
2012-10-03 07:09:00 +07:00
|
|
|
}
|
2005-10-19 04:20:18 +07:00
|
|
|
}
|
2006-01-03 15:55:21 +07:00
|
|
|
renew_lease(data->o_res.server, data->timestamp);
|
2007-07-09 03:19:56 +07:00
|
|
|
if (!(data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM))
|
|
|
|
nfs_confirm_seqid(&data->owner->so_seqid, 0);
|
2005-10-19 04:20:18 +07:00
|
|
|
}
|
2007-07-08 00:19:59 +07:00
|
|
|
data->rpc_done = 1;
|
2006-01-03 15:55:11 +07:00
|
|
|
}
|
2005-10-19 04:20:18 +07:00
|
|
|
|
2006-01-03 15:55:11 +07:00
|
|
|
static void nfs4_open_release(void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_opendata *data = calldata;
|
|
|
|
struct nfs4_state *state = NULL;
|
|
|
|
|
|
|
|
/* If this request hasn't been cancelled, do nothing */
|
|
|
|
if (data->cancelled == 0)
|
|
|
|
goto out_free;
|
|
|
|
/* In case of error, no cleanup! */
|
2007-07-08 00:19:59 +07:00
|
|
|
if (data->rpc_status != 0 || !data->rpc_done)
|
2006-01-03 15:55:11 +07:00
|
|
|
goto out_free;
|
|
|
|
/* In case we need an open_confirm, no cleanup! */
|
|
|
|
if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM)
|
|
|
|
goto out_free;
|
|
|
|
state = nfs4_opendata_to_nfs4_state(data);
|
2007-07-07 19:04:47 +07:00
|
|
|
if (!IS_ERR(state))
|
2011-06-23 05:20:23 +07:00
|
|
|
nfs4_close_state(state, data->o_arg.fmode);
|
2006-01-03 15:55:11 +07:00
|
|
|
out_free:
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_opendata_put(data);
|
2006-01-03 15:55:11 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_open_ops = {
|
|
|
|
.rpc_call_prepare = nfs4_open_prepare,
|
|
|
|
.rpc_call_done = nfs4_open_done,
|
|
|
|
.rpc_release = nfs4_open_release,
|
|
|
|
};
|
|
|
|
|
2009-12-15 12:27:57 +07:00
|
|
|
static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
|
2006-01-03 15:55:11 +07:00
|
|
|
{
|
|
|
|
struct inode *dir = data->dir->d_inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
|
|
struct nfs_openargs *o_arg = &data->o_arg;
|
|
|
|
struct nfs_openres *o_res = &data->o_res;
|
|
|
|
struct rpc_task *task;
|
2007-07-15 02:40:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN],
|
|
|
|
.rpc_argp = o_arg,
|
|
|
|
.rpc_resp = o_res,
|
|
|
|
.rpc_cred = data->owner->so_cred,
|
|
|
|
};
|
2007-07-15 02:39:59 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = server->client,
|
2007-07-15 02:40:01 +07:00
|
|
|
.rpc_message = &msg,
|
2007-07-15 02:39:59 +07:00
|
|
|
.callback_ops = &nfs4_open_ops,
|
|
|
|
.callback_data = data,
|
2008-02-20 08:04:23 +07:00
|
|
|
.workqueue = nfsiod_workqueue,
|
2007-07-15 02:39:59 +07:00
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2006-01-03 15:55:11 +07:00
|
|
|
int status;
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&o_arg->seq_args, &o_res->seq_res, 1);
|
2007-06-18 03:02:44 +07:00
|
|
|
kref_get(&data->kref);
|
2007-07-08 00:19:59 +07:00
|
|
|
data->rpc_done = 0;
|
|
|
|
data->rpc_status = 0;
|
2007-07-04 10:48:13 +07:00
|
|
|
data->cancelled = 0;
|
2013-04-24 01:52:44 +07:00
|
|
|
data->is_recover = 0;
|
|
|
|
if (isrecover) {
|
2012-10-30 06:02:20 +07:00
|
|
|
nfs4_set_sequence_privileged(&o_arg->seq_args);
|
2013-04-24 01:52:44 +07:00
|
|
|
data->is_recover = 1;
|
|
|
|
}
|
2007-07-15 02:39:59 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
2009-12-15 12:27:57 +07:00
|
|
|
if (IS_ERR(task))
|
|
|
|
return PTR_ERR(task);
|
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
|
|
|
if (status != 0) {
|
|
|
|
data->cancelled = 1;
|
|
|
|
smp_wmb();
|
|
|
|
} else
|
|
|
|
status = data->rpc_status;
|
|
|
|
rpc_put_task(task);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_recover_proc_open(struct nfs4_opendata *data)
|
|
|
|
{
|
|
|
|
struct inode *dir = data->dir->d_inode;
|
|
|
|
struct nfs_openres *o_res = &data->o_res;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nfs4_run_open_task(data, 1);
|
|
|
|
if (status != 0 || !data->rpc_done)
|
|
|
|
return status;
|
|
|
|
|
2012-01-08 01:22:46 +07:00
|
|
|
nfs_fattr_map_and_free_names(NFS_SERVER(dir), &data->f_attr);
|
|
|
|
|
2009-12-15 12:27:57 +07:00
|
|
|
if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
|
|
|
|
status = _nfs4_proc_open_confirm(data);
|
|
|
|
if (status != 0)
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-09-11 01:00:46 +07:00
|
|
|
static int nfs4_opendata_access(struct rpc_cred *cred,
|
|
|
|
struct nfs4_opendata *opendata,
|
2013-01-04 04:42:29 +07:00
|
|
|
struct nfs4_state *state, fmode_t fmode,
|
|
|
|
int openflags)
|
2012-09-11 01:00:46 +07:00
|
|
|
{
|
|
|
|
struct nfs_access_entry cache;
|
|
|
|
u32 mask;
|
|
|
|
|
|
|
|
/* access call failed or for some reason the server doesn't
|
|
|
|
* support any access modes -- defer access call until later */
|
|
|
|
if (opendata->o_res.access_supported == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mask = 0;
|
2012-10-03 04:49:51 +07:00
|
|
|
/* don't check MAY_WRITE - a newly created file may not have
|
2013-01-04 04:42:29 +07:00
|
|
|
* write mode bits, but POSIX allows the creating process to write.
|
|
|
|
* use openflags to check for exec, because fmode won't
|
|
|
|
* always have FMODE_EXEC set when file open for exec. */
|
|
|
|
if (openflags & __FMODE_EXEC) {
|
|
|
|
/* ONLY check for exec rights */
|
|
|
|
mask = MAY_EXEC;
|
|
|
|
} else if (fmode & FMODE_READ)
|
|
|
|
mask = MAY_READ;
|
2012-09-11 01:00:46 +07:00
|
|
|
|
|
|
|
cache.cred = cred;
|
|
|
|
cache.jiffies = jiffies;
|
|
|
|
nfs_access_set_mask(&cache, opendata->o_res.access_result);
|
|
|
|
nfs_access_add_cache(state->inode, &cache);
|
|
|
|
|
2012-10-03 04:49:51 +07:00
|
|
|
if ((mask & ~cache.mask & (MAY_READ | MAY_EXEC)) == 0)
|
2012-09-11 01:00:46 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* even though OPEN succeeded, access is denied. Close the file */
|
|
|
|
nfs4_close_state(state, fmode);
|
2012-11-03 05:00:56 +07:00
|
|
|
return -EACCES;
|
2012-09-11 01:00:46 +07:00
|
|
|
}
|
|
|
|
|
2009-12-15 12:27:57 +07:00
|
|
|
/*
|
|
|
|
* Note: On error, nfs4_proc_open will free the struct nfs4_opendata
|
|
|
|
*/
|
|
|
|
static int _nfs4_proc_open(struct nfs4_opendata *data)
|
|
|
|
{
|
|
|
|
struct inode *dir = data->dir->d_inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
|
|
struct nfs_openargs *o_arg = &data->o_arg;
|
|
|
|
struct nfs_openres *o_res = &data->o_res;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nfs4_run_open_task(data, 0);
|
2011-10-19 06:11:49 +07:00
|
|
|
if (!data->rpc_done)
|
|
|
|
return status;
|
|
|
|
if (status != 0) {
|
|
|
|
if (status == -NFS4ERR_BADNAME &&
|
|
|
|
!(o_arg->open_flags & O_CREAT))
|
|
|
|
return -ENOENT;
|
2006-01-03 15:55:11 +07:00
|
|
|
return status;
|
2011-10-19 06:11:49 +07:00
|
|
|
}
|
2006-01-03 15:55:11 +07:00
|
|
|
|
2012-01-08 01:22:46 +07:00
|
|
|
nfs_fattr_map_and_free_names(server, &data->f_attr);
|
|
|
|
|
2012-04-28 00:48:18 +07:00
|
|
|
if (o_arg->open_flags & O_CREAT)
|
2005-10-28 09:12:40 +07:00
|
|
|
update_changeattr(dir, &o_res->cinfo);
|
2010-04-12 03:48:44 +07:00
|
|
|
if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
|
|
|
|
server->caps &= ~NFS_CAP_POSIX_LOCK;
|
2005-04-17 05:20:36 +07:00
|
|
|
if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
|
2006-01-03 15:55:12 +07:00
|
|
|
status = _nfs4_proc_open_confirm(data);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (status != 0)
|
2006-01-03 15:55:11 +07:00
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
|
2007-07-18 08:52:41 +07:00
|
|
|
_nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr);
|
2006-01-03 15:55:11 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-03-01 08:34:17 +07:00
|
|
|
static int nfs4_recover_expired_lease(struct nfs_server *server)
|
|
|
|
{
|
|
|
|
return nfs4_client_recover_expired_lease(server->nfs_client);
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* OPEN_EXPIRED:
|
|
|
|
* reclaim state on the server after a network partition.
|
|
|
|
* Assumes caller holds the appropriate lock
|
|
|
|
*/
|
2007-06-05 22:46:42 +07:00
|
|
|
static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-03 15:55:08 +07:00
|
|
|
struct nfs4_opendata *opendata;
|
2006-01-03 15:55:15 +07:00
|
|
|
int ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-03-16 01:57:33 +07:00
|
|
|
opendata = nfs4_open_recoverdata_alloc(ctx, state,
|
2013-03-16 03:44:28 +07:00
|
|
|
NFS4_OPEN_CLAIM_FH);
|
2007-07-18 08:50:45 +07:00
|
|
|
if (IS_ERR(opendata))
|
|
|
|
return PTR_ERR(opendata);
|
2006-01-03 15:55:15 +07:00
|
|
|
ret = nfs4_open_recover(opendata, state);
|
2008-04-06 02:54:17 +07:00
|
|
|
if (ret == -ESTALE)
|
2011-06-23 05:40:12 +07:00
|
|
|
d_drop(ctx->dentry);
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_opendata_put(opendata);
|
2006-01-03 15:55:15 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-12-04 03:53:21 +07:00
|
|
|
static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state *state)
|
2005-06-23 00:16:29 +07:00
|
|
|
{
|
2007-06-05 22:46:42 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
2005-06-23 00:16:29 +07:00
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
2007-06-05 22:46:42 +07:00
|
|
|
err = _nfs4_open_expired(ctx, state);
|
2013-03-16 03:44:28 +07:00
|
|
|
if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
|
|
|
|
continue;
|
2009-12-04 03:53:21 +07:00
|
|
|
switch (err) {
|
|
|
|
default:
|
|
|
|
goto out;
|
|
|
|
case -NFS4ERR_GRACE:
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
nfs4_handle_exception(server, err, &exception);
|
|
|
|
err = 0;
|
|
|
|
}
|
2005-06-23 00:16:29 +07:00
|
|
|
} while (exception.retry);
|
2009-12-04 03:53:21 +07:00
|
|
|
out:
|
2005-06-23 00:16:29 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
2006-01-03 15:55:15 +07:00
|
|
|
int ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-01-03 15:55:15 +07:00
|
|
|
ctx = nfs4_state_find_open_context(state);
|
|
|
|
if (IS_ERR(ctx))
|
2013-03-29 01:01:33 +07:00
|
|
|
return -EAGAIN;
|
2007-06-05 22:46:42 +07:00
|
|
|
ret = nfs4_do_open_expired(ctx, state);
|
2006-01-03 15:55:15 +07:00
|
|
|
put_nfs_open_context(ctx);
|
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-06-03 01:59:10 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
2012-07-12 03:30:14 +07:00
|
|
|
static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
|
2011-06-03 01:59:10 +07:00
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
2012-07-12 03:30:14 +07:00
|
|
|
nfs4_stateid *stateid = &state->stateid;
|
2013-05-20 22:20:27 +07:00
|
|
|
struct nfs_delegation *delegation;
|
|
|
|
struct rpc_cred *cred = NULL;
|
|
|
|
int status = -NFS4ERR_BAD_STATEID;
|
2012-07-12 03:30:14 +07:00
|
|
|
|
|
|
|
/* If a state reset has been done, test_stateid is unneeded */
|
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
|
|
|
return;
|
|
|
|
|
2013-05-20 22:20:27 +07:00
|
|
|
/* Get the delegation credential for use by test/free_stateid */
|
|
|
|
rcu_read_lock();
|
|
|
|
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
|
|
|
|
if (delegation != NULL &&
|
|
|
|
nfs4_stateid_match(&delegation->stateid, stateid)) {
|
|
|
|
cred = get_rpccred(delegation->cred);
|
|
|
|
rcu_read_unlock();
|
|
|
|
status = nfs41_test_stateid(server, stateid, cred);
|
|
|
|
} else
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
2012-07-12 03:30:14 +07:00
|
|
|
if (status != NFS_OK) {
|
|
|
|
/* Free the stateid unless the server explicitly
|
|
|
|
* informs us the stateid is unrecognized. */
|
|
|
|
if (status != -NFS4ERR_BAD_STATEID)
|
2013-05-20 22:20:27 +07:00
|
|
|
nfs41_free_stateid(server, stateid, cred);
|
2012-09-27 02:25:52 +07:00
|
|
|
nfs_remove_bad_delegation(state->inode);
|
2012-07-12 03:30:14 +07:00
|
|
|
|
2012-09-27 02:25:52 +07:00
|
|
|
write_seqlock(&state->seqlock);
|
|
|
|
nfs4_stateid_copy(&state->stateid, &state->open_stateid);
|
|
|
|
write_sequnlock(&state->seqlock);
|
2012-07-12 03:30:14 +07:00
|
|
|
clear_bit(NFS_DELEGATED_STATE, &state->flags);
|
|
|
|
}
|
2013-05-20 22:20:27 +07:00
|
|
|
|
|
|
|
if (cred != NULL)
|
|
|
|
put_rpccred(cred);
|
2012-07-12 03:30:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs41_check_open_stateid - possibly free an open stateid
|
|
|
|
*
|
|
|
|
* @state: NFSv4 state for an inode
|
|
|
|
*
|
|
|
|
* Returns NFS_OK if recovery for this stateid is now finished.
|
|
|
|
* Otherwise a negative NFS4ERR value is returned.
|
|
|
|
*/
|
|
|
|
static int nfs41_check_open_stateid(struct nfs4_state *state)
|
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
2012-09-27 02:25:53 +07:00
|
|
|
nfs4_stateid *stateid = &state->open_stateid;
|
2013-05-20 22:20:27 +07:00
|
|
|
struct rpc_cred *cred = state->owner->so_cred;
|
2012-07-12 03:30:14 +07:00
|
|
|
int status;
|
|
|
|
|
|
|
|
/* If a state reset has been done, test_stateid is unneeded */
|
|
|
|
if ((test_bit(NFS_O_RDONLY_STATE, &state->flags) == 0) &&
|
|
|
|
(test_bit(NFS_O_WRONLY_STATE, &state->flags) == 0) &&
|
|
|
|
(test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
|
|
|
|
return -NFS4ERR_BAD_STATEID;
|
|
|
|
|
2013-05-20 22:20:27 +07:00
|
|
|
status = nfs41_test_stateid(server, stateid, cred);
|
2012-07-12 03:30:14 +07:00
|
|
|
if (status != NFS_OK) {
|
|
|
|
/* Free the stateid unless the server explicitly
|
|
|
|
* informs us the stateid is unrecognized. */
|
|
|
|
if (status != -NFS4ERR_BAD_STATEID)
|
2013-05-20 22:20:27 +07:00
|
|
|
nfs41_free_stateid(server, stateid, cred);
|
2012-07-12 03:30:14 +07:00
|
|
|
|
|
|
|
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
2013-04-22 22:29:51 +07:00
|
|
|
clear_bit(NFS_OPEN_STATE, &state->flags);
|
2012-01-31 22:39:30 +07:00
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
|
|
|
|
{
|
2012-07-12 03:30:05 +07:00
|
|
|
int status;
|
2012-01-31 22:39:30 +07:00
|
|
|
|
2012-07-12 03:30:14 +07:00
|
|
|
nfs41_clear_delegation_stateid(state);
|
|
|
|
status = nfs41_check_open_stateid(state);
|
2012-07-12 03:30:05 +07:00
|
|
|
if (status != NFS_OK)
|
|
|
|
status = nfs4_open_expired(sp, state);
|
|
|
|
return status;
|
2011-06-03 01:59:10 +07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-06-06 01:49:03 +07:00
|
|
|
/*
|
|
|
|
* on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-*
|
|
|
|
* fields corresponding to attributes that were used to store the verifier.
|
|
|
|
* Make sure we clobber those fields in the later setattr call
|
|
|
|
*/
|
|
|
|
static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct iattr *sattr)
|
|
|
|
{
|
|
|
|
if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_ACCESS) &&
|
|
|
|
!(sattr->ia_valid & ATTR_ATIME_SET))
|
|
|
|
sattr->ia_valid |= ATTR_ATIME;
|
|
|
|
|
|
|
|
if ((opendata->o_res.attrset[1] & FATTR4_WORD1_TIME_MODIFY) &&
|
|
|
|
!(sattr->ia_valid & ATTR_MTIME_SET))
|
|
|
|
sattr->ia_valid |= ATTR_MTIME;
|
|
|
|
}
|
|
|
|
|
2013-02-08 02:26:21 +07:00
|
|
|
static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
|
|
|
|
fmode_t fmode,
|
|
|
|
int flags,
|
2013-05-30 00:17:04 +07:00
|
|
|
struct nfs_open_context *ctx)
|
2013-02-08 02:26:21 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state_owner *sp = opendata->owner;
|
|
|
|
struct nfs_server *server = sp->so_server;
|
2013-05-30 00:11:28 +07:00
|
|
|
struct dentry *dentry;
|
2013-02-08 02:26:21 +07:00
|
|
|
struct nfs4_state *state;
|
|
|
|
unsigned int seq;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
|
|
|
|
|
|
|
|
ret = _nfs4_proc_open(opendata);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
state = nfs4_opendata_to_nfs4_state(opendata);
|
|
|
|
ret = PTR_ERR(state);
|
|
|
|
if (IS_ERR(state))
|
|
|
|
goto out;
|
|
|
|
if (server->caps & NFS_CAP_POSIX_LOCK)
|
|
|
|
set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
|
|
|
|
|
2013-05-30 00:11:28 +07:00
|
|
|
dentry = opendata->dentry;
|
|
|
|
if (dentry->d_inode == NULL) {
|
|
|
|
/* FIXME: Is this d_drop() ever needed? */
|
|
|
|
d_drop(dentry);
|
|
|
|
dentry = d_add_unique(dentry, igrab(state->inode));
|
|
|
|
if (dentry == NULL) {
|
|
|
|
dentry = opendata->dentry;
|
|
|
|
} else if (dentry != ctx->dentry) {
|
|
|
|
dput(ctx->dentry);
|
|
|
|
ctx->dentry = dget(dentry);
|
|
|
|
}
|
|
|
|
nfs_set_verifier(dentry,
|
|
|
|
nfs_save_change_attribute(opendata->dir->d_inode));
|
|
|
|
}
|
|
|
|
|
2013-02-08 02:26:21 +07:00
|
|
|
ret = nfs4_opendata_access(sp->so_cred, opendata, state, fmode, flags);
|
|
|
|
if (ret != 0)
|
|
|
|
goto out;
|
|
|
|
|
2013-05-30 00:17:04 +07:00
|
|
|
ctx->state = state;
|
2013-05-30 00:34:46 +07:00
|
|
|
if (dentry->d_inode == state->inode) {
|
|
|
|
nfs_inode_attach_open_context(ctx);
|
|
|
|
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
|
|
|
|
nfs4_schedule_stateid_recovery(server, state);
|
|
|
|
}
|
2013-02-08 02:26:21 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2006-01-03 15:55:11 +07:00
|
|
|
* Returns a referenced nfs4_state
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
2012-05-23 16:02:35 +07:00
|
|
|
static int _nfs4_do_open(struct inode *dir,
|
2013-05-29 23:37:49 +07:00
|
|
|
struct nfs_open_context *ctx,
|
2012-05-23 16:02:35 +07:00
|
|
|
int flags,
|
2013-05-30 00:17:04 +07:00
|
|
|
struct iattr *sattr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state_owner *sp;
|
|
|
|
struct nfs4_state *state = NULL;
|
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
2006-01-03 15:55:08 +07:00
|
|
|
struct nfs4_opendata *opendata;
|
2013-05-29 23:37:49 +07:00
|
|
|
struct dentry *dentry = ctx->dentry;
|
|
|
|
struct rpc_cred *cred = ctx->cred;
|
|
|
|
struct nfs4_threshold **ctx_th = &ctx->mdsthreshold;
|
|
|
|
fmode_t fmode = ctx->mode & (FMODE_READ|FMODE_WRITE|FMODE_EXEC);
|
2013-03-16 03:44:28 +07:00
|
|
|
enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
|
2007-07-06 06:02:21 +07:00
|
|
|
int status;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* Protect against reboot recovery conflicts */
|
|
|
|
status = -ENOMEM;
|
2012-01-18 10:04:24 +07:00
|
|
|
sp = nfs4_get_state_owner(server, cred, GFP_KERNEL);
|
|
|
|
if (sp == NULL) {
|
2005-04-17 05:20:36 +07:00
|
|
|
dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
|
|
|
|
goto out_err;
|
|
|
|
}
|
2006-01-03 15:55:24 +07:00
|
|
|
status = nfs4_recover_expired_lease(server);
|
|
|
|
if (status != 0)
|
2006-01-03 15:55:25 +07:00
|
|
|
goto err_put_state_owner;
|
2011-06-23 05:30:55 +07:00
|
|
|
if (dentry->d_inode != NULL)
|
|
|
|
nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
|
2006-01-03 15:55:24 +07:00
|
|
|
status = -ENOMEM;
|
2013-03-16 03:44:28 +07:00
|
|
|
if (dentry->d_inode)
|
|
|
|
claim = NFS4_OPEN_CLAIM_FH;
|
2013-03-16 01:57:33 +07:00
|
|
|
opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
|
2013-03-16 03:44:28 +07:00
|
|
|
claim, GFP_KERNEL);
|
2006-01-03 15:55:08 +07:00
|
|
|
if (opendata == NULL)
|
2008-12-24 03:21:45 +07:00
|
|
|
goto err_put_state_owner;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-05-23 16:02:35 +07:00
|
|
|
if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
|
|
|
|
opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
|
|
|
|
if (!opendata->f_attr.mdsthreshold)
|
|
|
|
goto err_opendata_put;
|
2012-06-05 20:16:47 +07:00
|
|
|
opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];
|
2012-05-23 16:02:35 +07:00
|
|
|
}
|
2011-06-23 05:30:55 +07:00
|
|
|
if (dentry->d_inode != NULL)
|
|
|
|
opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
|
2007-07-06 06:02:21 +07:00
|
|
|
|
2013-05-30 00:17:04 +07:00
|
|
|
status = _nfs4_open_and_get_state(opendata, fmode, flags, ctx);
|
2012-09-11 01:00:46 +07:00
|
|
|
if (status != 0)
|
|
|
|
goto err_opendata_put;
|
2013-05-30 00:17:04 +07:00
|
|
|
state = ctx->state;
|
2012-09-11 01:00:46 +07:00
|
|
|
|
2013-04-17 05:42:34 +07:00
|
|
|
if ((opendata->o_arg.open_flags & O_EXCL) &&
|
|
|
|
(opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
|
2010-04-17 03:22:51 +07:00
|
|
|
nfs4_exclusive_attrset(opendata, sattr);
|
|
|
|
|
|
|
|
nfs_fattr_init(opendata->o_res.f_attr);
|
|
|
|
status = nfs4_do_setattr(state->inode, cred,
|
|
|
|
opendata->o_res.f_attr, sattr,
|
|
|
|
state);
|
|
|
|
if (status == 0)
|
|
|
|
nfs_setattr_update_inode(state->inode, sattr);
|
|
|
|
nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
|
|
|
|
}
|
2012-05-23 16:02:35 +07:00
|
|
|
|
|
|
|
if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
|
|
|
|
*ctx_th = opendata->f_attr.mdsthreshold;
|
|
|
|
else
|
|
|
|
kfree(opendata->f_attr.mdsthreshold);
|
|
|
|
opendata->f_attr.mdsthreshold = NULL;
|
|
|
|
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_opendata_put(opendata);
|
2005-04-17 05:20:36 +07:00
|
|
|
nfs4_put_state_owner(sp);
|
|
|
|
return 0;
|
2007-06-18 03:02:44 +07:00
|
|
|
err_opendata_put:
|
2012-05-23 16:02:35 +07:00
|
|
|
kfree(opendata->f_attr.mdsthreshold);
|
2007-06-18 03:02:44 +07:00
|
|
|
nfs4_opendata_put(opendata);
|
2006-01-03 15:55:08 +07:00
|
|
|
err_put_state_owner:
|
|
|
|
nfs4_put_state_owner(sp);
|
2005-04-17 05:20:36 +07:00
|
|
|
out_err:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-23 16:02:35 +07:00
|
|
|
static struct nfs4_state *nfs4_do_open(struct inode *dir,
|
2013-05-29 23:37:49 +07:00
|
|
|
struct nfs_open_context *ctx,
|
2012-05-23 16:02:35 +07:00
|
|
|
int flags,
|
2013-05-29 23:37:49 +07:00
|
|
|
struct iattr *sattr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-03-16 03:44:28 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
struct nfs4_state *res;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
do {
|
2013-05-30 00:17:04 +07:00
|
|
|
status = _nfs4_do_open(dir, ctx, flags, sattr);
|
|
|
|
res = ctx->state;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (status == 0)
|
|
|
|
break;
|
|
|
|
/* NOTE: BAD_SEQID means the server and client disagree about the
|
|
|
|
* book-keeping w.r.t. state-changing operations
|
|
|
|
* (OPEN/CLOSE/LOCK/LOCKU...)
|
|
|
|
* It is actually a sign of a bug on the client or on the server.
|
|
|
|
*
|
|
|
|
* If we receive a BAD_SEQID error in the particular case of
|
NFSv4: Add functions to order RPC calls
NFSv4 file state-changing functions such as OPEN, CLOSE, LOCK,... are all
labelled with "sequence identifiers" in order to prevent the server from
reordering RPC requests, as this could cause its file state to
become out of sync with the client.
Currently the NFS client code enforces this ordering locally using
semaphores to restrict access to structures until the RPC call is done.
This, of course, only works with synchronous RPC calls, since the
user process must first grab the semaphore.
By dropping semaphores, and instead teaching the RPC engine to hold
the RPC calls until they are ready to be sent, we can extend this
process to work nicely with asynchronous RPC calls too.
This patch adds a new list called "rpc_sequence" that defines the order
of the RPC calls to be sent. We add one such list for each state_owner.
When an RPC call is ready to be sent, it checks if it is top of the
rpc_sequence list. If so, it proceeds. If not, it goes back to sleep,
and loops until it hits top of the list.
Once the RPC call has completed, it can then bump the sequence id counter,
and remove itself from the rpc_sequence list, and then wake up the next
sleeper.
Note that the state_owner sequence ids and lock_owner sequence ids are
all indexed to the same rpc_sequence list, so OPEN, LOCK,... requests
are all ordered w.r.t. each other.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2005-10-19 04:20:12 +07:00
|
|
|
* doing an OPEN, we assume that nfs_increment_open_seqid() will
|
2005-04-17 05:20:36 +07:00
|
|
|
* have unhashed the old state_owner for us, and that we can
|
|
|
|
* therefore safely retry using a new one. We should still warn
|
|
|
|
* the user though...
|
|
|
|
*/
|
|
|
|
if (status == -NFS4ERR_BAD_SEQID) {
|
2012-03-13 05:01:48 +07:00
|
|
|
pr_warn_ratelimited("NFS: v4 server %s "
|
2007-07-09 03:49:11 +07:00
|
|
|
" returned a bad sequence-id error!\n",
|
|
|
|
NFS_SERVER(dir)->nfs_client->cl_hostname);
|
2005-04-17 05:20:36 +07:00
|
|
|
exception.retry = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2005-10-19 04:20:21 +07:00
|
|
|
/*
|
|
|
|
* BAD_STATEID on OPEN means that the server cancelled our
|
|
|
|
* state before it received the OPEN_CONFIRM.
|
|
|
|
* Recover by retrying the request as per the discussion
|
|
|
|
* on Page 181 of RFC3530.
|
|
|
|
*/
|
|
|
|
if (status == -NFS4ERR_BAD_STATEID) {
|
|
|
|
exception.retry = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2007-07-06 06:02:21 +07:00
|
|
|
if (status == -EAGAIN) {
|
|
|
|
/* We must have found a delegation */
|
|
|
|
exception.retry = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2013-03-16 03:44:28 +07:00
|
|
|
if (nfs4_clear_cap_atomic_open_v1(server, status, &exception))
|
|
|
|
continue;
|
|
|
|
res = ERR_PTR(nfs4_handle_exception(server,
|
2005-04-17 05:20:36 +07:00
|
|
|
status, &exception));
|
|
|
|
} while (exception.retry);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2008-06-11 06:39:41 +07:00
|
|
|
static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
|
|
|
struct nfs_fattr *fattr, struct iattr *sattr,
|
|
|
|
struct nfs4_state *state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-03-21 01:44:46 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs_setattrargs arg = {
|
2006-03-21 01:44:46 +07:00
|
|
|
.fh = NFS_FH(inode),
|
2005-04-17 05:20:36 +07:00
|
|
|
.iap = sattr,
|
|
|
|
.server = server,
|
|
|
|
.bitmask = server->attr_bitmask,
|
|
|
|
};
|
|
|
|
struct nfs_setattrres res = {
|
|
|
|
.fattr = fattr,
|
|
|
|
.server = server,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
2008-06-11 06:39:41 +07:00
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
|
|
|
|
.rpc_argp = &arg,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
.rpc_cred = cred,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2006-01-03 15:55:21 +07:00
|
|
|
unsigned long timestamp = jiffies;
|
2013-04-29 21:35:36 +07:00
|
|
|
fmode_t fmode;
|
|
|
|
bool truncate;
|
2005-08-16 22:49:44 +07:00
|
|
|
int status;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(fattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-04-29 21:35:36 +07:00
|
|
|
/* Servers should only apply open mode checks for file size changes */
|
|
|
|
truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false;
|
|
|
|
fmode = truncate ? FMODE_WRITE : FMODE_READ;
|
|
|
|
|
|
|
|
if (nfs4_copy_delegation_stateid(&arg.stateid, inode, fmode)) {
|
|
|
|
/* Use that stateid */
|
|
|
|
} else if (truncate && state != NULL && nfs4_valid_open_stateid(state)) {
|
2012-08-14 05:54:45 +07:00
|
|
|
struct nfs_lockowner lockowner = {
|
|
|
|
.l_owner = current->files,
|
|
|
|
.l_pid = current->tgid,
|
|
|
|
};
|
2012-03-09 05:42:01 +07:00
|
|
|
nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE,
|
2012-08-14 05:54:45 +07:00
|
|
|
&lockowner);
|
2005-06-23 00:16:29 +07:00
|
|
|
} else
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&arg.stateid, &zero_stateid);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
2006-01-03 15:55:21 +07:00
|
|
|
if (status == 0 && state != NULL)
|
|
|
|
renew_lease(server, timestamp);
|
2005-08-16 22:49:44 +07:00
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-06-11 06:39:41 +07:00
|
|
|
static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
|
|
|
|
struct nfs_fattr *fattr, struct iattr *sattr,
|
|
|
|
struct nfs4_state *state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-03-21 01:44:46 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2012-03-06 07:56:44 +07:00
|
|
|
struct nfs4_exception exception = {
|
|
|
|
.state = state,
|
2012-03-08 04:39:06 +07:00
|
|
|
.inode = inode,
|
2012-03-06 07:56:44 +07:00
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
do {
|
2012-04-19 03:29:11 +07:00
|
|
|
err = _nfs4_do_setattr(inode, cred, fattr, sattr, state);
|
|
|
|
switch (err) {
|
|
|
|
case -NFS4ERR_OPENMODE:
|
2013-04-29 22:11:58 +07:00
|
|
|
if (!(sattr->ia_valid & ATTR_SIZE)) {
|
|
|
|
pr_warn_once("NFSv4: server %s is incorrectly "
|
|
|
|
"applying open mode checks to "
|
|
|
|
"a SETATTR that is not "
|
|
|
|
"changing file size.\n",
|
|
|
|
server->nfs_client->cl_hostname);
|
|
|
|
}
|
2012-04-19 03:29:11 +07:00
|
|
|
if (state && !(state->state & FMODE_WRITE)) {
|
|
|
|
err = -EBADF;
|
|
|
|
if (sattr->ia_valid & ATTR_OPEN)
|
|
|
|
err = -EACCES;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = nfs4_handle_exception(server, err, &exception);
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (exception.retry);
|
2012-04-19 03:29:11 +07:00
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nfs4_closedata {
|
|
|
|
struct inode *inode;
|
|
|
|
struct nfs4_state *state;
|
|
|
|
struct nfs_closeargs arg;
|
|
|
|
struct nfs_closeres res;
|
2005-10-28 09:12:41 +07:00
|
|
|
struct nfs_fattr fattr;
|
2006-01-03 15:55:21 +07:00
|
|
|
unsigned long timestamp;
|
2011-01-06 18:36:32 +07:00
|
|
|
bool roc;
|
|
|
|
u32 roc_barrier;
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2006-01-03 15:55:04 +07:00
|
|
|
static void nfs4_free_closedata(void *data)
|
2005-10-19 04:20:12 +07:00
|
|
|
{
|
2006-01-03 15:55:04 +07:00
|
|
|
struct nfs4_closedata *calldata = data;
|
|
|
|
struct nfs4_state_owner *sp = calldata->state->owner;
|
2011-06-23 05:20:23 +07:00
|
|
|
struct super_block *sb = calldata->state->inode->i_sb;
|
2005-10-19 04:20:12 +07:00
|
|
|
|
2011-01-06 18:36:32 +07:00
|
|
|
if (calldata->roc)
|
|
|
|
pnfs_roc_release(calldata->state->inode);
|
2005-10-19 04:20:12 +07:00
|
|
|
nfs4_put_open_state(calldata->state);
|
|
|
|
nfs_free_seqid(calldata->arg.seqid);
|
|
|
|
nfs4_put_state_owner(sp);
|
2013-01-12 04:39:51 +07:00
|
|
|
nfs_sb_deactive(sb);
|
2005-10-19 04:20:12 +07:00
|
|
|
kfree(calldata);
|
|
|
|
}
|
|
|
|
|
2009-12-08 20:33:16 +07:00
|
|
|
static void nfs4_close_clear_stateid_flags(struct nfs4_state *state,
|
|
|
|
fmode_t fmode)
|
|
|
|
{
|
|
|
|
spin_lock(&state->owner->so_lock);
|
2013-04-22 22:29:51 +07:00
|
|
|
clear_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
switch (fmode & (FMODE_READ|FMODE_WRITE)) {
|
|
|
|
case FMODE_WRITE:
|
2009-12-08 20:33:16 +07:00
|
|
|
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
2013-04-22 22:29:51 +07:00
|
|
|
break;
|
|
|
|
case FMODE_READ:
|
2009-12-08 20:33:16 +07:00
|
|
|
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
2013-04-22 22:29:51 +07:00
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
clear_bit(NFS_OPEN_STATE, &state->flags);
|
|
|
|
}
|
2009-12-08 20:33:16 +07:00
|
|
|
spin_unlock(&state->owner->so_lock);
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:04 +07:00
|
|
|
static void nfs4_close_done(struct rpc_task *task, void *data)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-03 15:55:04 +07:00
|
|
|
struct nfs4_closedata *calldata = data;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_state *state = calldata->state;
|
|
|
|
struct nfs_server *server = NFS_SERVER(calldata->inode);
|
|
|
|
|
2012-03-02 05:00:40 +07:00
|
|
|
dprintk("%s: begin!\n", __func__);
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs4_sequence_done(task, &calldata->res.seq_res))
|
|
|
|
return;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* hmm. we are done with the inode, and in the process of freeing
|
|
|
|
* the state_owner. we keep this around to process errors
|
|
|
|
*/
|
|
|
|
switch (task->tk_status) {
|
|
|
|
case 0:
|
2011-01-06 18:36:32 +07:00
|
|
|
if (calldata->roc)
|
|
|
|
pnfs_roc_set_barrier(state->inode,
|
|
|
|
calldata->roc_barrier);
|
2007-07-27 04:47:34 +07:00
|
|
|
nfs_set_open_stateid(state, &calldata->res.stateid, 0);
|
2006-01-03 15:55:21 +07:00
|
|
|
renew_lease(server, calldata->timestamp);
|
2009-12-08 20:33:16 +07:00
|
|
|
nfs4_close_clear_stateid_flags(state,
|
|
|
|
calldata->arg.fmode);
|
2005-04-17 05:20:36 +07:00
|
|
|
break;
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
2008-12-24 03:21:46 +07:00
|
|
|
case -NFS4ERR_OLD_STATEID:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_EXPIRED:
|
2008-12-24 03:21:56 +07:00
|
|
|
if (calldata->arg.fmode == 0)
|
2008-12-24 03:21:46 +07:00
|
|
|
break;
|
2005-04-17 05:20:36 +07:00
|
|
|
default:
|
2009-12-16 02:47:36 +07:00
|
|
|
if (nfs4_async_handle_error(task, server, state) == -EAGAIN)
|
|
|
|
rpc_restart_call_prepare(task);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-12-16 02:47:36 +07:00
|
|
|
nfs_release_seqid(calldata->arg.seqid);
|
2005-10-28 09:12:41 +07:00
|
|
|
nfs_refresh_inode(calldata->inode, calldata->res.fattr);
|
2012-03-02 05:00:40 +07:00
|
|
|
dprintk("%s: done, ret = %d!\n", __func__, task->tk_status);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:05 +07:00
|
|
|
static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-03 15:55:05 +07:00
|
|
|
struct nfs4_closedata *calldata = data;
|
2005-10-19 04:20:12 +07:00
|
|
|
struct nfs4_state *state = calldata->state;
|
2012-09-21 07:15:57 +07:00
|
|
|
struct inode *inode = calldata->inode;
|
2009-12-08 20:33:16 +07:00
|
|
|
int call_close = 0;
|
2005-10-19 04:20:12 +07:00
|
|
|
|
2012-03-02 05:00:40 +07:00
|
|
|
dprintk("%s: begin!\n", __func__);
|
2006-01-03 15:55:04 +07:00
|
|
|
if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_wait;
|
2007-07-06 05:07:55 +07:00
|
|
|
|
2009-12-08 20:33:16 +07:00
|
|
|
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
|
|
|
|
calldata->arg.fmode = FMODE_READ|FMODE_WRITE;
|
2005-11-05 03:32:58 +07:00
|
|
|
spin_lock(&state->owner->so_lock);
|
2007-07-06 05:07:55 +07:00
|
|
|
/* Calculate the change in open mode */
|
2006-01-03 15:55:13 +07:00
|
|
|
if (state->n_rdwr == 0) {
|
2007-07-06 05:07:55 +07:00
|
|
|
if (state->n_rdonly == 0) {
|
2009-12-08 20:33:16 +07:00
|
|
|
call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
|
|
|
|
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
calldata->arg.fmode &= ~FMODE_READ;
|
2007-07-06 05:07:55 +07:00
|
|
|
}
|
|
|
|
if (state->n_wronly == 0) {
|
2009-12-08 20:33:16 +07:00
|
|
|
call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
|
|
|
|
call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
|
|
|
|
calldata->arg.fmode &= ~FMODE_WRITE;
|
2007-07-06 05:07:55 +07:00
|
|
|
}
|
2006-01-03 15:55:13 +07:00
|
|
|
}
|
2013-03-15 03:57:48 +07:00
|
|
|
if (!nfs4_valid_open_stateid(state))
|
|
|
|
call_close = 0;
|
2005-11-05 03:32:58 +07:00
|
|
|
spin_unlock(&state->owner->so_lock);
|
2009-12-08 20:33:16 +07:00
|
|
|
|
|
|
|
if (!call_close) {
|
2006-01-03 15:55:04 +07:00
|
|
|
/* Note: exit _without_ calling nfs4_close_done */
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_no_action;
|
2005-10-19 04:20:12 +07:00
|
|
|
}
|
2009-12-08 20:33:16 +07:00
|
|
|
|
2011-01-06 18:36:32 +07:00
|
|
|
if (calldata->arg.fmode == 0) {
|
2009-12-08 20:33:16 +07:00
|
|
|
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
|
2011-01-06 18:36:32 +07:00
|
|
|
if (calldata->roc &&
|
2013-04-11 20:28:45 +07:00
|
|
|
pnfs_roc_drain(inode, &calldata->roc_barrier, task)) {
|
|
|
|
nfs_release_seqid(calldata->arg.seqid);
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_wait;
|
2013-04-11 20:28:45 +07:00
|
|
|
}
|
2011-01-06 18:36:32 +07:00
|
|
|
}
|
2009-12-08 20:33:16 +07:00
|
|
|
|
2005-10-28 09:12:41 +07:00
|
|
|
nfs_fattr_init(calldata->res.fattr);
|
2006-01-03 15:55:21 +07:00
|
|
|
calldata->timestamp = jiffies;
|
2012-09-21 07:15:57 +07:00
|
|
|
if (nfs4_setup_sequence(NFS_SERVER(inode),
|
2012-01-18 10:04:25 +07:00
|
|
|
&calldata->arg.seq_args,
|
|
|
|
&calldata->res.seq_res,
|
2012-10-30 05:37:40 +07:00
|
|
|
task) != 0)
|
|
|
|
nfs_release_seqid(calldata->arg.seqid);
|
2012-03-02 05:00:40 +07:00
|
|
|
dprintk("%s: done!\n", __func__);
|
2013-02-12 07:01:21 +07:00
|
|
|
return;
|
|
|
|
out_no_action:
|
|
|
|
task->tk_action = NULL;
|
|
|
|
out_wait:
|
|
|
|
nfs4_sequence_done(task, &calldata->res.seq_res);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:04 +07:00
|
|
|
static const struct rpc_call_ops nfs4_close_ops = {
|
2006-01-03 15:55:05 +07:00
|
|
|
.rpc_call_prepare = nfs4_close_prepare,
|
2006-01-03 15:55:04 +07:00
|
|
|
.rpc_call_done = nfs4_close_done,
|
|
|
|
.rpc_release = nfs4_free_closedata,
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* It is possible for data to be read/written from a mem-mapped file
|
|
|
|
* after the sys_close call (which hits the vfs layer as a flush).
|
|
|
|
* This means that we can't safely call nfsv4 close on a file until
|
|
|
|
* the inode is cleared. This in turn means that we are not good
|
|
|
|
* NFSv4 citizens - we do not indicate to the server to update the file's
|
|
|
|
* share state even when we are done with one of the three share
|
|
|
|
* stateid's in the inode.
|
|
|
|
*
|
|
|
|
* NOTE: Caller must be holding the sp->so_owner semaphore!
|
|
|
|
*/
|
2012-09-21 07:31:51 +07:00
|
|
|
int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-06-05 21:31:33 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_closedata *calldata;
|
2007-06-12 10:05:07 +07:00
|
|
|
struct nfs4_state_owner *sp = state->owner;
|
|
|
|
struct rpc_task *task;
|
2007-07-15 02:40:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
|
|
|
|
.rpc_cred = state->owner->so_cred,
|
|
|
|
};
|
2007-07-15 02:39:59 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = server->client,
|
2007-07-15 02:40:01 +07:00
|
|
|
.rpc_message = &msg,
|
2007-07-15 02:39:59 +07:00
|
|
|
.callback_ops = &nfs4_close_ops,
|
2008-02-20 08:04:23 +07:00
|
|
|
.workqueue = nfsiod_workqueue,
|
2007-07-15 02:39:59 +07:00
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2005-10-19 04:20:12 +07:00
|
|
|
int status = -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
calldata = kzalloc(sizeof(*calldata), gfp_mask);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (calldata == NULL)
|
2005-10-19 04:20:12 +07:00
|
|
|
goto out;
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 1);
|
2007-06-05 21:31:33 +07:00
|
|
|
calldata->inode = state->inode;
|
2005-04-17 05:20:36 +07:00
|
|
|
calldata->state = state;
|
2007-06-05 21:31:33 +07:00
|
|
|
calldata->arg.fh = NFS_FH(state->inode);
|
2007-07-06 05:07:55 +07:00
|
|
|
calldata->arg.stateid = &state->open_stateid;
|
2005-04-17 05:20:36 +07:00
|
|
|
/* Serialization for the sequence id */
|
2010-05-13 23:51:01 +07:00
|
|
|
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid, gfp_mask);
|
2005-10-19 04:20:12 +07:00
|
|
|
if (calldata->arg.seqid == NULL)
|
|
|
|
goto out_free_calldata;
|
2008-12-24 03:21:56 +07:00
|
|
|
calldata->arg.fmode = 0;
|
2009-03-12 01:10:28 +07:00
|
|
|
calldata->arg.bitmask = server->cache_consistency_bitmask;
|
2005-10-28 09:12:41 +07:00
|
|
|
calldata->res.fattr = &calldata->fattr;
|
2008-04-08 00:20:54 +07:00
|
|
|
calldata->res.seqid = calldata->arg.seqid;
|
2005-10-28 09:12:41 +07:00
|
|
|
calldata->res.server = server;
|
2012-09-21 07:31:51 +07:00
|
|
|
calldata->roc = pnfs_roc(state->inode);
|
2011-06-23 05:20:23 +07:00
|
|
|
nfs_sb_active(calldata->inode->i_sb);
|
2005-10-19 04:20:12 +07:00
|
|
|
|
2010-12-21 22:52:24 +07:00
|
|
|
msg.rpc_argp = &calldata->arg;
|
|
|
|
msg.rpc_resp = &calldata->res;
|
2007-07-15 02:39:59 +07:00
|
|
|
task_setup_data.callback_data = calldata;
|
|
|
|
task = rpc_run_task(&task_setup_data);
|
2007-06-12 10:05:07 +07:00
|
|
|
if (IS_ERR(task))
|
|
|
|
return PTR_ERR(task);
|
2007-10-19 05:03:27 +07:00
|
|
|
status = 0;
|
|
|
|
if (wait)
|
|
|
|
status = rpc_wait_for_completion_task(task);
|
2007-06-12 10:05:07 +07:00
|
|
|
rpc_put_task(task);
|
2007-10-19 05:03:27 +07:00
|
|
|
return status;
|
2005-10-19 04:20:12 +07:00
|
|
|
out_free_calldata:
|
|
|
|
kfree(calldata);
|
|
|
|
out:
|
2007-06-12 10:05:07 +07:00
|
|
|
nfs4_put_open_state(state);
|
|
|
|
nfs4_put_state_owner(sp);
|
2005-10-19 04:20:12 +07:00
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-09-17 21:56:51 +07:00
|
|
|
static struct inode *
|
2010-09-17 21:56:50 +07:00
|
|
|
nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_state *state;
|
|
|
|
|
2007-10-16 05:17:53 +07:00
|
|
|
/* Protect against concurrent sillydeletes */
|
2013-05-29 23:37:49 +07:00
|
|
|
state = nfs4_do_open(dir, ctx, open_flags, attr);
|
2010-09-17 21:56:50 +07:00
|
|
|
if (IS_ERR(state))
|
|
|
|
return ERR_CAST(state);
|
2013-05-30 00:11:28 +07:00
|
|
|
return state->inode;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2009-12-04 03:54:02 +07:00
|
|
|
static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
|
2009-03-20 02:35:50 +07:00
|
|
|
{
|
|
|
|
if (ctx->state == NULL)
|
|
|
|
return;
|
|
|
|
if (is_sync)
|
2011-06-23 05:20:23 +07:00
|
|
|
nfs4_close_sync(ctx->state, ctx->mode);
|
2009-03-20 02:35:50 +07:00
|
|
|
else
|
2011-06-23 05:20:23 +07:00
|
|
|
nfs4_close_state(ctx->state, ctx->mode);
|
2009-03-20 02:35:50 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
|
|
|
|
{
|
2009-04-01 20:21:54 +07:00
|
|
|
struct nfs4_server_caps_arg args = {
|
|
|
|
.fhandle = fhandle,
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_server_caps_res res = {};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS],
|
2009-04-01 20:21:54 +07:00
|
|
|
.rpc_argp = &args,
|
2005-04-17 05:20:36 +07:00
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (status == 0) {
|
|
|
|
memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
|
2009-08-10 02:06:19 +07:00
|
|
|
server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
|
|
|
|
NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
|
|
|
|
NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|
|
|
|
|
NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME|
|
|
|
|
NFS_CAP_CTIME|NFS_CAP_MTIME);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
|
|
|
|
server->caps |= NFS_CAP_ACLS;
|
|
|
|
if (res.has_links != 0)
|
|
|
|
server->caps |= NFS_CAP_HARDLINKS;
|
|
|
|
if (res.has_symlinks != 0)
|
|
|
|
server->caps |= NFS_CAP_SYMLINKS;
|
2009-08-10 02:06:19 +07:00
|
|
|
if (res.attr_bitmask[0] & FATTR4_WORD0_FILEID)
|
|
|
|
server->caps |= NFS_CAP_FILEID;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_MODE)
|
|
|
|
server->caps |= NFS_CAP_MODE;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS)
|
|
|
|
server->caps |= NFS_CAP_NLINK;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER)
|
|
|
|
server->caps |= NFS_CAP_OWNER;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER_GROUP)
|
|
|
|
server->caps |= NFS_CAP_OWNER_GROUP;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_ACCESS)
|
|
|
|
server->caps |= NFS_CAP_ATIME;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_METADATA)
|
|
|
|
server->caps |= NFS_CAP_CTIME;
|
|
|
|
if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
|
|
|
|
server->caps |= NFS_CAP_MTIME;
|
|
|
|
|
2009-03-12 01:10:28 +07:00
|
|
|
memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
|
|
|
|
server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
|
|
|
|
server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
|
2005-04-17 05:20:36 +07:00
|
|
|
server->acl_bitmask = res.acl_bitmask;
|
2012-03-02 05:02:05 +07:00
|
|
|
server->fh_expire_type = res.fh_expire_type;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2009-04-01 20:22:03 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-06-09 20:34:19 +07:00
|
|
|
int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(server,
|
|
|
|
_nfs4_server_capabilities(server, fhandle),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info)
|
|
|
|
{
|
|
|
|
struct nfs4_lookup_root_arg args = {
|
|
|
|
.bitmask = nfs4_fattr_bitmap,
|
|
|
|
};
|
|
|
|
struct nfs4_lookup_res res = {
|
|
|
|
.server = server,
|
2005-10-28 09:12:38 +07:00
|
|
|
.fattr = info->fattr,
|
2005-04-17 05:20:36 +07:00
|
|
|
.fh = fhandle,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP_ROOT],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
2009-04-01 20:22:50 +07:00
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(info->fattr);
|
2011-03-25 00:12:24 +07:00
|
|
|
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
2011-04-19 03:52:25 +07:00
|
|
|
err = _nfs4_lookup_root(server, fhandle, info);
|
|
|
|
switch (err) {
|
|
|
|
case 0:
|
|
|
|
case -NFS4ERR_WRONGSEC:
|
2012-03-28 05:13:02 +07:00
|
|
|
goto out;
|
2011-04-19 03:52:25 +07:00
|
|
|
default:
|
|
|
|
err = nfs4_handle_exception(server, err, &exception);
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (exception.retry);
|
2012-03-28 05:13:02 +07:00
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-03-25 00:12:31 +07:00
|
|
|
static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info, rpc_authflavor_t flavor)
|
|
|
|
{
|
|
|
|
struct rpc_auth *auth;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
auth = rpcauth_create(flavor, server->client);
|
2012-09-21 11:27:41 +07:00
|
|
|
if (IS_ERR(auth)) {
|
2013-03-17 02:55:36 +07:00
|
|
|
ret = -EACCES;
|
2011-03-25 00:12:31 +07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = nfs4_lookup_root(server, fhandle, info);
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
NFS: Use static list of security flavors during root FH lookup recovery
If the Linux NFS client receives an NFS4ERR_WRONGSEC error while
trying to look up an NFS server's root file handle, it retries the
lookup operation with various security flavors to see what flavor
the NFS server will accept for pseudo-fs access.
The list of flavors the client uses during retry consists only of
flavors that are currently registered in the kernel RPC client.
This list may not include any GSS pseudoflavors if auth_rpcgss.ko
has not yet been loaded.
Let's instead use a static list of security flavors that the NFS
standard requires the server to implement (RFC 3530bis, section
3.2.1). The RPC client should now be able to load support for
these dynamically; if not, they are skipped.
Recovery behavior here is prescribed by RFC 3530bis, section
15.33.5:
> For LOOKUPP, PUTROOTFH and PUTPUBFH, the client will be unable to
> use the SECINFO operation since SECINFO requires a current
> filehandle and none exist for these two [sic] operations. Therefore,
> the client must iterate through the security triples available at
> the client and reattempt the PUTROOTFH or PUTPUBFH operation. In
> the unfortunate event none of the MANDATORY security triples are
> supported by the client and server, the client SHOULD try using
> others that support integrity. Failing that, the client can try
> using AUTH_NONE, but because such forms lack integrity checks,
> this puts the client at risk.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-03-17 02:56:02 +07:00
|
|
|
/*
|
|
|
|
* Retry pseudoroot lookup with various security flavors. We do this when:
|
|
|
|
*
|
|
|
|
* NFSv4.0: the PUTROOTFH operation returns NFS4ERR_WRONGSEC
|
|
|
|
* NFSv4.1: the server does not support the SECINFO_NO_NAME operation
|
|
|
|
*
|
|
|
|
* Returns zero on success, or a negative NFS4ERR value, or a
|
|
|
|
* negative errno value.
|
|
|
|
*/
|
2011-04-14 01:31:30 +07:00
|
|
|
static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
NFS: Share NFS superblocks per-protocol per-server per-FSID
The attached patch makes NFS share superblocks between mounts from the same
server and FSID over the same protocol.
It does this by creating each superblock with a false root and returning the
real root dentry in the vfsmount presented by get_sb(). The root dentry set
starts off as an anonymous dentry if we don't already have the dentry for its
inode, otherwise it simply returns the dentry we already have.
We may thus end up with several trees of dentries in the superblock, and if at
some later point one of anonymous tree roots is discovered by normal filesystem
activity to be located in another tree within the superblock, the anonymous
root is named and materialises attached to the second tree at the appropriate
point.
Why do it this way? Why not pass an extra argument to the mount() syscall to
indicate the subpath and then pathwalk from the server root to the desired
directory? You can't guarantee this will work for two reasons:
(1) The root and intervening nodes may not be accessible to the client.
With NFS2 and NFS3, for instance, mountd is called on the server to get
the filehandle for the tip of a path. mountd won't give us handles for
anything we don't have permission to access, and so we can't set up NFS
inodes for such nodes, and so can't easily set up dentries (we'd have to
have ghost inodes or something).
With this patch we don't actually create dentries until we get handles
from the server that we can use to set up their inodes, and we don't
actually bind them into the tree until we know for sure where they go.
(2) Inaccessible symbolic links.
If we're asked to mount two exports from the server, eg:
mount warthog:/warthog/aaa/xxx /mmm
mount warthog:/warthog/bbb/yyy /nnn
We may not be able to access anything nearer the root than xxx and yyy,
but we may find out later that /mmm/www/yyy, say, is actually the same
directory as the one mounted on /nnn. What we might then find out, for
example, is that /warthog/bbb was actually a symbolic link to
/warthog/aaa/xxx/www, but we can't actually determine that by talking to
the server until /warthog is made available by NFS.
This would lead to having constructed an errneous dentry tree which we
can't easily fix. We can end up with a dentry marked as a directory when
it should actually be a symlink, or we could end up with an apparently
hardlinked directory.
With this patch we need not make assumptions about the type of a dentry
for which we can't retrieve information, nor need we assume we know its
place in the grand scheme of things until we actually see that place.
This patch reduces the possibility of aliasing in the inode and page caches for
inodes that may be accessed by more than one NFS export. It also reduces the
number of superblocks required for NFS where there are many NFS exports being
used from a server (home directory server + autofs for example).
This in turn makes it simpler to do local caching of network filesystems, as it
can then be guaranteed that there won't be links from multiple inodes in
separate superblocks to the same cache file.
Obviously, cache aliasing between different levels of NFS protocol could still
be a problem, but at least that gives us another key to use when indexing the
cache.
This patch makes the following changes:
(1) The server record construction/destruction has been abstracted out into
its own set of functions to make things easier to get right. These have
been moved into fs/nfs/client.c.
All the code in fs/nfs/client.c has to do with the management of
connections to servers, and doesn't touch superblocks in any way; the
remaining code in fs/nfs/super.c has to do with VFS superblock management.
(2) The sequence of events undertaken by NFS mount is now reordered:
(a) A volume representation (struct nfs_server) is allocated.
(b) A server representation (struct nfs_client) is acquired. This may be
allocated or shared, and is keyed on server address, port and NFS
version.
(c) If allocated, the client representation is initialised. The state
member variable of nfs_client is used to prevent a race during
initialisation from two mounts.
(d) For NFS4 a simple pathwalk is performed, walking from FH to FH to find
the root filehandle for the mount (fs/nfs/getroot.c). For NFS2/3 we
are given the root FH in advance.
(e) The volume FSID is probed for on the root FH.
(f) The volume representation is initialised from the FSINFO record
retrieved on the root FH.
(g) sget() is called to acquire a superblock. This may be allocated or
shared, keyed on client pointer and FSID.
(h) If allocated, the superblock is initialised.
(i) If the superblock is shared, then the new nfs_server record is
discarded.
(j) The root dentry for this mount is looked up from the root FH.
(k) The root dentry for this mount is assigned to the vfsmount.
(3) nfs_readdir_lookup() creates dentries for each of the entries readdir()
returns; this function now attaches disconnected trees from alternate
roots that happen to be discovered attached to a directory being read (in
the same way nfs_lookup() is made to do for lookup ops).
The new d_materialise_unique() function is now used to do this, thus
permitting the whole thing to be done under one set of locks, and thus
avoiding any race between mount and lookup operations on the same
directory.
(4) The client management code uses a new debug facility: NFSDBG_CLIENT which
is set by echoing 1024 to /proc/net/sunrpc/nfs_debug.
(5) Clone mounts are now called xdev mounts.
(6) Use the dentry passed to the statfs() op as the handle for retrieving fs
statistics rather than the root dentry of the superblock (which is now a
dummy).
Signed-Off-By: David Howells <dhowells@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2006-08-23 07:06:13 +07:00
|
|
|
struct nfs_fsinfo *info)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
NFS: Use static list of security flavors during root FH lookup recovery
If the Linux NFS client receives an NFS4ERR_WRONGSEC error while
trying to look up an NFS server's root file handle, it retries the
lookup operation with various security flavors to see what flavor
the NFS server will accept for pseudo-fs access.
The list of flavors the client uses during retry consists only of
flavors that are currently registered in the kernel RPC client.
This list may not include any GSS pseudoflavors if auth_rpcgss.ko
has not yet been loaded.
Let's instead use a static list of security flavors that the NFS
standard requires the server to implement (RFC 3530bis, section
3.2.1). The RPC client should now be able to load support for
these dynamically; if not, they are skipped.
Recovery behavior here is prescribed by RFC 3530bis, section
15.33.5:
> For LOOKUPP, PUTROOTFH and PUTPUBFH, the client will be unable to
> use the SECINFO operation since SECINFO requires a current
> filehandle and none exist for these two [sic] operations. Therefore,
> the client must iterate through the security triples available at
> the client and reattempt the PUTROOTFH or PUTPUBFH operation. In
> the unfortunate event none of the MANDATORY security triples are
> supported by the client and server, the client SHOULD try using
> others that support integrity. Failing that, the client can try
> using AUTH_NONE, but because such forms lack integrity checks,
> this puts the client at risk.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-03-17 02:56:02 +07:00
|
|
|
/* Per 3530bis 15.33.5 */
|
|
|
|
static const rpc_authflavor_t flav_array[] = {
|
|
|
|
RPC_AUTH_GSS_KRB5P,
|
|
|
|
RPC_AUTH_GSS_KRB5I,
|
|
|
|
RPC_AUTH_GSS_KRB5,
|
2013-03-17 02:56:11 +07:00
|
|
|
RPC_AUTH_UNIX, /* courtesy */
|
NFS: Use static list of security flavors during root FH lookup recovery
If the Linux NFS client receives an NFS4ERR_WRONGSEC error while
trying to look up an NFS server's root file handle, it retries the
lookup operation with various security flavors to see what flavor
the NFS server will accept for pseudo-fs access.
The list of flavors the client uses during retry consists only of
flavors that are currently registered in the kernel RPC client.
This list may not include any GSS pseudoflavors if auth_rpcgss.ko
has not yet been loaded.
Let's instead use a static list of security flavors that the NFS
standard requires the server to implement (RFC 3530bis, section
3.2.1). The RPC client should now be able to load support for
these dynamically; if not, they are skipped.
Recovery behavior here is prescribed by RFC 3530bis, section
15.33.5:
> For LOOKUPP, PUTROOTFH and PUTPUBFH, the client will be unable to
> use the SECINFO operation since SECINFO requires a current
> filehandle and none exist for these two [sic] operations. Therefore,
> the client must iterate through the security triples available at
> the client and reattempt the PUTROOTFH or PUTPUBFH operation. In
> the unfortunate event none of the MANDATORY security triples are
> supported by the client and server, the client SHOULD try using
> others that support integrity. Failing that, the client can try
> using AUTH_NONE, but because such forms lack integrity checks,
> this puts the client at risk.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-03-17 02:56:02 +07:00
|
|
|
RPC_AUTH_NULL,
|
|
|
|
};
|
|
|
|
int status = -EPERM;
|
|
|
|
size_t i;
|
2012-07-12 03:31:08 +07:00
|
|
|
|
NFS: Use static list of security flavors during root FH lookup recovery
If the Linux NFS client receives an NFS4ERR_WRONGSEC error while
trying to look up an NFS server's root file handle, it retries the
lookup operation with various security flavors to see what flavor
the NFS server will accept for pseudo-fs access.
The list of flavors the client uses during retry consists only of
flavors that are currently registered in the kernel RPC client.
This list may not include any GSS pseudoflavors if auth_rpcgss.ko
has not yet been loaded.
Let's instead use a static list of security flavors that the NFS
standard requires the server to implement (RFC 3530bis, section
3.2.1). The RPC client should now be able to load support for
these dynamically; if not, they are skipped.
Recovery behavior here is prescribed by RFC 3530bis, section
15.33.5:
> For LOOKUPP, PUTROOTFH and PUTPUBFH, the client will be unable to
> use the SECINFO operation since SECINFO requires a current
> filehandle and none exist for these two [sic] operations. Therefore,
> the client must iterate through the security triples available at
> the client and reattempt the PUTROOTFH or PUTPUBFH operation. In
> the unfortunate event none of the MANDATORY security triples are
> supported by the client and server, the client SHOULD try using
> others that support integrity. Failing that, the client can try
> using AUTH_NONE, but because such forms lack integrity checks,
> this puts the client at risk.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-03-17 02:56:02 +07:00
|
|
|
for (i = 0; i < ARRAY_SIZE(flav_array); i++) {
|
2011-03-25 00:12:31 +07:00
|
|
|
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
|
2011-04-19 03:52:25 +07:00
|
|
|
if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
|
2011-04-14 01:31:28 +07:00
|
|
|
continue;
|
|
|
|
break;
|
2011-03-25 00:12:31 +07:00
|
|
|
}
|
NFS: Use static list of security flavors during root FH lookup recovery
If the Linux NFS client receives an NFS4ERR_WRONGSEC error while
trying to look up an NFS server's root file handle, it retries the
lookup operation with various security flavors to see what flavor
the NFS server will accept for pseudo-fs access.
The list of flavors the client uses during retry consists only of
flavors that are currently registered in the kernel RPC client.
This list may not include any GSS pseudoflavors if auth_rpcgss.ko
has not yet been loaded.
Let's instead use a static list of security flavors that the NFS
standard requires the server to implement (RFC 3530bis, section
3.2.1). The RPC client should now be able to load support for
these dynamically; if not, they are skipped.
Recovery behavior here is prescribed by RFC 3530bis, section
15.33.5:
> For LOOKUPP, PUTROOTFH and PUTPUBFH, the client will be unable to
> use the SECINFO operation since SECINFO requires a current
> filehandle and none exist for these two [sic] operations. Therefore,
> the client must iterate through the security triples available at
> the client and reattempt the PUTROOTFH or PUTPUBFH operation. In
> the unfortunate event none of the MANDATORY security triples are
> supported by the client and server, the client SHOULD try using
> others that support integrity. Failing that, the client can try
> using AUTH_NONE, but because such forms lack integrity checks,
> this puts the client at risk.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2013-03-17 02:56:02 +07:00
|
|
|
|
2011-04-19 03:52:25 +07:00
|
|
|
/*
|
|
|
|
* -EACCESS could mean that the user doesn't have correct permissions
|
|
|
|
* to access the mount. It could also mean that we tried to mount
|
|
|
|
* with a gss auth flavor, but rpc.gssd isn't running. Either way,
|
|
|
|
* existing mount programs don't handle -EACCES very well so it should
|
|
|
|
* be mapped to -EPERM instead.
|
|
|
|
*/
|
|
|
|
if (status == -EACCES)
|
|
|
|
status = -EPERM;
|
2011-04-14 01:31:30 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2013-03-17 02:55:45 +07:00
|
|
|
static int nfs4_do_find_root_sec(struct nfs_server *server,
|
|
|
|
struct nfs_fh *fhandle, struct nfs_fsinfo *info)
|
|
|
|
{
|
|
|
|
int mv = server->nfs_client->cl_minorversion;
|
|
|
|
return nfs_v4_minor_ops[mv]->find_root_sec(server, fhandle, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nfs4_proc_get_rootfh - get file handle for server's pseudoroot
|
|
|
|
* @server: initialized nfs_server handle
|
|
|
|
* @fhandle: we fill in the pseudo-fs root file handle
|
|
|
|
* @info: we fill in an FSINFO struct
|
|
|
|
*
|
|
|
|
* Returns zero on success, or a negative errno.
|
2011-04-14 01:31:30 +07:00
|
|
|
*/
|
2012-05-11 02:07:30 +07:00
|
|
|
int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info)
|
2011-04-14 01:31:30 +07:00
|
|
|
{
|
2013-03-17 02:55:45 +07:00
|
|
|
int status;
|
|
|
|
|
|
|
|
status = nfs4_lookup_root(server, fhandle, info);
|
|
|
|
if ((status == -NFS4ERR_WRONGSEC) &&
|
|
|
|
!(server->flags & NFS_MOUNT_SECFLAVOUR))
|
|
|
|
status = nfs4_do_find_root_sec(server, fhandle, info);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (status == 0)
|
|
|
|
status = nfs4_server_capabilities(server, fhandle);
|
|
|
|
if (status == 0)
|
|
|
|
status = nfs4_do_fsinfo(server, fhandle, info);
|
2013-03-17 02:55:45 +07:00
|
|
|
|
2006-03-14 12:20:47 +07:00
|
|
|
return nfs4_map_errors(status);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-05-11 02:07:31 +07:00
|
|
|
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
|
|
|
|
struct nfs_fsinfo *info)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
struct nfs_fattr *fattr = info->fattr;
|
|
|
|
|
|
|
|
error = nfs4_server_capabilities(server, mntfh);
|
|
|
|
if (error < 0) {
|
|
|
|
dprintk("nfs4_get_root: getcaps error = %d\n", -error);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = nfs4_proc_getattr(server, mntfh, fattr);
|
|
|
|
if (error < 0) {
|
|
|
|
dprintk("nfs4_get_root: getattr error = %d\n", -error);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fattr->valid & NFS_ATTR_FATTR_FSID &&
|
|
|
|
!nfs_fsid_equal(&server->fsid, &fattr->fsid))
|
|
|
|
memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2006-06-09 20:34:29 +07:00
|
|
|
/*
|
|
|
|
* Get locations and (maybe) other attributes of a referral.
|
|
|
|
* Note that we'll actually follow the referral later when
|
|
|
|
* we detect fsid mismatch in inode revalidation
|
|
|
|
*/
|
2012-04-28 00:27:41 +07:00
|
|
|
static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
|
|
|
|
const struct qstr *name, struct nfs_fattr *fattr,
|
|
|
|
struct nfs_fh *fhandle)
|
2006-06-09 20:34:29 +07:00
|
|
|
{
|
|
|
|
int status = -ENOMEM;
|
|
|
|
struct page *page = NULL;
|
|
|
|
struct nfs4_fs_locations *locations = NULL;
|
|
|
|
|
|
|
|
page = alloc_page(GFP_KERNEL);
|
|
|
|
if (page == NULL)
|
|
|
|
goto out;
|
|
|
|
locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
|
|
|
|
if (locations == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2012-04-28 00:27:41 +07:00
|
|
|
status = nfs4_proc_fs_locations(client, dir, name, locations, page);
|
2006-06-09 20:34:29 +07:00
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
|
|
|
/* Make sure server returned a different fsid for the referral */
|
|
|
|
if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
|
2011-06-14 05:25:56 +07:00
|
|
|
dprintk("%s: server did not return a different fsid for"
|
|
|
|
" a referral at %s\n", __func__, name->name);
|
2006-06-09 20:34:29 +07:00
|
|
|
status = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-06-14 05:25:56 +07:00
|
|
|
/* Fixup attributes for the nfs_lookup() call to nfs_fhget() */
|
|
|
|
nfs_fixup_referral_attributes(&locations->fattr);
|
2006-06-09 20:34:29 +07:00
|
|
|
|
2011-06-14 05:25:56 +07:00
|
|
|
/* replace the lookup nfs_fattr with the locations nfs_fattr */
|
2006-06-09 20:34:29 +07:00
|
|
|
memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
|
|
|
|
memset(fhandle, 0, sizeof(struct nfs_fh));
|
|
|
|
out:
|
|
|
|
if (page)
|
|
|
|
__free_page(page);
|
2010-08-11 23:42:15 +07:00
|
|
|
kfree(locations);
|
2006-06-09 20:34:29 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
struct nfs4_getattr_arg args = {
|
|
|
|
.fh = fhandle,
|
|
|
|
.bitmask = server->attr_bitmask,
|
|
|
|
};
|
|
|
|
struct nfs4_getattr_res res = {
|
|
|
|
.fattr = fattr,
|
|
|
|
.server = server,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(fattr);
|
2011-03-25 00:12:24 +07:00
|
|
|
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(server,
|
|
|
|
_nfs4_proc_getattr(server, fhandle, fattr),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The file is not closed if it is opened due to the a request to change
|
|
|
|
* the size of the file. The open call will not be needed once the
|
|
|
|
* VFS layer lookup-intents are implemented.
|
|
|
|
*
|
|
|
|
* Close is called when the inode is destroyed.
|
|
|
|
* If we haven't opened the file for O_WRONLY, we
|
|
|
|
* need to in the size_change case to obtain a stateid.
|
|
|
|
*
|
|
|
|
* Got race?
|
|
|
|
* Because OPEN is always done by name in nfsv4, it is
|
|
|
|
* possible that we opened a different file by the same
|
|
|
|
* name. We can recognize this race condition, but we
|
|
|
|
* can't do anything about it besides returning an error.
|
|
|
|
*
|
|
|
|
* This will be fixed with VFS changes (lookup-intent).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
|
|
|
struct iattr *sattr)
|
|
|
|
{
|
2005-06-23 00:16:29 +07:00
|
|
|
struct inode *inode = dentry->d_inode;
|
2008-06-11 06:39:41 +07:00
|
|
|
struct rpc_cred *cred = NULL;
|
2005-11-05 03:33:38 +07:00
|
|
|
struct nfs4_state *state = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
int status;
|
|
|
|
|
2010-07-15 02:43:57 +07:00
|
|
|
if (pnfs_ld_layoutret_on_setattr(inode))
|
2013-03-21 00:23:33 +07:00
|
|
|
pnfs_commit_and_return_layout(inode);
|
2010-07-15 02:43:57 +07:00
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(fattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-05-31 03:12:24 +07:00
|
|
|
/* Deal with open(O_TRUNC) */
|
|
|
|
if (sattr->ia_valid & ATTR_OPEN)
|
|
|
|
sattr->ia_valid &= ~(ATTR_MTIME|ATTR_CTIME|ATTR_OPEN);
|
|
|
|
|
|
|
|
/* Optimization: if the end result is no change, don't RPC */
|
|
|
|
if ((sattr->ia_valid & ~(ATTR_FILE)) == 0)
|
|
|
|
return 0;
|
|
|
|
|
2005-11-05 03:33:38 +07:00
|
|
|
/* Search for an existing open(O_WRITE) file */
|
2008-06-11 06:39:41 +07:00
|
|
|
if (sattr->ia_valid & ATTR_FILE) {
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
|
|
|
|
ctx = nfs_file_open_context(sattr->ia_file);
|
2008-10-16 10:15:16 +07:00
|
|
|
if (ctx) {
|
|
|
|
cred = ctx->cred;
|
|
|
|
state = ctx->state;
|
|
|
|
}
|
2008-06-11 06:39:41 +07:00
|
|
|
}
|
2005-06-23 00:16:29 +07:00
|
|
|
|
2008-06-11 06:39:41 +07:00
|
|
|
status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
|
2005-08-16 22:49:44 +07:00
|
|
|
if (status == 0)
|
|
|
|
nfs_setattr_update_inode(inode, sattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2011-10-19 06:11:22 +07:00
|
|
|
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
|
|
|
const struct qstr *name, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fattr *fattr)
|
2006-08-23 07:06:09 +07:00
|
|
|
{
|
2011-10-19 06:11:22 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
2006-08-23 07:06:09 +07:00
|
|
|
int status;
|
|
|
|
struct nfs4_lookup_arg args = {
|
|
|
|
.bitmask = server->attr_bitmask,
|
2011-10-19 06:11:22 +07:00
|
|
|
.dir_fh = NFS_FH(dir),
|
2006-08-23 07:06:09 +07:00
|
|
|
.name = name,
|
|
|
|
};
|
|
|
|
struct nfs4_lookup_res res = {
|
|
|
|
.server = server,
|
|
|
|
.fattr = fattr,
|
|
|
|
.fh = fhandle,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
|
|
|
|
nfs_fattr_init(fattr);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
dprintk("NFS call lookup %s\n", name->name);
|
2011-10-19 06:11:22 +07:00
|
|
|
status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
dprintk("NFS reply lookup: %d\n", status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:40 +07:00
|
|
|
static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
|
2011-03-25 00:12:30 +07:00
|
|
|
{
|
|
|
|
fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
|
2012-04-28 00:27:40 +07:00
|
|
|
NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_MOUNTPOINT;
|
2011-03-25 00:12:30 +07:00
|
|
|
fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
|
|
|
fattr->nlink = 2;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:40 +07:00
|
|
|
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
|
|
|
|
struct qstr *name, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fattr *fattr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
2012-04-28 00:27:40 +07:00
|
|
|
struct rpc_clnt *client = *clnt;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
do {
|
2012-04-28 00:27:40 +07:00
|
|
|
err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr);
|
|
|
|
switch (err) {
|
2011-10-19 06:11:49 +07:00
|
|
|
case -NFS4ERR_BADNAME:
|
2012-04-28 00:27:40 +07:00
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
2011-10-19 06:11:22 +07:00
|
|
|
case -NFS4ERR_MOVED:
|
2012-04-28 00:27:41 +07:00
|
|
|
err = nfs4_get_referral(client, dir, name, fattr, fhandle);
|
2012-04-28 00:27:40 +07:00
|
|
|
goto out;
|
2011-10-19 06:11:22 +07:00
|
|
|
case -NFS4ERR_WRONGSEC:
|
2012-04-28 00:27:40 +07:00
|
|
|
err = -EPERM;
|
|
|
|
if (client != *clnt)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
client = nfs4_create_sec_client(client, dir, name);
|
|
|
|
if (IS_ERR(client))
|
|
|
|
return PTR_ERR(client);
|
|
|
|
|
|
|
|
exception.retry = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception);
|
2011-10-19 06:11:22 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (exception.retry);
|
2012-04-28 00:27:40 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (err == 0)
|
|
|
|
*clnt = client;
|
|
|
|
else if (client != *clnt)
|
|
|
|
rpc_shutdown_client(client);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:46 +07:00
|
|
|
static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
|
2012-04-28 00:27:40 +07:00
|
|
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct rpc_clnt *client = NFS_CLIENT(dir);
|
|
|
|
|
|
|
|
status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
|
|
|
|
if (client != NFS_CLIENT(dir)) {
|
|
|
|
rpc_shutdown_client(client);
|
|
|
|
nfs_fixup_secinfo_attributes(fattr);
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:41 +07:00
|
|
|
struct rpc_clnt *
|
|
|
|
nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name,
|
|
|
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
|
|
|
|
|
|
|
|
status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
|
|
|
|
if (status < 0) {
|
|
|
|
rpc_shutdown_client(client);
|
|
|
|
return ERR_PTR(status);
|
|
|
|
}
|
|
|
|
return client;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
|
|
|
|
{
|
2007-08-11 04:45:11 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_accessargs args = {
|
|
|
|
.fh = NFS_FH(inode),
|
2012-01-31 03:43:56 +07:00
|
|
|
.bitmask = server->cache_consistency_bitmask,
|
2007-08-11 04:45:11 +07:00
|
|
|
};
|
|
|
|
struct nfs4_accessres res = {
|
|
|
|
.server = server,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
.rpc_cred = entry->cred,
|
|
|
|
};
|
|
|
|
int mode = entry->mask;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine which access bits we want to ask for...
|
|
|
|
*/
|
|
|
|
if (mode & MAY_READ)
|
|
|
|
args.access |= NFS4_ACCESS_READ;
|
|
|
|
if (S_ISDIR(inode->i_mode)) {
|
|
|
|
if (mode & MAY_WRITE)
|
|
|
|
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE;
|
|
|
|
if (mode & MAY_EXEC)
|
|
|
|
args.access |= NFS4_ACCESS_LOOKUP;
|
|
|
|
} else {
|
|
|
|
if (mode & MAY_WRITE)
|
|
|
|
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND;
|
|
|
|
if (mode & MAY_EXEC)
|
|
|
|
args.access |= NFS4_ACCESS_EXECUTE;
|
|
|
|
}
|
2010-04-17 03:22:48 +07:00
|
|
|
|
|
|
|
res.fattr = nfs_alloc_fattr();
|
|
|
|
if (res.fattr == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!status) {
|
2012-09-11 01:00:46 +07:00
|
|
|
nfs_access_set_mask(entry, res.access);
|
2010-04-17 03:22:48 +07:00
|
|
|
nfs_refresh_inode(inode, res.fattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2010-04-17 03:22:48 +07:00
|
|
|
nfs_free_fattr(res.fattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(inode),
|
|
|
|
_nfs4_proc_access(inode, entry),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: For the time being, we don't try to get any attributes
|
|
|
|
* along with any of the zero-copy operations READ, READDIR,
|
|
|
|
* READLINK, WRITE.
|
|
|
|
*
|
|
|
|
* In the case of the first three, we want to put the GETATTR
|
|
|
|
* after the read-type operation -- this is because it is hard
|
|
|
|
* to predict the length of a GETATTR response in v4, and thus
|
|
|
|
* align the READ data correctly. This means that the GETATTR
|
|
|
|
* may end up partially falling into the page cache, and we should
|
|
|
|
* shift it into the 'tail' of the xdr_buf before processing.
|
|
|
|
* To do this efficiently, we need to know the total length
|
|
|
|
* of data received, which doesn't seem to be available outside
|
|
|
|
* of the RPC layer.
|
|
|
|
*
|
|
|
|
* In the case of WRITE, we also want to put the GETATTR after
|
|
|
|
* the operation -- in this case because we want to make sure
|
2012-06-06 02:20:25 +07:00
|
|
|
* we get the post-operation mtime and size.
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Both of these changes to the XDR layer would in fact be quite
|
|
|
|
* minor, but I decided to leave them for a subsequent patch.
|
|
|
|
*/
|
|
|
|
static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
|
|
|
|
unsigned int pgbase, unsigned int pglen)
|
|
|
|
{
|
|
|
|
struct nfs4_readlink args = {
|
|
|
|
.fh = NFS_FH(inode),
|
|
|
|
.pgbase = pgbase,
|
|
|
|
.pglen = pglen,
|
|
|
|
.pages = &page,
|
|
|
|
};
|
2009-04-01 20:21:55 +07:00
|
|
|
struct nfs4_readlink_res res;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK],
|
|
|
|
.rpc_argp = &args,
|
2009-04-01 20:21:55 +07:00
|
|
|
.rpc_resp = &res,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
return nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_readlink(struct inode *inode, struct page *page,
|
|
|
|
unsigned int pgbase, unsigned int pglen)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(inode),
|
|
|
|
_nfs4_proc_readlink(inode, page, pgbase, pglen),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-06-05 20:10:19 +07:00
|
|
|
* This is just for mknod. open(O_CREAT) will always do ->open_context().
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
2012-06-05 20:10:19 +07:00
|
|
|
int flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-06-05 20:10:19 +07:00
|
|
|
struct nfs_open_context *ctx;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_state *state;
|
|
|
|
int status = 0;
|
|
|
|
|
2012-06-05 20:10:19 +07:00
|
|
|
ctx = alloc_nfs_open_context(dentry, FMODE_READ);
|
|
|
|
if (IS_ERR(ctx))
|
|
|
|
return PTR_ERR(ctx);
|
|
|
|
|
2010-12-09 18:35:14 +07:00
|
|
|
sattr->ia_mode &= ~current_umask();
|
2013-05-29 23:37:49 +07:00
|
|
|
state = nfs4_do_open(dir, ctx, flags, sattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (IS_ERR(state)) {
|
|
|
|
status = PTR_ERR(state);
|
2010-09-17 21:56:51 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
out:
|
2012-06-05 20:10:19 +07:00
|
|
|
put_nfs_open_context(ctx);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
|
|
|
|
{
|
2005-10-28 09:12:44 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
2007-07-15 02:39:57 +07:00
|
|
|
struct nfs_removeargs args = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.fh = NFS_FH(dir),
|
2012-05-11 03:14:12 +07:00
|
|
|
.name = *name,
|
2005-10-28 09:12:44 +07:00
|
|
|
};
|
2007-07-15 02:39:57 +07:00
|
|
|
struct nfs_removeres res = {
|
2005-10-28 09:12:44 +07:00
|
|
|
.server = server,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
2007-07-15 02:39:57 +07:00
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2012-04-28 00:48:19 +07:00
|
|
|
int status;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
|
2012-04-28 00:48:19 +07:00
|
|
|
if (status == 0)
|
2005-10-28 09:12:44 +07:00
|
|
|
update_changeattr(dir, &res.cinfo);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir),
|
|
|
|
_nfs4_proc_remove(dir, name),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-07-15 02:39:58 +07:00
|
|
|
static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-15 02:39:58 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
|
|
struct nfs_removeargs *args = msg->rpc_argp;
|
|
|
|
struct nfs_removeres *res = msg->rpc_resp;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-07-15 02:39:58 +07:00
|
|
|
res->server = server;
|
2005-04-17 05:20:36 +07:00
|
|
|
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&args->seq_args, &res->seq_res, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-03-20 01:54:41 +07:00
|
|
|
static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data)
|
|
|
|
{
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs4_setup_sequence(NFS_SERVER(data->dir),
|
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
|
|
|
task);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2007-07-15 02:39:58 +07:00
|
|
|
static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2007-07-15 02:39:58 +07:00
|
|
|
struct nfs_removeres *res = task->tk_msg.rpc_resp;
|
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs4_sequence_done(task, &res->seq_res))
|
|
|
|
return 0;
|
2008-12-24 03:21:46 +07:00
|
|
|
if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
|
2007-07-15 02:39:58 +07:00
|
|
|
return 0;
|
|
|
|
update_changeattr(dir, &res->cinfo);
|
|
|
|
return 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-09-18 04:31:57 +07:00
|
|
|
static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir)
|
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
|
|
struct nfs_renameargs *arg = msg->rpc_argp;
|
|
|
|
struct nfs_renameres *res = msg->rpc_resp;
|
|
|
|
|
|
|
|
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
|
|
|
|
res->server = server;
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&arg->seq_args, &res->seq_res, 1);
|
2010-09-18 04:31:57 +07:00
|
|
|
}
|
|
|
|
|
2012-03-20 01:54:42 +07:00
|
|
|
static void nfs4_proc_rename_rpc_prepare(struct rpc_task *task, struct nfs_renamedata *data)
|
|
|
|
{
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs4_setup_sequence(NFS_SERVER(data->old_dir),
|
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
|
|
|
task);
|
2010-09-18 04:31:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
|
|
|
|
struct inode *new_dir)
|
|
|
|
{
|
|
|
|
struct nfs_renameres *res = task->tk_msg.rpc_resp;
|
|
|
|
|
|
|
|
if (!nfs4_sequence_done(task, &res->seq_res))
|
|
|
|
return 0;
|
|
|
|
if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
update_changeattr(old_dir, &res->old_cinfo);
|
|
|
|
update_changeattr(new_dir, &res->new_cinfo);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
|
|
|
|
struct inode *new_dir, struct qstr *new_name)
|
|
|
|
{
|
2005-10-28 09:12:43 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(old_dir);
|
2010-09-18 04:30:25 +07:00
|
|
|
struct nfs_renameargs arg = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.old_dir = NFS_FH(old_dir),
|
|
|
|
.new_dir = NFS_FH(new_dir),
|
|
|
|
.old_name = old_name,
|
|
|
|
.new_name = new_name,
|
2005-10-28 09:12:43 +07:00
|
|
|
};
|
2010-09-18 04:31:06 +07:00
|
|
|
struct nfs_renameres res = {
|
2005-10-28 09:12:43 +07:00
|
|
|
.server = server,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME],
|
|
|
|
.rpc_argp = &arg,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
2010-04-17 03:22:49 +07:00
|
|
|
int status = -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (!status) {
|
|
|
|
update_changeattr(old_dir, &res.old_cinfo);
|
|
|
|
update_changeattr(new_dir, &res.new_cinfo);
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
|
|
|
|
struct inode *new_dir, struct qstr *new_name)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(old_dir),
|
|
|
|
_nfs4_proc_rename(old_dir, old_name,
|
|
|
|
new_dir, new_name),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
|
|
|
|
{
|
2005-10-28 09:12:42 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_link_arg arg = {
|
|
|
|
.fh = NFS_FH(inode),
|
|
|
|
.dir_fh = NFS_FH(dir),
|
|
|
|
.name = name,
|
2005-10-28 09:12:42 +07:00
|
|
|
.bitmask = server->attr_bitmask,
|
|
|
|
};
|
|
|
|
struct nfs4_link_res res = {
|
|
|
|
.server = server,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
|
|
|
|
.rpc_argp = &arg,
|
2005-10-28 09:12:42 +07:00
|
|
|
.rpc_resp = &res,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2010-04-17 03:22:49 +07:00
|
|
|
int status = -ENOMEM;
|
|
|
|
|
|
|
|
res.fattr = nfs_alloc_fattr();
|
2012-04-28 00:48:19 +07:00
|
|
|
if (res.fattr == NULL)
|
2010-04-17 03:22:49 +07:00
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
2005-10-28 09:12:42 +07:00
|
|
|
if (!status) {
|
|
|
|
update_changeattr(dir, &res.cinfo);
|
2006-05-25 12:40:47 +07:00
|
|
|
nfs_post_op_update_inode(inode, res.fattr);
|
2005-10-28 09:12:42 +07:00
|
|
|
}
|
2010-04-17 03:22:49 +07:00
|
|
|
out:
|
|
|
|
nfs_free_fattr(res.fattr);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(inode),
|
|
|
|
_nfs4_proc_link(inode, dir, name),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-06-21 02:35:32 +07:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
nfs_fattr_init(data->res.fattr);
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
|
|
|
|
{
|
2011-03-25 00:12:24 +07:00
|
|
|
int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
|
2011-03-25 00:12:23 +07:00
|
|
|
&data->arg.seq_args, &data->res.seq_res, 1);
|
2008-06-21 02:35:32 +07:00
|
|
|
if (status == 0) {
|
|
|
|
update_changeattr(dir, &data->res.dir_cinfo);
|
|
|
|
status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_free_createdata(struct nfs4_createdata *data)
|
|
|
|
{
|
|
|
|
kfree(data);
|
|
|
|
}
|
|
|
|
|
2006-08-23 07:06:22 +07:00
|
|
|
static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
|
2006-08-23 07:06:23 +07:00
|
|
|
struct page *page, unsigned int len, struct iattr *sattr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-06-21 02:35:32 +07:00
|
|
|
struct nfs4_createdata *data;
|
|
|
|
int status = -ENAMETOOLONG;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-08-23 07:06:23 +07:00
|
|
|
if (len > NFS4_MAXPATHLEN)
|
2008-06-21 02:35:32 +07:00
|
|
|
goto out;
|
2006-08-23 07:06:22 +07:00
|
|
|
|
2008-06-21 02:35:32 +07:00
|
|
|
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;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-21 02:35:32 +07:00
|
|
|
status = nfs4_do_create(dir, dentry, data);
|
|
|
|
|
|
|
|
nfs4_free_createdata(data);
|
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-08-23 07:06:22 +07:00
|
|
|
static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
|
2006-08-23 07:06:23 +07:00
|
|
|
struct page *page, unsigned int len, struct iattr *sattr)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir),
|
2006-08-23 07:06:23 +07:00
|
|
|
_nfs4_proc_symlink(dir, dentry, page,
|
|
|
|
len, sattr),
|
2005-04-17 05:20:36 +07:00
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct iattr *sattr)
|
|
|
|
{
|
2008-06-21 02:35:32 +07:00
|
|
|
struct nfs4_createdata *data;
|
|
|
|
int status = -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-21 02:35:32 +07:00
|
|
|
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:
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct iattr *sattr)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
2010-12-09 18:35:14 +07:00
|
|
|
|
|
|
|
sattr->ia_mode &= ~current_umask();
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir),
|
|
|
|
_nfs4_proc_mkdir(dir, dentry, sattr),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
2010-10-21 02:44:37 +07:00
|
|
|
u64 cookie, struct page **pages, unsigned int count, int plus)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inode *dir = dentry->d_inode;
|
|
|
|
struct nfs4_readdir_arg args = {
|
|
|
|
.fh = NFS_FH(dir),
|
2010-10-21 02:44:37 +07:00
|
|
|
.pages = pages,
|
2005-04-17 05:20:36 +07:00
|
|
|
.pgbase = 0,
|
|
|
|
.count = count,
|
2009-11-11 14:15:42 +07:00
|
|
|
.bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask,
|
NFS: Readdir plus in v4
By requsting more attributes during a readdir, we can mimic the readdir plus
operation that was in NFSv3.
To test, I ran the command `ls -lU --color=none` on directories with various
numbers of files. Without readdir plus, I see this:
n files | 100 | 1,000 | 10,000 | 100,000 | 1,000,000
--------+-----------+-----------+-----------+-----------+----------
real | 0m00.153s | 0m00.589s | 0m05.601s | 0m56.691s | 9m59.128s
user | 0m00.007s | 0m00.007s | 0m00.077s | 0m00.703s | 0m06.800s
sys | 0m00.010s | 0m00.070s | 0m00.633s | 0m06.423s | 1m10.005s
access | 3 | 1 | 1 | 4 | 31
getattr | 2 | 1 | 1 | 1 | 1
lookup | 104 | 1,003 | 10,003 | 100,003 | 1,000,003
readdir | 2 | 16 | 158 | 1,575 | 15,749
total | 111 | 1,021 | 10,163 | 101,583 | 1,015,784
With readdir plus enabled, I see this:
n files | 100 | 1,000 | 10,000 | 100,000 | 1,000,000
--------+-----------+-----------+-----------+-----------+----------
real | 0m00.115s | 0m00.206s | 0m01.079s | 0m12.521s | 2m07.528s
user | 0m00.003s | 0m00.003s | 0m00.040s | 0m00.290s | 0m03.296s
sys | 0m00.007s | 0m00.020s | 0m00.120s | 0m01.357s | 0m17.556s
access | 3 | 1 | 1 | 1 | 7
getattr | 2 | 1 | 1 | 1 | 1
lookup | 4 | 3 | 3 | 3 | 3
readdir | 6 | 62 | 630 | 6,300 | 62,993
total | 15 | 67 | 635 | 6,305 | 63,004
Readdir plus disabled has about a 16x increase in the number of rpc calls and
is 4 - 5 times slower on large directories.
Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2010-10-22 03:33:18 +07:00
|
|
|
.plus = plus,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct nfs4_readdir_res res;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
.rpc_cred = cred,
|
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: dentry = %s/%s, cookie = %Lu\n", __func__,
|
2005-06-23 00:16:39 +07:00
|
|
|
dentry->d_parent->d_name.name,
|
|
|
|
dentry->d_name.name,
|
|
|
|
(unsigned long long)cookie);
|
2012-09-04 01:56:02 +07:00
|
|
|
nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);
|
2005-04-17 05:20:36 +07:00
|
|
|
res.pgbase = args.pgbase;
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
|
2010-11-16 08:26:22 +07:00
|
|
|
if (status >= 0) {
|
2012-09-04 01:56:02 +07:00
|
|
|
memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE);
|
2010-11-16 08:26:22 +07:00
|
|
|
status += args.pgbase;
|
|
|
|
}
|
2007-09-29 04:11:45 +07:00
|
|
|
|
|
|
|
nfs_invalidate_atime(dir);
|
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: returns %d\n", __func__, status);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
|
2010-10-21 02:44:37 +07:00
|
|
|
u64 cookie, struct page **pages, unsigned int count, int plus)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
|
|
|
|
_nfs4_proc_readdir(dentry, cred, cookie,
|
2010-10-21 02:44:37 +07:00
|
|
|
pages, count, plus),
|
2005-04-17 05:20:36 +07:00
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct iattr *sattr, dev_t rdev)
|
|
|
|
{
|
2008-06-21 02:35:32 +07:00
|
|
|
struct nfs4_createdata *data;
|
|
|
|
int mode = sattr->ia_mode;
|
|
|
|
int status = -ENOMEM;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-06-21 02:35:32 +07:00
|
|
|
data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK);
|
|
|
|
if (data == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
if (S_ISFIFO(mode))
|
2008-06-21 02:35:32 +07:00
|
|
|
data->arg.ftype = NF4FIFO;
|
2005-04-17 05:20:36 +07:00
|
|
|
else if (S_ISBLK(mode)) {
|
2008-06-21 02:35:32 +07:00
|
|
|
data->arg.ftype = NF4BLK;
|
|
|
|
data->arg.u.device.specdata1 = MAJOR(rdev);
|
|
|
|
data->arg.u.device.specdata2 = MINOR(rdev);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
else if (S_ISCHR(mode)) {
|
2008-06-21 02:35:32 +07:00
|
|
|
data->arg.ftype = NF4CHR;
|
|
|
|
data->arg.u.device.specdata1 = MAJOR(rdev);
|
|
|
|
data->arg.u.device.specdata2 = MINOR(rdev);
|
2012-10-16 02:47:41 +07:00
|
|
|
} else if (!S_ISSOCK(mode)) {
|
|
|
|
status = -EINVAL;
|
|
|
|
goto out_free;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-06-21 02:35:32 +07:00
|
|
|
status = nfs4_do_create(dir, dentry, data);
|
2012-10-16 02:47:41 +07:00
|
|
|
out_free:
|
2008-06-21 02:35:32 +07:00
|
|
|
nfs4_free_createdata(data);
|
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
|
|
|
|
struct iattr *sattr, dev_t rdev)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
2010-12-09 18:35:14 +07:00
|
|
|
|
|
|
|
sattr->ia_mode &= ~current_umask();
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir),
|
|
|
|
_nfs4_proc_mknod(dir, dentry, sattr, rdev),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsstat *fsstat)
|
|
|
|
{
|
|
|
|
struct nfs4_statfs_arg args = {
|
|
|
|
.fh = fhandle,
|
|
|
|
.bitmask = server->attr_bitmask,
|
|
|
|
};
|
2009-04-01 20:21:56 +07:00
|
|
|
struct nfs4_statfs_res res = {
|
|
|
|
.fsstat = fsstat,
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS],
|
|
|
|
.rpc_argp = &args,
|
2009-04-01 20:21:56 +07:00
|
|
|
.rpc_resp = &res,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(fsstat->fattr);
|
2011-03-25 00:12:24 +07:00
|
|
|
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(server,
|
|
|
|
_nfs4_proc_statfs(server, fhandle, fsstat),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *fsinfo)
|
|
|
|
{
|
|
|
|
struct nfs4_fsinfo_arg args = {
|
|
|
|
.fh = fhandle,
|
|
|
|
.bitmask = server->attr_bitmask,
|
|
|
|
};
|
2009-04-01 20:21:57 +07:00
|
|
|
struct nfs4_fsinfo_res res = {
|
|
|
|
.fsinfo = fsinfo,
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO],
|
|
|
|
.rpc_argp = &args,
|
2009-04-01 20:21:57 +07:00
|
|
|
.rpc_resp = &res,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2011-03-25 00:12:24 +07:00
|
|
|
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
2013-03-17 02:55:53 +07:00
|
|
|
unsigned long now = jiffies;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
2013-03-17 02:55:53 +07:00
|
|
|
err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
|
|
|
|
if (err == 0) {
|
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
|
|
|
|
spin_lock(&clp->cl_lock);
|
|
|
|
clp->cl_lease_time = fsinfo->lease_time * HZ;
|
|
|
|
clp->cl_last_renewal = now;
|
|
|
|
spin_unlock(&clp->cl_lock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
err = nfs4_handle_exception(server, err, &exception);
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
|
|
|
|
{
|
2012-06-21 02:53:40 +07:00
|
|
|
int error;
|
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(fsinfo->fattr);
|
2012-06-21 02:53:40 +07:00
|
|
|
error = nfs4_do_fsinfo(server, fhandle, fsinfo);
|
2012-08-23 23:27:49 +07:00
|
|
|
if (error == 0) {
|
|
|
|
/* block layout checks this! */
|
|
|
|
server->pnfs_blksize = fsinfo->blksize;
|
2012-06-21 02:53:40 +07:00
|
|
|
set_pnfs_layoutdriver(server, fhandle, fsinfo->layouttype);
|
2012-08-23 23:27:49 +07:00
|
|
|
}
|
2012-06-21 02:53:40 +07:00
|
|
|
|
|
|
|
return error;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_pathconf *pathconf)
|
|
|
|
{
|
|
|
|
struct nfs4_pathconf_arg args = {
|
|
|
|
.fh = fhandle,
|
|
|
|
.bitmask = server->attr_bitmask,
|
|
|
|
};
|
2009-04-01 20:21:58 +07:00
|
|
|
struct nfs4_pathconf_res res = {
|
|
|
|
.pathconf = pathconf,
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
|
|
|
|
.rpc_argp = &args,
|
2009-04-01 20:21:58 +07:00
|
|
|
.rpc_resp = &res,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
/* None of the pathconf attributes are mandatory to implement */
|
|
|
|
if ((args.bitmask[0] & nfs4_pathconf_bitmap[0]) == 0) {
|
|
|
|
memset(pathconf, 0, sizeof(*pathconf));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-10-28 09:12:38 +07:00
|
|
|
nfs_fattr_init(pathconf->fattr);
|
2011-03-25 00:12:24 +07:00
|
|
|
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_pathconf *pathconf)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(server,
|
|
|
|
_nfs4_proc_pathconf(server, fhandle, pathconf),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-03-17 07:54:34 +07:00
|
|
|
int nfs4_set_rw_stateid(nfs4_stateid *stateid,
|
2013-03-18 02:52:00 +07:00
|
|
|
const struct nfs_open_context *ctx,
|
|
|
|
const struct nfs_lock_context *l_ctx,
|
|
|
|
fmode_t fmode)
|
|
|
|
{
|
|
|
|
const struct nfs_lockowner *lockowner = NULL;
|
|
|
|
|
|
|
|
if (l_ctx != NULL)
|
|
|
|
lockowner = &l_ctx->lockowner;
|
2013-03-17 07:54:34 +07:00
|
|
|
return nfs4_select_rw_stateid(stateid, ctx->state, fmode, lockowner);
|
2013-03-18 02:52:00 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs4_set_rw_stateid);
|
|
|
|
|
2013-03-17 07:54:34 +07:00
|
|
|
static bool nfs4_stateid_is_current(nfs4_stateid *stateid,
|
|
|
|
const struct nfs_open_context *ctx,
|
|
|
|
const struct nfs_lock_context *l_ctx,
|
|
|
|
fmode_t fmode)
|
|
|
|
{
|
|
|
|
nfs4_stateid current_stateid;
|
|
|
|
|
|
|
|
if (nfs4_set_rw_stateid(¤t_stateid, ctx, l_ctx, fmode))
|
|
|
|
return false;
|
|
|
|
return nfs4_stateid_match(stateid, ¤t_stateid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nfs4_error_stateid_expired(int err)
|
|
|
|
{
|
|
|
|
switch (err) {
|
|
|
|
case -NFS4ERR_DELEG_REVOKED:
|
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_OLD_STATEID:
|
|
|
|
case -NFS4ERR_OPENMODE:
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-05-22 23:52:03 +07:00
|
|
|
void __nfs4_read_done_cb(struct nfs_read_data *data)
|
|
|
|
{
|
2012-04-21 01:47:44 +07:00
|
|
|
nfs_invalidate_atime(data->header->inode);
|
2011-05-22 23:52:03 +07:00
|
|
|
}
|
|
|
|
|
2011-03-01 08:34:20 +07:00
|
|
|
static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-04-21 01:47:44 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(data->header->inode);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-12-24 03:21:46 +07:00
|
|
|
if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2006-03-21 01:44:27 +07:00
|
|
|
return -EAGAIN;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-09-29 04:20:07 +07:00
|
|
|
|
2011-05-22 23:52:03 +07:00
|
|
|
__nfs4_read_done_cb(data);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (task->tk_status > 0)
|
2006-03-21 01:44:27 +07:00
|
|
|
renew_lease(server, data->timestamp);
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2013-03-17 07:54:34 +07:00
|
|
|
static bool nfs4_read_stateid_changed(struct rpc_task *task,
|
|
|
|
struct nfs_readargs *args)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!nfs4_error_stateid_expired(task->tk_status) ||
|
|
|
|
nfs4_stateid_is_current(&args->stateid,
|
|
|
|
args->context,
|
|
|
|
args->lock_context,
|
|
|
|
FMODE_READ))
|
|
|
|
return false;
|
|
|
|
rpc_restart_call_prepare(task);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-03-01 08:34:20 +07:00
|
|
|
static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
|
|
|
|
{
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
|
|
|
|
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
|
|
|
return -EAGAIN;
|
2013-03-17 07:54:34 +07:00
|
|
|
if (nfs4_read_stateid_changed(task, &data->args))
|
|
|
|
return -EAGAIN;
|
2011-05-22 23:52:03 +07:00
|
|
|
return data->read_done_cb ? data->read_done_cb(task, data) :
|
|
|
|
nfs4_read_done_cb(task, data);
|
2011-03-01 08:34:20 +07:00
|
|
|
}
|
|
|
|
|
2007-07-15 02:40:00 +07:00
|
|
|
static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message *msg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
data->timestamp = jiffies;
|
2011-03-01 08:34:20 +07:00
|
|
|
data->read_done_cb = nfs4_read_done_cb;
|
2007-07-15 02:40:00 +07:00
|
|
|
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-03-20 01:54:40 +07:00
|
|
|
static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
|
|
|
|
{
|
2013-03-18 02:52:00 +07:00
|
|
|
if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
|
2012-10-23 07:28:44 +07:00
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
2013-03-18 02:52:00 +07:00
|
|
|
task))
|
|
|
|
return;
|
|
|
|
nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
|
|
|
|
data->args.lock_context, FMODE_READ);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-03-03 22:13:42 +07:00
|
|
|
static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-04-21 01:47:44 +07:00
|
|
|
struct inode *inode = data->header->inode;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-12-24 03:21:46 +07:00
|
|
|
if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2006-03-21 01:44:27 +07:00
|
|
|
return -EAGAIN;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-10-28 09:12:44 +07:00
|
|
|
if (task->tk_status >= 0) {
|
2005-04-17 05:20:36 +07:00
|
|
|
renew_lease(NFS_SERVER(inode), data->timestamp);
|
2012-04-29 01:55:16 +07:00
|
|
|
nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
|
2005-10-28 09:12:44 +07:00
|
|
|
}
|
2006-03-21 01:44:27 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2013-03-17 07:54:34 +07:00
|
|
|
static bool nfs4_write_stateid_changed(struct rpc_task *task,
|
|
|
|
struct nfs_writeargs *args)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!nfs4_error_stateid_expired(task->tk_status) ||
|
|
|
|
nfs4_stateid_is_current(&args->stateid,
|
|
|
|
args->context,
|
|
|
|
args->lock_context,
|
|
|
|
FMODE_WRITE))
|
|
|
|
return false;
|
|
|
|
rpc_restart_call_prepare(task);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-03-03 22:13:42 +07:00
|
|
|
static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
|
|
|
|
{
|
|
|
|
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
|
|
|
return -EAGAIN;
|
2013-03-17 07:54:34 +07:00
|
|
|
if (nfs4_write_stateid_changed(task, &data->args))
|
|
|
|
return -EAGAIN;
|
2011-05-22 23:52:03 +07:00
|
|
|
return data->write_done_cb ? data->write_done_cb(task, data) :
|
|
|
|
nfs4_write_done_cb(task, data);
|
2011-03-03 22:13:42 +07:00
|
|
|
}
|
|
|
|
|
2012-04-29 01:55:16 +07:00
|
|
|
static
|
|
|
|
bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data)
|
2011-03-03 22:13:47 +07:00
|
|
|
{
|
2012-04-29 01:55:16 +07:00
|
|
|
const struct nfs_pgio_header *hdr = data->header;
|
|
|
|
|
|
|
|
/* Don't request attributes for pNFS or O_DIRECT writes */
|
|
|
|
if (data->ds_clp != NULL || hdr->dreq != NULL)
|
|
|
|
return false;
|
|
|
|
/* Otherwise, request attributes if and only if we don't hold
|
|
|
|
* a delegation
|
|
|
|
*/
|
2012-06-21 02:53:43 +07:00
|
|
|
return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;
|
2011-03-03 22:13:47 +07:00
|
|
|
}
|
|
|
|
|
2007-07-15 02:40:00 +07:00
|
|
|
static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-04-21 01:47:44 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(data->header->inode);
|
2007-07-15 02:40:00 +07:00
|
|
|
|
2012-04-29 01:55:16 +07:00
|
|
|
if (!nfs4_write_need_cache_consistency_data(data)) {
|
2011-03-03 22:13:46 +07:00
|
|
|
data->args.bitmask = NULL;
|
|
|
|
data->res.fattr = NULL;
|
|
|
|
} else
|
|
|
|
data->args.bitmask = server->cache_consistency_bitmask;
|
2012-04-29 01:55:16 +07:00
|
|
|
|
2011-03-03 22:13:42 +07:00
|
|
|
if (!data->write_done_cb)
|
|
|
|
data->write_done_cb = nfs4_write_done_cb;
|
2005-10-28 09:12:44 +07:00
|
|
|
data->res.server = server;
|
2005-04-17 05:20:36 +07:00
|
|
|
data->timestamp = jiffies;
|
|
|
|
|
2007-07-15 02:40:00 +07:00
|
|
|
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-03-20 01:54:39 +07:00
|
|
|
static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
|
|
|
|
{
|
2013-03-18 02:52:00 +07:00
|
|
|
if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
|
2012-10-23 07:28:44 +07:00
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
2013-03-18 02:52:00 +07:00
|
|
|
task))
|
|
|
|
return;
|
|
|
|
nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
|
|
|
|
data->args.lock_context, FMODE_WRITE);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-04-21 01:47:39 +07:00
|
|
|
static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
|
2012-03-20 01:54:39 +07:00
|
|
|
{
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs4_setup_sequence(NFS_SERVER(data->inode),
|
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
|
|
|
task);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-04-21 01:47:39 +07:00
|
|
|
static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_commit_data *data)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct inode *inode = data->inode;
|
2010-08-01 01:29:06 +07:00
|
|
|
|
2008-12-24 03:21:46 +07:00
|
|
|
if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2006-03-21 01:44:27 +07:00
|
|
|
return -EAGAIN;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-03-21 01:44:27 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-04-21 01:47:39 +07:00
|
|
|
static int nfs4_commit_done(struct rpc_task *task, struct nfs_commit_data *data)
|
2011-03-23 20:27:46 +07:00
|
|
|
{
|
|
|
|
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
|
|
|
return -EAGAIN;
|
2012-04-21 01:47:39 +07:00
|
|
|
return data->commit_done_cb(task, data);
|
2011-03-23 20:27:46 +07:00
|
|
|
}
|
|
|
|
|
2012-04-21 01:47:39 +07:00
|
|
|
static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-03-21 01:44:27 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(data->inode);
|
2011-03-23 20:27:52 +07:00
|
|
|
|
2012-04-21 01:47:39 +07:00
|
|
|
if (data->commit_done_cb == NULL)
|
|
|
|
data->commit_done_cb = nfs4_commit_done_cb;
|
2005-10-28 09:12:44 +07:00
|
|
|
data->res.server = server;
|
2007-07-15 02:40:00 +07:00
|
|
|
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-05-08 00:34:17 +07:00
|
|
|
struct nfs4_renewdata {
|
|
|
|
struct nfs_client *client;
|
|
|
|
unsigned long timestamp;
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special
|
|
|
|
* standalone procedure for queueing an asynchronous RENEW.
|
|
|
|
*/
|
2010-05-08 00:34:17 +07:00
|
|
|
static void nfs4_renew_release(void *calldata)
|
2010-02-05 18:45:04 +07:00
|
|
|
{
|
2010-05-08 00:34:17 +07:00
|
|
|
struct nfs4_renewdata *data = calldata;
|
|
|
|
struct nfs_client *clp = data->client;
|
2010-02-05 18:45:04 +07:00
|
|
|
|
2010-02-05 18:45:06 +07:00
|
|
|
if (atomic_read(&clp->cl_count) > 1)
|
|
|
|
nfs4_schedule_state_renewal(clp);
|
|
|
|
nfs_put_client(clp);
|
2010-05-08 00:34:17 +07:00
|
|
|
kfree(data);
|
2010-02-05 18:45:04 +07:00
|
|
|
}
|
|
|
|
|
2010-05-08 00:34:17 +07:00
|
|
|
static void nfs4_renew_done(struct rpc_task *task, void *calldata)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-05-08 00:34:17 +07:00
|
|
|
struct nfs4_renewdata *data = calldata;
|
|
|
|
struct nfs_client *clp = data->client;
|
|
|
|
unsigned long timestamp = data->timestamp;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (task->tk_status < 0) {
|
2009-05-27 01:51:00 +07:00
|
|
|
/* Unless we're shutting down, schedule state recovery! */
|
2011-08-25 02:07:37 +07:00
|
|
|
if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0)
|
|
|
|
return;
|
|
|
|
if (task->tk_status != NFS4ERR_CB_PATH_DOWN) {
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_lease_recovery(clp);
|
2011-08-25 02:07:37 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
nfs4_schedule_path_down_recovery(clp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2010-08-01 01:29:06 +07:00
|
|
|
do_renew_lease(clp, timestamp);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:04 +07:00
|
|
|
static const struct rpc_call_ops nfs4_renew_ops = {
|
|
|
|
.rpc_call_done = nfs4_renew_done,
|
2010-02-05 18:45:04 +07:00
|
|
|
.rpc_release = nfs4_renew_release,
|
2006-01-03 15:55:04 +07:00
|
|
|
};
|
|
|
|
|
2011-08-25 02:07:37 +07:00
|
|
|
static int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
|
|
|
|
.rpc_argp = clp,
|
2006-01-03 15:55:25 +07:00
|
|
|
.rpc_cred = cred,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2010-05-08 00:34:17 +07:00
|
|
|
struct nfs4_renewdata *data;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-08-25 02:07:37 +07:00
|
|
|
if (renew_flags == 0)
|
|
|
|
return 0;
|
2010-02-05 18:45:06 +07:00
|
|
|
if (!atomic_inc_not_zero(&clp->cl_count))
|
|
|
|
return -EIO;
|
2011-08-25 02:07:35 +07:00
|
|
|
data = kmalloc(sizeof(*data), GFP_NOFS);
|
2010-05-08 00:34:17 +07:00
|
|
|
if (data == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
data->client = clp;
|
|
|
|
data->timestamp = jiffies;
|
2013-04-09 04:50:28 +07:00
|
|
|
return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT,
|
2010-05-08 00:34:17 +07:00
|
|
|
&nfs4_renew_ops, data);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-08-25 02:07:37 +07:00
|
|
|
static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
|
|
|
|
.rpc_argp = clp,
|
2006-01-03 15:55:25 +07:00
|
|
|
.rpc_cred = cred,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
unsigned long now = jiffies;
|
|
|
|
int status;
|
|
|
|
|
2013-04-09 04:50:28 +07:00
|
|
|
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (status < 0)
|
|
|
|
return status;
|
2010-08-01 01:29:06 +07:00
|
|
|
do_renew_lease(clp, now);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-06-23 00:16:22 +07:00
|
|
|
static inline int nfs4_server_supports_acls(struct nfs_server *server)
|
|
|
|
{
|
|
|
|
return (server->caps & NFS_CAP_ACLS)
|
|
|
|
&& (server->acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
|
|
|
|
&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
|
|
|
|
}
|
|
|
|
|
2012-08-24 21:59:25 +07:00
|
|
|
/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
|
|
|
|
* it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
|
2005-06-23 00:16:22 +07:00
|
|
|
* the stack.
|
|
|
|
*/
|
2012-08-24 21:59:25 +07:00
|
|
|
#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
|
2005-06-23 00:16:22 +07:00
|
|
|
|
nfs4: Ensure that ACL pages sent over NFS were not allocated from the slab (v3)
The "bad_page()" page allocator sanity check was reported recently (call
chain as follows):
bad_page+0x69/0x91
free_hot_cold_page+0x81/0x144
skb_release_data+0x5f/0x98
__kfree_skb+0x11/0x1a
tcp_ack+0x6a3/0x1868
tcp_rcv_established+0x7a6/0x8b9
tcp_v4_do_rcv+0x2a/0x2fa
tcp_v4_rcv+0x9a2/0x9f6
do_timer+0x2df/0x52c
ip_local_deliver+0x19d/0x263
ip_rcv+0x539/0x57c
netif_receive_skb+0x470/0x49f
:virtio_net:virtnet_poll+0x46b/0x5c5
net_rx_action+0xac/0x1b3
__do_softirq+0x89/0x133
call_softirq+0x1c/0x28
do_softirq+0x2c/0x7d
do_IRQ+0xec/0xf5
default_idle+0x0/0x50
ret_from_intr+0x0/0xa
default_idle+0x29/0x50
cpu_idle+0x95/0xb8
start_kernel+0x220/0x225
_sinittext+0x22f/0x236
It occurs because an skb with a fraglist was freed from the tcp
retransmit queue when it was acked, but a page on that fraglist had
PG_Slab set (indicating it was allocated from the Slab allocator (which
means the free path above can't safely free it via put_page.
We tracked this back to an nfsv4 setacl operation, in which the nfs code
attempted to fill convert the passed in buffer to an array of pages in
__nfs4_proc_set_acl, which gets used by the skb->frags list in
xs_sendpages. __nfs4_proc_set_acl just converts each page in the buffer
to a page struct via virt_to_page, but the vfs allocates the buffer via
kmalloc, meaning the PG_slab bit is set. We can't create a buffer with
kmalloc and free it later in the tcp ack path with put_page, so we need
to either:
1) ensure that when we create the list of pages, no page struct has
PG_Slab set
or
2) not use a page list to send this data
Given that these buffers can be multiple pages and arbitrarily sized, I
think (1) is the right way to go. I've written the below patch to
allocate a page from the buddy allocator directly and copy the data over
to it. This ensures that we have a put_page free-able page for every
entry that winds up on an skb frag list, so it can be safely freed when
the frame is acked. We do a put page on each entry after the
rpc_call_sync call so as to drop our own reference count to the page,
leaving only the ref count taken by tcp_sendpages. This way the data
will be properly freed when the ack comes in
Successfully tested by myself to solve the above oops.
Note, as this is the result of a setacl operation that exceeded a page
of data, I think this amounts to a local DOS triggerable by an
uprivlidged user, so I'm CCing security on this as well.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
CC: security@kernel.org
CC: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-05 07:26:03 +07:00
|
|
|
static int buf_to_pages_noslab(const void *buf, size_t buflen,
|
|
|
|
struct page **pages, unsigned int *pgbase)
|
|
|
|
{
|
|
|
|
struct page *newpage, **spages;
|
|
|
|
int rc = 0;
|
|
|
|
size_t len;
|
|
|
|
spages = pages;
|
|
|
|
|
|
|
|
do {
|
2012-08-24 21:59:25 +07:00
|
|
|
len = min_t(size_t, PAGE_SIZE, buflen);
|
nfs4: Ensure that ACL pages sent over NFS were not allocated from the slab (v3)
The "bad_page()" page allocator sanity check was reported recently (call
chain as follows):
bad_page+0x69/0x91
free_hot_cold_page+0x81/0x144
skb_release_data+0x5f/0x98
__kfree_skb+0x11/0x1a
tcp_ack+0x6a3/0x1868
tcp_rcv_established+0x7a6/0x8b9
tcp_v4_do_rcv+0x2a/0x2fa
tcp_v4_rcv+0x9a2/0x9f6
do_timer+0x2df/0x52c
ip_local_deliver+0x19d/0x263
ip_rcv+0x539/0x57c
netif_receive_skb+0x470/0x49f
:virtio_net:virtnet_poll+0x46b/0x5c5
net_rx_action+0xac/0x1b3
__do_softirq+0x89/0x133
call_softirq+0x1c/0x28
do_softirq+0x2c/0x7d
do_IRQ+0xec/0xf5
default_idle+0x0/0x50
ret_from_intr+0x0/0xa
default_idle+0x29/0x50
cpu_idle+0x95/0xb8
start_kernel+0x220/0x225
_sinittext+0x22f/0x236
It occurs because an skb with a fraglist was freed from the tcp
retransmit queue when it was acked, but a page on that fraglist had
PG_Slab set (indicating it was allocated from the Slab allocator (which
means the free path above can't safely free it via put_page.
We tracked this back to an nfsv4 setacl operation, in which the nfs code
attempted to fill convert the passed in buffer to an array of pages in
__nfs4_proc_set_acl, which gets used by the skb->frags list in
xs_sendpages. __nfs4_proc_set_acl just converts each page in the buffer
to a page struct via virt_to_page, but the vfs allocates the buffer via
kmalloc, meaning the PG_slab bit is set. We can't create a buffer with
kmalloc and free it later in the tcp ack path with put_page, so we need
to either:
1) ensure that when we create the list of pages, no page struct has
PG_Slab set
or
2) not use a page list to send this data
Given that these buffers can be multiple pages and arbitrarily sized, I
think (1) is the right way to go. I've written the below patch to
allocate a page from the buddy allocator directly and copy the data over
to it. This ensures that we have a put_page free-able page for every
entry that winds up on an skb frag list, so it can be safely freed when
the frame is acked. We do a put page on each entry after the
rpc_call_sync call so as to drop our own reference count to the page,
leaving only the ref count taken by tcp_sendpages. This way the data
will be properly freed when the ack comes in
Successfully tested by myself to solve the above oops.
Note, as this is the result of a setacl operation that exceeded a page
of data, I think this amounts to a local DOS triggerable by an
uprivlidged user, so I'm CCing security on this as well.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
CC: security@kernel.org
CC: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-05 07:26:03 +07:00
|
|
|
newpage = alloc_page(GFP_KERNEL);
|
|
|
|
|
|
|
|
if (newpage == NULL)
|
|
|
|
goto unwind;
|
|
|
|
memcpy(page_address(newpage), buf, len);
|
|
|
|
buf += len;
|
|
|
|
buflen -= len;
|
|
|
|
*pages++ = newpage;
|
|
|
|
rc++;
|
|
|
|
} while (buflen != 0);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
unwind:
|
|
|
|
for(; rc > 0; rc--)
|
|
|
|
__free_page(spages[rc-1]);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2005-06-23 00:16:23 +07:00
|
|
|
struct nfs4_cached_acl {
|
|
|
|
int cached;
|
|
|
|
size_t len;
|
2005-06-23 00:16:28 +07:00
|
|
|
char data[0];
|
2005-06-23 00:16:23 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
kfree(nfsi->nfs4_acl);
|
|
|
|
nfsi->nfs4_acl = acl;
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_zap_acl_attr(struct inode *inode)
|
|
|
|
{
|
|
|
|
nfs4_set_cached_acl(inode, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
|
|
|
struct nfs4_cached_acl *acl;
|
|
|
|
int ret = -ENOENT;
|
|
|
|
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
acl = nfsi->nfs4_acl;
|
|
|
|
if (acl == NULL)
|
|
|
|
goto out;
|
|
|
|
if (buf == NULL) /* user is just asking for length */
|
|
|
|
goto out_len;
|
|
|
|
if (acl->cached == 0)
|
|
|
|
goto out;
|
|
|
|
ret = -ERANGE; /* see getxattr(2) man page */
|
|
|
|
if (acl->len > buflen)
|
|
|
|
goto out;
|
|
|
|
memcpy(buf, acl->data, acl->len);
|
|
|
|
out_len:
|
|
|
|
ret = acl->len;
|
|
|
|
out:
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-17 20:36:40 +07:00
|
|
|
static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
|
2005-06-23 00:16:23 +07:00
|
|
|
{
|
|
|
|
struct nfs4_cached_acl *acl;
|
2012-08-15 05:30:41 +07:00
|
|
|
size_t buflen = sizeof(*acl) + acl_len;
|
2005-06-23 00:16:23 +07:00
|
|
|
|
2012-08-27 01:44:43 +07:00
|
|
|
if (buflen <= PAGE_SIZE) {
|
2012-08-15 05:30:41 +07:00
|
|
|
acl = kmalloc(buflen, GFP_KERNEL);
|
2005-06-23 00:16:23 +07:00
|
|
|
if (acl == NULL)
|
|
|
|
goto out;
|
|
|
|
acl->cached = 1;
|
2012-04-17 20:36:40 +07:00
|
|
|
_copy_from_pages(acl->data, pages, pgbase, acl_len);
|
2005-06-23 00:16:23 +07:00
|
|
|
} else {
|
|
|
|
acl = kmalloc(sizeof(*acl), GFP_KERNEL);
|
|
|
|
if (acl == NULL)
|
|
|
|
goto out;
|
|
|
|
acl->cached = 0;
|
|
|
|
}
|
|
|
|
acl->len = acl_len;
|
|
|
|
out:
|
|
|
|
nfs4_set_cached_acl(inode, acl);
|
|
|
|
}
|
|
|
|
|
2011-12-07 23:55:27 +07:00
|
|
|
/*
|
|
|
|
* The getxattr API returns the required buffer length when called with a
|
|
|
|
* NULL buf. The NFSv4 acl tool then calls getxattr again after allocating
|
|
|
|
* the required buf. On a NULL buf, we send a page of data to the server
|
|
|
|
* guessing that the ACL request can be serviced by a page. If so, we cache
|
|
|
|
* up to the page of ACL data, and the 2nd call to getxattr is serviced by
|
|
|
|
* the cache. If not so, we throw away the page, and cache the required
|
|
|
|
* length. The next getxattr call will then produce another round trip to
|
|
|
|
* the server, this time with the input buf of the required size.
|
|
|
|
*/
|
2006-08-24 23:27:15 +07:00
|
|
|
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
|
2005-06-23 00:16:22 +07:00
|
|
|
{
|
2011-12-07 23:55:27 +07:00
|
|
|
struct page *pages[NFS4ACL_MAXPAGES] = {NULL, };
|
2005-06-23 00:16:22 +07:00
|
|
|
struct nfs_getaclargs args = {
|
|
|
|
.fh = NFS_FH(inode),
|
|
|
|
.acl_pages = pages,
|
|
|
|
.acl_len = buflen,
|
|
|
|
};
|
2009-04-01 20:21:59 +07:00
|
|
|
struct nfs_getaclres res = {
|
|
|
|
.acl_len = buflen,
|
|
|
|
};
|
2005-06-23 00:16:22 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
|
|
|
|
.rpc_argp = &args,
|
2009-04-01 20:21:59 +07:00
|
|
|
.rpc_resp = &res,
|
2005-06-23 00:16:22 +07:00
|
|
|
};
|
2012-08-24 21:59:25 +07:00
|
|
|
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
|
|
|
|
int ret = -ENOMEM, i;
|
2005-06-23 00:16:22 +07:00
|
|
|
|
2011-12-07 23:55:27 +07:00
|
|
|
/* As long as we're doing a round trip to the server anyway,
|
|
|
|
* let's be prepared for a page of acl data. */
|
|
|
|
if (npages == 0)
|
|
|
|
npages = 1;
|
2012-08-24 21:59:25 +07:00
|
|
|
if (npages > ARRAY_SIZE(pages))
|
|
|
|
return -ERANGE;
|
2012-04-17 20:35:39 +07:00
|
|
|
|
2011-12-07 23:55:27 +07:00
|
|
|
for (i = 0; i < npages; i++) {
|
|
|
|
pages[i] = alloc_page(GFP_KERNEL);
|
|
|
|
if (!pages[i])
|
|
|
|
goto out_free;
|
2005-06-23 00:16:23 +07:00
|
|
|
}
|
2012-04-17 20:35:39 +07:00
|
|
|
|
|
|
|
/* for decoding across pages */
|
|
|
|
res.acl_scratch = alloc_page(GFP_KERNEL);
|
|
|
|
if (!res.acl_scratch)
|
|
|
|
goto out_free;
|
|
|
|
|
2011-12-07 23:55:27 +07:00
|
|
|
args.acl_len = npages * PAGE_SIZE;
|
|
|
|
args.acl_pgbase = 0;
|
2012-04-17 20:35:39 +07:00
|
|
|
|
2012-01-10 21:42:47 +07:00
|
|
|
dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n",
|
2011-12-07 23:55:27 +07:00
|
|
|
__func__, buf, buflen, npages, args.acl_len);
|
|
|
|
ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
|
|
|
|
&msg, &args.seq_args, &res.seq_res, 0);
|
2005-06-23 00:16:23 +07:00
|
|
|
if (ret)
|
|
|
|
goto out_free;
|
2011-12-07 23:55:27 +07:00
|
|
|
|
2012-08-27 01:44:43 +07:00
|
|
|
/* Handle the case where the passed-in buffer is too short */
|
|
|
|
if (res.acl_flags & NFS4_ACL_TRUNC) {
|
|
|
|
/* Did the user only issue a request for the acl length? */
|
|
|
|
if (buf == NULL)
|
|
|
|
goto out_ok;
|
2005-06-23 00:16:23 +07:00
|
|
|
ret = -ERANGE;
|
2012-08-27 01:44:43 +07:00
|
|
|
goto out_free;
|
2005-06-23 00:16:23 +07:00
|
|
|
}
|
2012-08-27 01:44:43 +07:00
|
|
|
nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
|
2012-12-08 21:30:18 +07:00
|
|
|
if (buf) {
|
|
|
|
if (res.acl_len > buflen) {
|
|
|
|
ret = -ERANGE;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2012-08-27 01:44:43 +07:00
|
|
|
_copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
|
2012-12-08 21:30:18 +07:00
|
|
|
}
|
2012-08-27 01:44:43 +07:00
|
|
|
out_ok:
|
|
|
|
ret = res.acl_len;
|
2005-06-23 00:16:23 +07:00
|
|
|
out_free:
|
2011-12-07 23:55:27 +07:00
|
|
|
for (i = 0; i < npages; i++)
|
|
|
|
if (pages[i])
|
|
|
|
__free_page(pages[i]);
|
2012-02-04 06:30:53 +07:00
|
|
|
if (res.acl_scratch)
|
|
|
|
__free_page(res.acl_scratch);
|
2005-06-23 00:16:22 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-08-24 23:27:15 +07:00
|
|
|
static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
ssize_t ret;
|
|
|
|
do {
|
|
|
|
ret = __nfs4_get_acl_uncached(inode, buf, buflen);
|
|
|
|
if (ret >= 0)
|
|
|
|
break;
|
|
|
|
ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-06-23 00:16:23 +07:00
|
|
|
static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!nfs4_server_supports_acls(server))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
ret = nfs_revalidate_inode(server, inode);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2010-12-01 17:42:16 +07:00
|
|
|
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
|
|
|
|
nfs_zap_acl_cache(inode);
|
2005-06-23 00:16:23 +07:00
|
|
|
ret = nfs4_read_cached_acl(inode, buf, buflen);
|
|
|
|
if (ret != -ENOENT)
|
2011-12-07 23:55:27 +07:00
|
|
|
/* -ENOENT is returned if there is no ACL or if there is an ACL
|
|
|
|
* but no cached acl data, just the acl length */
|
2005-06-23 00:16:23 +07:00
|
|
|
return ret;
|
|
|
|
return nfs4_get_acl_uncached(inode, buf, buflen);
|
|
|
|
}
|
|
|
|
|
2006-08-24 23:27:15 +07:00
|
|
|
static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
|
2005-06-23 00:16:23 +07:00
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
|
struct page *pages[NFS4ACL_MAXPAGES];
|
|
|
|
struct nfs_setaclargs arg = {
|
|
|
|
.fh = NFS_FH(inode),
|
|
|
|
.acl_pages = pages,
|
|
|
|
.acl_len = buflen,
|
|
|
|
};
|
2009-04-01 20:22:01 +07:00
|
|
|
struct nfs_setaclres res;
|
2005-06-23 00:16:23 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETACL],
|
|
|
|
.rpc_argp = &arg,
|
2009-04-01 20:22:01 +07:00
|
|
|
.rpc_resp = &res,
|
2005-06-23 00:16:23 +07:00
|
|
|
};
|
2012-08-24 21:59:25 +07:00
|
|
|
unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
|
nfs4: Ensure that ACL pages sent over NFS were not allocated from the slab (v3)
The "bad_page()" page allocator sanity check was reported recently (call
chain as follows):
bad_page+0x69/0x91
free_hot_cold_page+0x81/0x144
skb_release_data+0x5f/0x98
__kfree_skb+0x11/0x1a
tcp_ack+0x6a3/0x1868
tcp_rcv_established+0x7a6/0x8b9
tcp_v4_do_rcv+0x2a/0x2fa
tcp_v4_rcv+0x9a2/0x9f6
do_timer+0x2df/0x52c
ip_local_deliver+0x19d/0x263
ip_rcv+0x539/0x57c
netif_receive_skb+0x470/0x49f
:virtio_net:virtnet_poll+0x46b/0x5c5
net_rx_action+0xac/0x1b3
__do_softirq+0x89/0x133
call_softirq+0x1c/0x28
do_softirq+0x2c/0x7d
do_IRQ+0xec/0xf5
default_idle+0x0/0x50
ret_from_intr+0x0/0xa
default_idle+0x29/0x50
cpu_idle+0x95/0xb8
start_kernel+0x220/0x225
_sinittext+0x22f/0x236
It occurs because an skb with a fraglist was freed from the tcp
retransmit queue when it was acked, but a page on that fraglist had
PG_Slab set (indicating it was allocated from the Slab allocator (which
means the free path above can't safely free it via put_page.
We tracked this back to an nfsv4 setacl operation, in which the nfs code
attempted to fill convert the passed in buffer to an array of pages in
__nfs4_proc_set_acl, which gets used by the skb->frags list in
xs_sendpages. __nfs4_proc_set_acl just converts each page in the buffer
to a page struct via virt_to_page, but the vfs allocates the buffer via
kmalloc, meaning the PG_slab bit is set. We can't create a buffer with
kmalloc and free it later in the tcp ack path with put_page, so we need
to either:
1) ensure that when we create the list of pages, no page struct has
PG_Slab set
or
2) not use a page list to send this data
Given that these buffers can be multiple pages and arbitrarily sized, I
think (1) is the right way to go. I've written the below patch to
allocate a page from the buddy allocator directly and copy the data over
to it. This ensures that we have a put_page free-able page for every
entry that winds up on an skb frag list, so it can be safely freed when
the frame is acked. We do a put page on each entry after the
rpc_call_sync call so as to drop our own reference count to the page,
leaving only the ref count taken by tcp_sendpages. This way the data
will be properly freed when the ack comes in
Successfully tested by myself to solve the above oops.
Note, as this is the result of a setacl operation that exceeded a page
of data, I think this amounts to a local DOS triggerable by an
uprivlidged user, so I'm CCing security on this as well.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
CC: security@kernel.org
CC: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-05 07:26:03 +07:00
|
|
|
int ret, i;
|
2005-06-23 00:16:23 +07:00
|
|
|
|
|
|
|
if (!nfs4_server_supports_acls(server))
|
|
|
|
return -EOPNOTSUPP;
|
2012-08-24 21:59:25 +07:00
|
|
|
if (npages > ARRAY_SIZE(pages))
|
|
|
|
return -ERANGE;
|
nfs4: Ensure that ACL pages sent over NFS were not allocated from the slab (v3)
The "bad_page()" page allocator sanity check was reported recently (call
chain as follows):
bad_page+0x69/0x91
free_hot_cold_page+0x81/0x144
skb_release_data+0x5f/0x98
__kfree_skb+0x11/0x1a
tcp_ack+0x6a3/0x1868
tcp_rcv_established+0x7a6/0x8b9
tcp_v4_do_rcv+0x2a/0x2fa
tcp_v4_rcv+0x9a2/0x9f6
do_timer+0x2df/0x52c
ip_local_deliver+0x19d/0x263
ip_rcv+0x539/0x57c
netif_receive_skb+0x470/0x49f
:virtio_net:virtnet_poll+0x46b/0x5c5
net_rx_action+0xac/0x1b3
__do_softirq+0x89/0x133
call_softirq+0x1c/0x28
do_softirq+0x2c/0x7d
do_IRQ+0xec/0xf5
default_idle+0x0/0x50
ret_from_intr+0x0/0xa
default_idle+0x29/0x50
cpu_idle+0x95/0xb8
start_kernel+0x220/0x225
_sinittext+0x22f/0x236
It occurs because an skb with a fraglist was freed from the tcp
retransmit queue when it was acked, but a page on that fraglist had
PG_Slab set (indicating it was allocated from the Slab allocator (which
means the free path above can't safely free it via put_page.
We tracked this back to an nfsv4 setacl operation, in which the nfs code
attempted to fill convert the passed in buffer to an array of pages in
__nfs4_proc_set_acl, which gets used by the skb->frags list in
xs_sendpages. __nfs4_proc_set_acl just converts each page in the buffer
to a page struct via virt_to_page, but the vfs allocates the buffer via
kmalloc, meaning the PG_slab bit is set. We can't create a buffer with
kmalloc and free it later in the tcp ack path with put_page, so we need
to either:
1) ensure that when we create the list of pages, no page struct has
PG_Slab set
or
2) not use a page list to send this data
Given that these buffers can be multiple pages and arbitrarily sized, I
think (1) is the right way to go. I've written the below patch to
allocate a page from the buddy allocator directly and copy the data over
to it. This ensures that we have a put_page free-able page for every
entry that winds up on an skb frag list, so it can be safely freed when
the frame is acked. We do a put page on each entry after the
rpc_call_sync call so as to drop our own reference count to the page,
leaving only the ref count taken by tcp_sendpages. This way the data
will be properly freed when the ack comes in
Successfully tested by myself to solve the above oops.
Note, as this is the result of a setacl operation that exceeded a page
of data, I think this amounts to a local DOS triggerable by an
uprivlidged user, so I'm CCing security on this as well.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
CC: security@kernel.org
CC: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-05 07:26:03 +07:00
|
|
|
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
|
|
|
|
if (i < 0)
|
|
|
|
return i;
|
2012-06-21 02:53:44 +07:00
|
|
|
nfs4_inode_return_delegation(inode);
|
2011-03-25 00:12:24 +07:00
|
|
|
ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
nfs4: Ensure that ACL pages sent over NFS were not allocated from the slab (v3)
The "bad_page()" page allocator sanity check was reported recently (call
chain as follows):
bad_page+0x69/0x91
free_hot_cold_page+0x81/0x144
skb_release_data+0x5f/0x98
__kfree_skb+0x11/0x1a
tcp_ack+0x6a3/0x1868
tcp_rcv_established+0x7a6/0x8b9
tcp_v4_do_rcv+0x2a/0x2fa
tcp_v4_rcv+0x9a2/0x9f6
do_timer+0x2df/0x52c
ip_local_deliver+0x19d/0x263
ip_rcv+0x539/0x57c
netif_receive_skb+0x470/0x49f
:virtio_net:virtnet_poll+0x46b/0x5c5
net_rx_action+0xac/0x1b3
__do_softirq+0x89/0x133
call_softirq+0x1c/0x28
do_softirq+0x2c/0x7d
do_IRQ+0xec/0xf5
default_idle+0x0/0x50
ret_from_intr+0x0/0xa
default_idle+0x29/0x50
cpu_idle+0x95/0xb8
start_kernel+0x220/0x225
_sinittext+0x22f/0x236
It occurs because an skb with a fraglist was freed from the tcp
retransmit queue when it was acked, but a page on that fraglist had
PG_Slab set (indicating it was allocated from the Slab allocator (which
means the free path above can't safely free it via put_page.
We tracked this back to an nfsv4 setacl operation, in which the nfs code
attempted to fill convert the passed in buffer to an array of pages in
__nfs4_proc_set_acl, which gets used by the skb->frags list in
xs_sendpages. __nfs4_proc_set_acl just converts each page in the buffer
to a page struct via virt_to_page, but the vfs allocates the buffer via
kmalloc, meaning the PG_slab bit is set. We can't create a buffer with
kmalloc and free it later in the tcp ack path with put_page, so we need
to either:
1) ensure that when we create the list of pages, no page struct has
PG_Slab set
or
2) not use a page list to send this data
Given that these buffers can be multiple pages and arbitrarily sized, I
think (1) is the right way to go. I've written the below patch to
allocate a page from the buddy allocator directly and copy the data over
to it. This ensures that we have a put_page free-able page for every
entry that winds up on an skb frag list, so it can be safely freed when
the frame is acked. We do a put page on each entry after the
rpc_call_sync call so as to drop our own reference count to the page,
leaving only the ref count taken by tcp_sendpages. This way the data
will be properly freed when the ack comes in
Successfully tested by myself to solve the above oops.
Note, as this is the result of a setacl operation that exceeded a page
of data, I think this amounts to a local DOS triggerable by an
uprivlidged user, so I'm CCing security on this as well.
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Trond Myklebust <Trond.Myklebust@netapp.com>
CC: security@kernel.org
CC: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-03-05 07:26:03 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Free each page after tx, so the only ref left is
|
|
|
|
* held by the network stack
|
|
|
|
*/
|
|
|
|
for (; i > 0; i--)
|
|
|
|
put_page(pages[i-1]);
|
|
|
|
|
2010-12-01 17:42:16 +07:00
|
|
|
/*
|
|
|
|
* Acl update can result in inode attribute update.
|
|
|
|
* so mark the attribute cache invalid.
|
|
|
|
*/
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
|
|
|
|
spin_unlock(&inode->i_lock);
|
2008-06-12 04:39:04 +07:00
|
|
|
nfs_access_zap_cache(inode);
|
|
|
|
nfs_zap_acl_cache(inode);
|
2005-06-23 00:16:23 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-08-24 23:27:15 +07:00
|
|
|
static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(inode),
|
|
|
|
__nfs4_proc_set_acl(inode, buf, buflen),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int
|
2010-06-16 20:52:25 +07:00
|
|
|
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2010-06-16 20:52:25 +07:00
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
|
|
|
|
|
if (task->tk_status >= 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
switch(task->tk_status) {
|
2012-03-06 07:56:44 +07:00
|
|
|
case -NFS4ERR_DELEG_REVOKED:
|
2008-12-24 03:21:46 +07:00
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
2012-03-28 05:31:25 +07:00
|
|
|
if (state == NULL)
|
|
|
|
break;
|
|
|
|
nfs_remove_bad_delegation(state->inode);
|
2008-12-24 03:21:46 +07:00
|
|
|
case -NFS4ERR_OPENMODE:
|
|
|
|
if (state == NULL)
|
|
|
|
break;
|
2013-03-15 03:57:48 +07:00
|
|
|
if (nfs4_schedule_stateid_recovery(server, state) < 0)
|
|
|
|
goto stateid_invalid;
|
2011-03-10 04:00:53 +07:00
|
|
|
goto wait_on_recovery;
|
2011-05-27 01:26:35 +07:00
|
|
|
case -NFS4ERR_EXPIRED:
|
2013-03-15 03:57:48 +07:00
|
|
|
if (state != NULL) {
|
|
|
|
if (nfs4_schedule_stateid_recovery(server, state) < 0)
|
|
|
|
goto stateid_invalid;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_STALE_STATEID:
|
2010-01-27 03:42:47 +07:00
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_lease_recovery(clp);
|
|
|
|
goto wait_on_recovery;
|
nfs41: kick start nfs41 session recovery when handling errors
Remove checking for any errors that the SEQUENCE operation does not return.
-NFS4ERR_STALE_CLIENTID, NFS4ERR_EXPIRED, NFS4ERR_CB_PATH_DOWN, NFS4ERR_BACK_CHAN_BUSY, NFS4ERR_OP_NOT_IN_SESSION.
SEQUENCE operation error recovery is very primative, we only reset the session.
Remove checking for any errors that are returned by the SEQUENCE operation, but
that resetting the session won't address.
NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_SEQUENCE_POS,NFS4ERR_TOO_MANY_OPS.
Add error checking for missing SEQUENCE errors that a session reset will
address.
NFS4ERR_BAD_HIGH_SLOT, NFS4ERR_DEADSESSION, NFS4ERR_SEQ_FALSE_RETRY.
A reset of the session is currently our only response to a SEQUENCE operation
error. Don't reset the session on errors where a new session won't help.
Don't reset the session on errors where a new session won't help.
[nfs41: nfs4_async_handle_error update error checking]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[nfs41: trigger the state manager for session reset]
Replace session state bit with nfs_client state bit. Set the
NFS4CLNT_SESSION_SETUP bit upon a session related error in the sync/async
error handlers.
[nfs41: _nfs4_async_handle_error fix session reset error list]
Sequence operation errors that session reset could help.
NFS4ERR_BADSESSION
NFS4ERR_BADSLOT
NFS4ERR_BAD_HIGH_SLOT
NFS4ERR_DEADSESSION
NFS4ERR_CONN_NOT_BOUND_TO_SESSION
NFS4ERR_SEQ_FALSE_RETRY
NFS4ERR_SEQ_MISORDERED
Sequence operation errors that a session reset would not help
NFS4ERR_BADXDR
NFS4ERR_DELAY
NFS4ERR_REP_TOO_BIG
NFS4ERR_REP_TOO_BIG_TO_CACHE
NFS4ERR_REQ_TOO_BIG
NFS4ERR_RETRY_UNCACHED_REP
NFS4ERR_SEQUENCE_POS
NFS4ERR_TOO_MANY_OPS
Signed-off-by: Andy Adamson <andros@netapp.com>
[nfs41 nfs4_handle_exception fix session reset error list]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[moved nfs41_sequece_call_done code to nfs41: sequence operation]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-04-01 20:22:42 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
case -NFS4ERR_BADSESSION:
|
|
|
|
case -NFS4ERR_BADSLOT:
|
|
|
|
case -NFS4ERR_BAD_HIGH_SLOT:
|
|
|
|
case -NFS4ERR_DEADSESSION:
|
|
|
|
case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
|
|
|
|
case -NFS4ERR_SEQ_FALSE_RETRY:
|
|
|
|
case -NFS4ERR_SEQ_MISORDERED:
|
|
|
|
dprintk("%s ERROR %d, Reset session\n", __func__,
|
|
|
|
task->tk_status);
|
2012-05-28 00:02:53 +07:00
|
|
|
nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
|
nfs41: kick start nfs41 session recovery when handling errors
Remove checking for any errors that the SEQUENCE operation does not return.
-NFS4ERR_STALE_CLIENTID, NFS4ERR_EXPIRED, NFS4ERR_CB_PATH_DOWN, NFS4ERR_BACK_CHAN_BUSY, NFS4ERR_OP_NOT_IN_SESSION.
SEQUENCE operation error recovery is very primative, we only reset the session.
Remove checking for any errors that are returned by the SEQUENCE operation, but
that resetting the session won't address.
NFS4ERR_RETRY_UNCACHED_REP, NFS4ERR_SEQUENCE_POS,NFS4ERR_TOO_MANY_OPS.
Add error checking for missing SEQUENCE errors that a session reset will
address.
NFS4ERR_BAD_HIGH_SLOT, NFS4ERR_DEADSESSION, NFS4ERR_SEQ_FALSE_RETRY.
A reset of the session is currently our only response to a SEQUENCE operation
error. Don't reset the session on errors where a new session won't help.
Don't reset the session on errors where a new session won't help.
[nfs41: nfs4_async_handle_error update error checking]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[nfs41: trigger the state manager for session reset]
Replace session state bit with nfs_client state bit. Set the
NFS4CLNT_SESSION_SETUP bit upon a session related error in the sync/async
error handlers.
[nfs41: _nfs4_async_handle_error fix session reset error list]
Sequence operation errors that session reset could help.
NFS4ERR_BADSESSION
NFS4ERR_BADSLOT
NFS4ERR_BAD_HIGH_SLOT
NFS4ERR_DEADSESSION
NFS4ERR_CONN_NOT_BOUND_TO_SESSION
NFS4ERR_SEQ_FALSE_RETRY
NFS4ERR_SEQ_MISORDERED
Sequence operation errors that a session reset would not help
NFS4ERR_BADXDR
NFS4ERR_DELAY
NFS4ERR_REP_TOO_BIG
NFS4ERR_REP_TOO_BIG_TO_CACHE
NFS4ERR_REQ_TOO_BIG
NFS4ERR_RETRY_UNCACHED_REP
NFS4ERR_SEQUENCE_POS
NFS4ERR_TOO_MANY_OPS
Signed-off-by: Andy Adamson <andros@netapp.com>
[nfs41 nfs4_handle_exception fix session reset error list]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[moved nfs41_sequece_call_done code to nfs41: sequence operation]
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2009-04-01 20:22:42 +07:00
|
|
|
task->tk_status = 0;
|
|
|
|
return -EAGAIN;
|
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_DELAY:
|
2010-06-16 20:52:25 +07:00
|
|
|
nfs_inc_server_stats(server, NFSIOS_DELAY);
|
2006-03-21 01:44:14 +07:00
|
|
|
case -NFS4ERR_GRACE:
|
2005-04-17 05:20:36 +07:00
|
|
|
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
|
|
|
task->tk_status = 0;
|
|
|
|
return -EAGAIN;
|
2011-05-04 00:43:03 +07:00
|
|
|
case -NFS4ERR_RETRY_UNCACHED_REP:
|
2005-04-17 05:20:36 +07:00
|
|
|
case -NFS4ERR_OLD_STATEID:
|
|
|
|
task->tk_status = 0;
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
task->tk_status = nfs4_map_errors(task->tk_status);
|
|
|
|
return 0;
|
2013-03-15 03:57:48 +07:00
|
|
|
stateid_invalid:
|
|
|
|
task->tk_status = -EIO;
|
|
|
|
return 0;
|
2011-03-10 04:00:53 +07:00
|
|
|
wait_on_recovery:
|
2010-01-27 03:42:47 +07:00
|
|
|
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
|
|
|
|
if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
|
|
|
|
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
|
|
|
|
task->tk_status = 0;
|
|
|
|
return -EAGAIN;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
NFS: Always use the same SETCLIENTID boot verifier
Currently our NFS client assigns a unique SETCLIENTID boot verifier
for each server IP address it knows about. It's set to CURRENT_TIME
when the struct nfs_client for that server IP is created.
During the SETCLIENTID operation, our client also presents an
nfs_client_id4 string to servers, as an identifier on which the server
can hang all of this client's NFSv4 state. Our client's
nfs_client_id4 string is unique for each server IP address.
An NFSv4 server is obligated to wipe all NFSv4 state associated with
an nfs_client_id4 string when the client presents the same
nfs_client_id4 string along with a changed SETCLIENTID boot verifier.
When our client unmounts the last of a server's shares, it destroys
that server's struct nfs_client. The next time the client mounts that
NFS server, it creates a fresh struct nfs_client with a fresh boot
verifier. On seeing the fresh verifer, the server wipes any previous
NFSv4 state associated with that nfs_client_id4.
However, NFSv4.1 clients are supposed to present the same
nfs_client_id4 string to all servers. And, to support Transparent
State Migration, the same nfs_client_id4 string should be presented
to all NFSv4.0 servers so they recognize that migrated state for this
client belongs with state a server may already have for this client.
(This is known as the Uniform Client String model).
If the nfs_client_id4 string is the same but the boot verifier changes
for each server IP address, SETCLIENTID and EXCHANGE_ID operations
from such a client could unintentionally result in a server wiping a
client's previously obtained lease.
Thus, if our NFS client is going to use a fixed nfs_client_id4 string,
either for NFSv4.0 or NFSv4.1 mounts, our NFS client should use a
boot verifier that does not change depending on server IP address.
Replace our current per-nfs_client boot verifier with a per-nfs_net
boot verifier.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-05-22 09:45:41 +07:00
|
|
|
static void nfs4_init_boot_verifier(const struct nfs_client *clp,
|
|
|
|
nfs4_verifier *bootverf)
|
2012-03-03 05:14:31 +07:00
|
|
|
{
|
|
|
|
__be32 verf[2];
|
|
|
|
|
2012-05-22 09:45:33 +07:00
|
|
|
if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
|
|
|
|
/* An impossible timestamp guarantees this value
|
|
|
|
* will never match a generated boot time. */
|
|
|
|
verf[0] = 0;
|
|
|
|
verf[1] = (__be32)(NSEC_PER_SEC + 1);
|
|
|
|
} else {
|
NFS: Always use the same SETCLIENTID boot verifier
Currently our NFS client assigns a unique SETCLIENTID boot verifier
for each server IP address it knows about. It's set to CURRENT_TIME
when the struct nfs_client for that server IP is created.
During the SETCLIENTID operation, our client also presents an
nfs_client_id4 string to servers, as an identifier on which the server
can hang all of this client's NFSv4 state. Our client's
nfs_client_id4 string is unique for each server IP address.
An NFSv4 server is obligated to wipe all NFSv4 state associated with
an nfs_client_id4 string when the client presents the same
nfs_client_id4 string along with a changed SETCLIENTID boot verifier.
When our client unmounts the last of a server's shares, it destroys
that server's struct nfs_client. The next time the client mounts that
NFS server, it creates a fresh struct nfs_client with a fresh boot
verifier. On seeing the fresh verifer, the server wipes any previous
NFSv4 state associated with that nfs_client_id4.
However, NFSv4.1 clients are supposed to present the same
nfs_client_id4 string to all servers. And, to support Transparent
State Migration, the same nfs_client_id4 string should be presented
to all NFSv4.0 servers so they recognize that migrated state for this
client belongs with state a server may already have for this client.
(This is known as the Uniform Client String model).
If the nfs_client_id4 string is the same but the boot verifier changes
for each server IP address, SETCLIENTID and EXCHANGE_ID operations
from such a client could unintentionally result in a server wiping a
client's previously obtained lease.
Thus, if our NFS client is going to use a fixed nfs_client_id4 string,
either for NFSv4.0 or NFSv4.1 mounts, our NFS client should use a
boot verifier that does not change depending on server IP address.
Replace our current per-nfs_client boot verifier with a per-nfs_net
boot verifier.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-05-22 09:45:41 +07:00
|
|
|
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
|
|
|
|
verf[0] = (__be32)nn->boot_time.tv_sec;
|
|
|
|
verf[1] = (__be32)nn->boot_time.tv_nsec;
|
2012-05-22 09:45:33 +07:00
|
|
|
}
|
2012-03-03 05:14:31 +07:00
|
|
|
memcpy(bootverf->data, verf, sizeof(bootverf->data));
|
|
|
|
}
|
|
|
|
|
NFS: Use the same nfs_client_id4 for every server
Currently, when identifying itself to NFS servers, the Linux NFS
client uses a unique nfs_client_id4.id string for each server IP
address it talks with. For example, when client A talks to server X,
the client identifies itself using a string like "AX". The
requirements for these strings are specified in detail by RFC 3530
(and bis).
This form of client identification presents a problem for Transparent
State Migration. When client A's state on server X is migrated to
server Y, it continues to be associated with string "AX." But,
according to the rules of client string construction above, client
A will present string "AY" when communicating with server Y.
Server Y thus has no way to know that client A should be associated
with the state migrated from server X. "AX" is all but abandoned,
interfering with establishing fresh state for client A on server Y.
To support transparent state migration, then, NFSv4.0 clients must
instead use the same nfs_client_id4.id string to identify themselves
to every NFS server; something like "A".
Now a client identifies itself as "A" to server X. When a file
system on server X transitions to server Y, and client A identifies
itself as "A" to server Y, Y will know immediately that the state
associated with "A," whether it is native or migrated, is owned by
the client, and can merge both into a single lease.
As a pre-requisite to adding support for NFSv4 migration to the Linux
NFS client, this patch changes the way Linux identifies itself to NFS
servers via the SETCLIENTID (NFSv4 minor version 0) and EXCHANGE_ID
(NFSv4 minor version 1) operations.
In addition to removing the server's IP address from nfs_client_id4,
the Linux NFS client will also no longer use its own source IP address
as part of the nfs_client_id4 string. On multi-homed clients, the
value of this address depends on the address family and network
routing used to contact the server, thus it can be different for each
server.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-09-15 04:24:21 +07:00
|
|
|
static unsigned int
|
|
|
|
nfs4_init_nonuniform_client_string(const struct nfs_client *clp,
|
|
|
|
char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned int result;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
result = scnprintf(buf, len, "Linux NFSv4.0 %s/%s %s",
|
|
|
|
clp->cl_ipaddr,
|
|
|
|
rpc_peeraddr2str(clp->cl_rpcclient,
|
|
|
|
RPC_DISPLAY_ADDR),
|
|
|
|
rpc_peeraddr2str(clp->cl_rpcclient,
|
|
|
|
RPC_DISPLAY_PROTO));
|
|
|
|
rcu_read_unlock();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
nfs4_init_uniform_client_string(const struct nfs_client *clp,
|
|
|
|
char *buf, size_t len)
|
|
|
|
{
|
2012-09-15 04:24:41 +07:00
|
|
|
char *nodename = clp->cl_rpcclient->cl_nodename;
|
|
|
|
|
|
|
|
if (nfs4_client_id_uniquifier[0] != '\0')
|
|
|
|
nodename = nfs4_client_id_uniquifier;
|
NFS: Use the same nfs_client_id4 for every server
Currently, when identifying itself to NFS servers, the Linux NFS
client uses a unique nfs_client_id4.id string for each server IP
address it talks with. For example, when client A talks to server X,
the client identifies itself using a string like "AX". The
requirements for these strings are specified in detail by RFC 3530
(and bis).
This form of client identification presents a problem for Transparent
State Migration. When client A's state on server X is migrated to
server Y, it continues to be associated with string "AX." But,
according to the rules of client string construction above, client
A will present string "AY" when communicating with server Y.
Server Y thus has no way to know that client A should be associated
with the state migrated from server X. "AX" is all but abandoned,
interfering with establishing fresh state for client A on server Y.
To support transparent state migration, then, NFSv4.0 clients must
instead use the same nfs_client_id4.id string to identify themselves
to every NFS server; something like "A".
Now a client identifies itself as "A" to server X. When a file
system on server X transitions to server Y, and client A identifies
itself as "A" to server Y, Y will know immediately that the state
associated with "A," whether it is native or migrated, is owned by
the client, and can merge both into a single lease.
As a pre-requisite to adding support for NFSv4 migration to the Linux
NFS client, this patch changes the way Linux identifies itself to NFS
servers via the SETCLIENTID (NFSv4 minor version 0) and EXCHANGE_ID
(NFSv4 minor version 1) operations.
In addition to removing the server's IP address from nfs_client_id4,
the Linux NFS client will also no longer use its own source IP address
as part of the nfs_client_id4 string. On multi-homed clients, the
value of this address depends on the address family and network
routing used to contact the server, thus it can be different for each
server.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-09-15 04:24:21 +07:00
|
|
|
return scnprintf(buf, len, "Linux NFSv%u.%u %s",
|
|
|
|
clp->rpc_ops->version, clp->cl_minorversion,
|
2012-09-15 04:24:41 +07:00
|
|
|
nodename);
|
NFS: Use the same nfs_client_id4 for every server
Currently, when identifying itself to NFS servers, the Linux NFS
client uses a unique nfs_client_id4.id string for each server IP
address it talks with. For example, when client A talks to server X,
the client identifies itself using a string like "AX". The
requirements for these strings are specified in detail by RFC 3530
(and bis).
This form of client identification presents a problem for Transparent
State Migration. When client A's state on server X is migrated to
server Y, it continues to be associated with string "AX." But,
according to the rules of client string construction above, client
A will present string "AY" when communicating with server Y.
Server Y thus has no way to know that client A should be associated
with the state migrated from server X. "AX" is all but abandoned,
interfering with establishing fresh state for client A on server Y.
To support transparent state migration, then, NFSv4.0 clients must
instead use the same nfs_client_id4.id string to identify themselves
to every NFS server; something like "A".
Now a client identifies itself as "A" to server X. When a file
system on server X transitions to server Y, and client A identifies
itself as "A" to server Y, Y will know immediately that the state
associated with "A," whether it is native or migrated, is owned by
the client, and can merge both into a single lease.
As a pre-requisite to adding support for NFSv4 migration to the Linux
NFS client, this patch changes the way Linux identifies itself to NFS
servers via the SETCLIENTID (NFSv4 minor version 0) and EXCHANGE_ID
(NFSv4 minor version 1) operations.
In addition to removing the server's IP address from nfs_client_id4,
the Linux NFS client will also no longer use its own source IP address
as part of the nfs_client_id4 string. On multi-homed clients, the
value of this address depends on the address family and network
routing used to contact the server, thus it can be different for each
server.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-09-15 04:24:21 +07:00
|
|
|
}
|
|
|
|
|
2012-07-12 03:30:59 +07:00
|
|
|
/**
|
|
|
|
* nfs4_proc_setclientid - Negotiate client ID
|
|
|
|
* @clp: state data structure
|
|
|
|
* @program: RPC program for NFSv4 callback service
|
|
|
|
* @port: IP port number for NFS4 callback service
|
|
|
|
* @cred: RPC credential to use for this call
|
|
|
|
* @res: where to place the result
|
|
|
|
*
|
|
|
|
* Returns zero, a negative errno, or a negative NFS4ERR status code.
|
|
|
|
*/
|
2010-04-17 03:43:06 +07:00
|
|
|
int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
|
|
|
|
unsigned short port, struct rpc_cred *cred,
|
|
|
|
struct nfs4_setclientid_res *res)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
nfs4_verifier sc_verifier;
|
|
|
|
struct nfs4_setclientid setclientid = {
|
|
|
|
.sc_verifier = &sc_verifier,
|
|
|
|
.sc_prog = program,
|
2011-01-06 09:04:30 +07:00
|
|
|
.sc_cb_ident = clp->cl_cb_ident,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID],
|
|
|
|
.rpc_argp = &setclientid,
|
2010-04-17 03:43:06 +07:00
|
|
|
.rpc_resp = res,
|
2006-01-03 15:55:26 +07:00
|
|
|
.rpc_cred = cred,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2012-07-12 03:30:59 +07:00
|
|
|
int status;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-07-12 03:30:50 +07:00
|
|
|
/* nfs_client_id4 */
|
NFS: Always use the same SETCLIENTID boot verifier
Currently our NFS client assigns a unique SETCLIENTID boot verifier
for each server IP address it knows about. It's set to CURRENT_TIME
when the struct nfs_client for that server IP is created.
During the SETCLIENTID operation, our client also presents an
nfs_client_id4 string to servers, as an identifier on which the server
can hang all of this client's NFSv4 state. Our client's
nfs_client_id4 string is unique for each server IP address.
An NFSv4 server is obligated to wipe all NFSv4 state associated with
an nfs_client_id4 string when the client presents the same
nfs_client_id4 string along with a changed SETCLIENTID boot verifier.
When our client unmounts the last of a server's shares, it destroys
that server's struct nfs_client. The next time the client mounts that
NFS server, it creates a fresh struct nfs_client with a fresh boot
verifier. On seeing the fresh verifer, the server wipes any previous
NFSv4 state associated with that nfs_client_id4.
However, NFSv4.1 clients are supposed to present the same
nfs_client_id4 string to all servers. And, to support Transparent
State Migration, the same nfs_client_id4 string should be presented
to all NFSv4.0 servers so they recognize that migrated state for this
client belongs with state a server may already have for this client.
(This is known as the Uniform Client String model).
If the nfs_client_id4 string is the same but the boot verifier changes
for each server IP address, SETCLIENTID and EXCHANGE_ID operations
from such a client could unintentionally result in a server wiping a
client's previously obtained lease.
Thus, if our NFS client is going to use a fixed nfs_client_id4 string,
either for NFSv4.0 or NFSv4.1 mounts, our NFS client should use a
boot verifier that does not change depending on server IP address.
Replace our current per-nfs_client boot verifier with a per-nfs_net
boot verifier.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-05-22 09:45:41 +07:00
|
|
|
nfs4_init_boot_verifier(clp, &sc_verifier);
|
NFS: Use the same nfs_client_id4 for every server
Currently, when identifying itself to NFS servers, the Linux NFS
client uses a unique nfs_client_id4.id string for each server IP
address it talks with. For example, when client A talks to server X,
the client identifies itself using a string like "AX". The
requirements for these strings are specified in detail by RFC 3530
(and bis).
This form of client identification presents a problem for Transparent
State Migration. When client A's state on server X is migrated to
server Y, it continues to be associated with string "AX." But,
according to the rules of client string construction above, client
A will present string "AY" when communicating with server Y.
Server Y thus has no way to know that client A should be associated
with the state migrated from server X. "AX" is all but abandoned,
interfering with establishing fresh state for client A on server Y.
To support transparent state migration, then, NFSv4.0 clients must
instead use the same nfs_client_id4.id string to identify themselves
to every NFS server; something like "A".
Now a client identifies itself as "A" to server X. When a file
system on server X transitions to server Y, and client A identifies
itself as "A" to server Y, Y will know immediately that the state
associated with "A," whether it is native or migrated, is owned by
the client, and can merge both into a single lease.
As a pre-requisite to adding support for NFSv4 migration to the Linux
NFS client, this patch changes the way Linux identifies itself to NFS
servers via the SETCLIENTID (NFSv4 minor version 0) and EXCHANGE_ID
(NFSv4 minor version 1) operations.
In addition to removing the server's IP address from nfs_client_id4,
the Linux NFS client will also no longer use its own source IP address
as part of the nfs_client_id4 string. On multi-homed clients, the
value of this address depends on the address family and network
routing used to contact the server, thus it can be different for each
server.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-09-15 04:24:21 +07:00
|
|
|
if (test_bit(NFS_CS_MIGRATION, &clp->cl_flags))
|
|
|
|
setclientid.sc_name_len =
|
|
|
|
nfs4_init_uniform_client_string(clp,
|
|
|
|
setclientid.sc_name,
|
|
|
|
sizeof(setclientid.sc_name));
|
|
|
|
else
|
|
|
|
setclientid.sc_name_len =
|
|
|
|
nfs4_init_nonuniform_client_string(clp,
|
|
|
|
setclientid.sc_name,
|
|
|
|
sizeof(setclientid.sc_name));
|
2012-07-12 03:30:50 +07:00
|
|
|
/* cb_client4 */
|
NFS: Use the same nfs_client_id4 for every server
Currently, when identifying itself to NFS servers, the Linux NFS
client uses a unique nfs_client_id4.id string for each server IP
address it talks with. For example, when client A talks to server X,
the client identifies itself using a string like "AX". The
requirements for these strings are specified in detail by RFC 3530
(and bis).
This form of client identification presents a problem for Transparent
State Migration. When client A's state on server X is migrated to
server Y, it continues to be associated with string "AX." But,
according to the rules of client string construction above, client
A will present string "AY" when communicating with server Y.
Server Y thus has no way to know that client A should be associated
with the state migrated from server X. "AX" is all but abandoned,
interfering with establishing fresh state for client A on server Y.
To support transparent state migration, then, NFSv4.0 clients must
instead use the same nfs_client_id4.id string to identify themselves
to every NFS server; something like "A".
Now a client identifies itself as "A" to server X. When a file
system on server X transitions to server Y, and client A identifies
itself as "A" to server Y, Y will know immediately that the state
associated with "A," whether it is native or migrated, is owned by
the client, and can merge both into a single lease.
As a pre-requisite to adding support for NFSv4 migration to the Linux
NFS client, this patch changes the way Linux identifies itself to NFS
servers via the SETCLIENTID (NFSv4 minor version 0) and EXCHANGE_ID
(NFSv4 minor version 1) operations.
In addition to removing the server's IP address from nfs_client_id4,
the Linux NFS client will also no longer use its own source IP address
as part of the nfs_client_id4 string. On multi-homed clients, the
value of this address depends on the address family and network
routing used to contact the server, thus it can be different for each
server.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-09-15 04:24:21 +07:00
|
|
|
rcu_read_lock();
|
2012-07-12 03:30:50 +07:00
|
|
|
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
|
2013-06-18 23:58:12 +07:00
|
|
|
sizeof(setclientid.sc_netid), "%s",
|
2007-12-11 02:57:09 +07:00
|
|
|
rpc_peeraddr2str(clp->cl_rpcclient,
|
|
|
|
RPC_DISPLAY_NETID));
|
2012-07-12 03:30:50 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
|
2007-12-11 02:57:09 +07:00
|
|
|
sizeof(setclientid.sc_uaddr), "%s.%u.%u",
|
2005-04-17 05:20:36 +07:00
|
|
|
clp->cl_ipaddr, port >> 8, port & 255);
|
|
|
|
|
2012-07-12 03:30:59 +07:00
|
|
|
dprintk("NFS call setclientid auth=%s, '%.*s'\n",
|
|
|
|
clp->cl_rpcclient->cl_auth->au_ops->au_name,
|
|
|
|
setclientid.sc_name_len, setclientid.sc_name);
|
|
|
|
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
|
|
|
dprintk("NFS reply setclientid: %d\n", status);
|
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-07-12 03:30:59 +07:00
|
|
|
/**
|
|
|
|
* nfs4_proc_setclientid_confirm - Confirm client ID
|
|
|
|
* @clp: state data structure
|
|
|
|
* @res: result of a previous SETCLIENTID
|
|
|
|
* @cred: RPC credential to use for this call
|
|
|
|
*
|
|
|
|
* Returns zero, a negative errno, or a negative NFS4ERR status code.
|
|
|
|
*/
|
2011-04-25 01:28:18 +07:00
|
|
|
int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
|
2010-04-17 03:43:06 +07:00
|
|
|
struct nfs4_setclientid_res *arg,
|
|
|
|
struct rpc_cred *cred)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
|
2010-04-17 03:43:06 +07:00
|
|
|
.rpc_argp = arg,
|
2006-01-03 15:55:26 +07:00
|
|
|
.rpc_cred = cred,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
2012-07-12 03:30:59 +07:00
|
|
|
dprintk("NFS call setclientid_confirm auth=%s, (client ID %llx)\n",
|
|
|
|
clp->cl_rpcclient->cl_auth->au_ops->au_name,
|
|
|
|
clp->cl_clientid);
|
2011-04-25 01:29:33 +07:00
|
|
|
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
2012-07-12 03:30:59 +07:00
|
|
|
dprintk("NFS reply setclientid_confirm: %d\n", status);
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:18 +07:00
|
|
|
struct nfs4_delegreturndata {
|
|
|
|
struct nfs4_delegreturnargs args;
|
2006-01-03 15:55:38 +07:00
|
|
|
struct nfs4_delegreturnres res;
|
2006-01-03 15:55:18 +07:00
|
|
|
struct nfs_fh fh;
|
|
|
|
nfs4_stateid stateid;
|
2006-01-03 15:55:21 +07:00
|
|
|
unsigned long timestamp;
|
2006-01-03 15:55:38 +07:00
|
|
|
struct nfs_fattr fattr;
|
2006-01-03 15:55:18 +07:00
|
|
|
int rpc_status;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_delegreturndata *data = calldata;
|
2009-04-01 20:22:28 +07:00
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
|
|
|
return;
|
2009-04-01 20:22:28 +07:00
|
|
|
|
2009-12-07 21:23:21 +07:00
|
|
|
switch (task->tk_status) {
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
case 0:
|
2006-01-03 15:55:38 +07:00
|
|
|
renew_lease(data->res.server, data->timestamp);
|
2009-12-07 21:23:21 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (nfs4_async_handle_error(task, data->res.server, NULL) ==
|
|
|
|
-EAGAIN) {
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2009-12-07 21:23:21 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
data->rpc_status = task->tk_status;
|
2006-01-03 15:55:18 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_delegreturn_release(void *calldata)
|
|
|
|
{
|
|
|
|
kfree(calldata);
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:28 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
|
|
|
|
{
|
|
|
|
struct nfs4_delegreturndata *d_data;
|
|
|
|
|
|
|
|
d_data = (struct nfs4_delegreturndata *)data;
|
|
|
|
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs4_setup_sequence(d_data->res.server,
|
|
|
|
&d_data->args.seq_args,
|
|
|
|
&d_data->res.seq_res,
|
|
|
|
task);
|
2009-04-01 20:22:28 +07:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
2006-03-21 01:44:07 +07:00
|
|
|
static const struct rpc_call_ops nfs4_delegreturn_ops = {
|
2009-04-01 20:22:28 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
.rpc_call_prepare = nfs4_delegreturn_prepare,
|
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
2006-01-03 15:55:18 +07:00
|
|
|
.rpc_call_done = nfs4_delegreturn_done,
|
|
|
|
.rpc_release = nfs4_delegreturn_release,
|
|
|
|
};
|
|
|
|
|
2008-01-25 06:14:34 +07:00
|
|
|
static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
|
2006-01-03 15:55:18 +07:00
|
|
|
{
|
|
|
|
struct nfs4_delegreturndata *data;
|
2006-01-03 15:55:38 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2006-01-03 15:55:18 +07:00
|
|
|
struct rpc_task *task;
|
2007-07-15 02:40:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN],
|
|
|
|
.rpc_cred = cred,
|
|
|
|
};
|
2007-07-15 02:39:59 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = server->client,
|
2007-07-15 02:40:01 +07:00
|
|
|
.rpc_message = &msg,
|
2007-07-15 02:39:59 +07:00
|
|
|
.callback_ops = &nfs4_delegreturn_ops,
|
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2008-01-25 06:14:34 +07:00
|
|
|
int status = 0;
|
2006-01-03 15:55:18 +07:00
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
data = kzalloc(sizeof(*data), GFP_NOFS);
|
2006-01-03 15:55:18 +07:00
|
|
|
if (data == NULL)
|
|
|
|
return -ENOMEM;
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
|
2006-01-03 15:55:18 +07:00
|
|
|
data->args.fhandle = &data->fh;
|
|
|
|
data->args.stateid = &data->stateid;
|
2012-04-28 00:48:17 +07:00
|
|
|
data->args.bitmask = server->cache_consistency_bitmask;
|
2006-01-03 15:55:18 +07:00
|
|
|
nfs_copy_fh(&data->fh, NFS_FH(inode));
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&data->stateid, stateid);
|
2006-01-03 15:55:38 +07:00
|
|
|
data->res.fattr = &data->fattr;
|
|
|
|
data->res.server = server;
|
2007-07-15 02:40:01 +07:00
|
|
|
nfs_fattr_init(data->res.fattr);
|
2006-01-03 15:55:21 +07:00
|
|
|
data->timestamp = jiffies;
|
2006-01-03 15:55:18 +07:00
|
|
|
data->rpc_status = 0;
|
|
|
|
|
2007-07-15 02:39:59 +07:00
|
|
|
task_setup_data.callback_data = data;
|
2010-12-21 22:52:24 +07:00
|
|
|
msg.rpc_argp = &data->args;
|
|
|
|
msg.rpc_resp = &data->res;
|
2007-07-15 02:39:59 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
2006-03-21 06:11:10 +07:00
|
|
|
if (IS_ERR(task))
|
2006-01-03 15:55:18 +07:00
|
|
|
return PTR_ERR(task);
|
2008-01-25 06:14:34 +07:00
|
|
|
if (!issync)
|
|
|
|
goto out;
|
2006-01-03 15:55:18 +07:00
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
2008-01-25 06:14:34 +07:00
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
|
|
|
status = data->rpc_status;
|
2012-04-29 03:05:03 +07:00
|
|
|
if (status == 0)
|
|
|
|
nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
|
|
|
|
else
|
|
|
|
nfs_refresh_inode(inode, &data->fattr);
|
2008-01-25 06:14:34 +07:00
|
|
|
out:
|
2006-11-12 10:18:03 +07:00
|
|
|
rpc_put_task(task);
|
2006-01-03 15:55:18 +07:00
|
|
|
return status;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2008-01-25 06:14:34 +07:00
|
|
|
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
2008-01-25 06:14:34 +07:00
|
|
|
err = _nfs4_proc_delegreturn(inode, cred, stateid, issync);
|
2005-04-17 05:20:36 +07:00
|
|
|
switch (err) {
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
case 0:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
err = nfs4_handle_exception(server, err, &exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
|
|
|
|
#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sleep, with exponential backoff, and retry the LOCK operation.
|
|
|
|
*/
|
|
|
|
static unsigned long
|
|
|
|
nfs4_set_lock_task_retry(unsigned long timeout)
|
|
|
|
{
|
2011-12-02 04:44:39 +07:00
|
|
|
freezable_schedule_timeout_killable(timeout);
|
2005-04-17 05:20:36 +07:00
|
|
|
timeout <<= 1;
|
|
|
|
if (timeout > NFS4_LOCK_MAXTIMEOUT)
|
|
|
|
return NFS4_LOCK_MAXTIMEOUT;
|
|
|
|
return timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
|
|
|
{
|
|
|
|
struct inode *inode = state->inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2006-08-23 07:06:09 +07:00
|
|
|
struct nfs_client *clp = server->nfs_client;
|
2006-01-03 15:55:16 +07:00
|
|
|
struct nfs_lockt_args arg = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.fh = NFS_FH(inode),
|
2006-01-03 15:55:16 +07:00
|
|
|
.fl = request,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2006-01-03 15:55:16 +07:00
|
|
|
struct nfs_lockt_res res = {
|
|
|
|
.denied = request,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT],
|
|
|
|
.rpc_argp = &arg,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
.rpc_cred = state->owner->so_cred,
|
|
|
|
};
|
|
|
|
struct nfs4_lock_state *lsp;
|
|
|
|
int status;
|
|
|
|
|
2006-01-03 15:55:16 +07:00
|
|
|
arg.lock_owner.clientid = clp->cl_clientid;
|
2005-06-23 00:16:32 +07:00
|
|
|
status = nfs4_set_lock_state(state, request);
|
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
|
|
|
lsp = request->fl_u.nfs4_fl.owner;
|
2012-01-18 10:04:25 +07:00
|
|
|
arg.lock_owner.id = lsp->ls_seqid.owner_id;
|
2010-12-21 22:45:27 +07:00
|
|
|
arg.lock_owner.s_dev = server->s_dev;
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
|
2006-01-03 15:55:16 +07:00
|
|
|
switch (status) {
|
|
|
|
case 0:
|
|
|
|
request->fl_type = F_UNLCK;
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_DENIED:
|
|
|
|
status = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2007-02-23 06:48:53 +07:00
|
|
|
request->fl_ops->fl_release_private(request);
|
2005-06-23 00:16:32 +07:00
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(state->inode),
|
|
|
|
_nfs4_proc_getlk(state, cmd, request),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_vfs_lock(struct file *file, struct file_lock *fl)
|
|
|
|
{
|
|
|
|
int res = 0;
|
|
|
|
switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) {
|
|
|
|
case FL_POSIX:
|
|
|
|
res = posix_lock_file_wait(file, fl);
|
|
|
|
break;
|
|
|
|
case FL_FLOCK:
|
|
|
|
res = flock_lock_file_wait(file, fl);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2005-10-19 04:20:15 +07:00
|
|
|
struct nfs4_unlockdata {
|
2006-01-03 15:55:16 +07:00
|
|
|
struct nfs_locku_args arg;
|
|
|
|
struct nfs_locku_res res;
|
2005-10-19 04:20:15 +07:00
|
|
|
struct nfs4_lock_state *lsp;
|
|
|
|
struct nfs_open_context *ctx;
|
2006-01-03 15:55:16 +07:00
|
|
|
struct file_lock fl;
|
|
|
|
const struct nfs_server *server;
|
2006-01-03 15:55:21 +07:00
|
|
|
unsigned long timestamp;
|
2005-10-19 04:20:15 +07:00
|
|
|
};
|
|
|
|
|
2006-01-03 15:55:16 +07:00
|
|
|
static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
|
|
|
|
struct nfs_open_context *ctx,
|
|
|
|
struct nfs4_lock_state *lsp,
|
|
|
|
struct nfs_seqid *seqid)
|
|
|
|
{
|
|
|
|
struct nfs4_unlockdata *p;
|
|
|
|
struct inode *inode = lsp->ls_state->inode;
|
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
p = kzalloc(sizeof(*p), GFP_NOFS);
|
2006-01-03 15:55:16 +07:00
|
|
|
if (p == NULL)
|
|
|
|
return NULL;
|
|
|
|
p->arg.fh = NFS_FH(inode);
|
|
|
|
p->arg.fl = &p->fl;
|
|
|
|
p->arg.seqid = seqid;
|
2008-04-08 00:20:54 +07:00
|
|
|
p->res.seqid = seqid;
|
2006-01-03 15:55:16 +07:00
|
|
|
p->arg.stateid = &lsp->ls_stateid;
|
|
|
|
p->lsp = lsp;
|
|
|
|
atomic_inc(&lsp->ls_count);
|
|
|
|
/* Ensure we don't close file until we're done freeing locks! */
|
|
|
|
p->ctx = get_nfs_open_context(ctx);
|
|
|
|
memcpy(&p->fl, fl, sizeof(p->fl));
|
|
|
|
p->server = NFS_SERVER(inode);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:07 +07:00
|
|
|
static void nfs4_locku_release_calldata(void *data)
|
2005-10-19 04:20:15 +07:00
|
|
|
{
|
2006-01-03 15:55:04 +07:00
|
|
|
struct nfs4_unlockdata *calldata = data;
|
2006-01-03 15:55:16 +07:00
|
|
|
nfs_free_seqid(calldata->arg.seqid);
|
2006-01-03 15:55:07 +07:00
|
|
|
nfs4_put_lock_state(calldata->lsp);
|
|
|
|
put_nfs_open_context(calldata->ctx);
|
|
|
|
kfree(calldata);
|
2005-10-19 04:20:15 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:04 +07:00
|
|
|
static void nfs4_locku_done(struct rpc_task *task, void *data)
|
2005-10-19 04:20:15 +07:00
|
|
|
{
|
2006-01-03 15:55:04 +07:00
|
|
|
struct nfs4_unlockdata *calldata = data;
|
2005-10-19 04:20:15 +07:00
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs4_sequence_done(task, &calldata->res.seq_res))
|
|
|
|
return;
|
2005-10-19 04:20:15 +07:00
|
|
|
switch (task->tk_status) {
|
|
|
|
case 0:
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&calldata->lsp->ls_stateid,
|
|
|
|
&calldata->res.stateid);
|
2006-01-03 15:55:21 +07:00
|
|
|
renew_lease(calldata->server, calldata->timestamp);
|
2005-10-19 04:20:15 +07:00
|
|
|
break;
|
2008-12-24 03:21:46 +07:00
|
|
|
case -NFS4ERR_BAD_STATEID:
|
|
|
|
case -NFS4ERR_OLD_STATEID:
|
2005-10-19 04:20:15 +07:00
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
break;
|
|
|
|
default:
|
2008-12-24 03:21:46 +07:00
|
|
|
if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2005-10-19 04:20:15 +07:00
|
|
|
}
|
2012-10-30 05:53:23 +07:00
|
|
|
nfs_release_seqid(calldata->arg.seqid);
|
2005-10-19 04:20:15 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:05 +07:00
|
|
|
static void nfs4_locku_prepare(struct rpc_task *task, void *data)
|
2005-10-19 04:20:15 +07:00
|
|
|
{
|
2006-01-03 15:55:05 +07:00
|
|
|
struct nfs4_unlockdata *calldata = data;
|
2005-10-19 04:20:15 +07:00
|
|
|
|
2006-01-03 15:55:16 +07:00
|
|
|
if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_wait;
|
2012-09-11 00:26:49 +07:00
|
|
|
if (test_bit(NFS_LOCK_INITIALIZED, &calldata->lsp->ls_flags) == 0) {
|
2006-01-03 15:55:04 +07:00
|
|
|
/* Note: exit _without_ running nfs4_locku_done */
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_no_action;
|
2005-10-19 04:20:15 +07:00
|
|
|
}
|
2006-01-03 15:55:21 +07:00
|
|
|
calldata->timestamp = jiffies;
|
2010-06-16 20:52:26 +07:00
|
|
|
if (nfs4_setup_sequence(calldata->server,
|
2009-04-01 20:22:23 +07:00
|
|
|
&calldata->arg.seq_args,
|
2012-10-30 05:37:40 +07:00
|
|
|
&calldata->res.seq_res,
|
|
|
|
task) != 0)
|
|
|
|
nfs_release_seqid(calldata->arg.seqid);
|
2013-02-12 07:01:21 +07:00
|
|
|
return;
|
|
|
|
out_no_action:
|
|
|
|
task->tk_action = NULL;
|
|
|
|
out_wait:
|
|
|
|
nfs4_sequence_done(task, &calldata->res.seq_res);
|
2005-10-19 04:20:15 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:04 +07:00
|
|
|
static const struct rpc_call_ops nfs4_locku_ops = {
|
2006-01-03 15:55:05 +07:00
|
|
|
.rpc_call_prepare = nfs4_locku_prepare,
|
2006-01-03 15:55:04 +07:00
|
|
|
.rpc_call_done = nfs4_locku_done,
|
2006-01-03 15:55:07 +07:00
|
|
|
.rpc_release = nfs4_locku_release_calldata,
|
2006-01-03 15:55:04 +07:00
|
|
|
};
|
|
|
|
|
2006-01-03 15:55:17 +07:00
|
|
|
static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
|
|
|
|
struct nfs_open_context *ctx,
|
|
|
|
struct nfs4_lock_state *lsp,
|
|
|
|
struct nfs_seqid *seqid)
|
|
|
|
{
|
|
|
|
struct nfs4_unlockdata *data;
|
2007-07-15 02:40:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU],
|
|
|
|
.rpc_cred = ctx->cred,
|
|
|
|
};
|
2007-07-15 02:39:59 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = NFS_CLIENT(lsp->ls_state->inode),
|
2007-07-15 02:40:01 +07:00
|
|
|
.rpc_message = &msg,
|
2007-07-15 02:39:59 +07:00
|
|
|
.callback_ops = &nfs4_locku_ops,
|
2008-02-20 08:04:23 +07:00
|
|
|
.workqueue = nfsiod_workqueue,
|
2007-07-15 02:39:59 +07:00
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2006-01-03 15:55:17 +07:00
|
|
|
|
NFSv4: Make sure unlock is really an unlock when cancelling a lock
I ran into a curious issue when a lock is being canceled. The
cancellation results in a lock request to the vfs layer instead of an
unlock request. This is particularly insidious when the process that
owns the lock is exiting. In that case, sometimes the erroneous lock is
applied AFTER the process has entered zombie state, preventing the lock
from ever being released. Eventually other processes block on the lock
causing a slow degredation of the system. In the 2.6.16 kernel this was
investigated on, the problem is compounded by the fact that the cl_sem
is held while blocking on the vfs lock, which results in most processes
accessing the nfs file system in question hanging.
In more detail, here is how the situation occurs:
first _nfs4_do_setlk():
static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int reclaim)
...
ret = nfs4_wait_for_completion_rpc_task(task);
if (ret == 0) {
...
} else
data->cancelled = 1;
then nfs4_lock_release():
static void nfs4_lock_release(void *calldata)
...
if (data->cancelled != 0) {
struct rpc_task *task;
task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
data->arg.lock_seqid);
The problem is the same file_lock that was passed in to _nfs4_do_setlk()
gets passed to nfs4_do_unlck() from nfs4_lock_release(). So the type is
still F_RDLCK or FWRLCK, not F_UNLCK. At some point, when cancelling the
lock, the type needs to be changed to F_UNLCK. It seemed easiest to do
that in nfs4_do_unlck(), but it could be done in nfs4_lock_release().
The concern I had with doing it there was if something still needed the
original file_lock, though it turns out the original file_lock still
needs to be modified by nfs4_do_unlck() because nfs4_do_unlck() uses the
original file_lock to pass to the vfs layer, and a copy of the original
file_lock for the RPC request.
It seems like the simplest solution is to force all situations where
nfs4_do_unlck() is being used to result in an unlock, so with that in
mind, I made the following change:
Signed-off-by: Frank Filz <ffilzlnx@us.ibm.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2007-07-10 05:32:29 +07:00
|
|
|
/* Ensure this is an unlock - when canceling a lock, the
|
|
|
|
* canceled lock is passed in, and it won't be an unlock.
|
|
|
|
*/
|
|
|
|
fl->fl_type = F_UNLCK;
|
|
|
|
|
2006-01-03 15:55:17 +07:00
|
|
|
data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
|
|
|
|
if (data == NULL) {
|
|
|
|
nfs_free_seqid(seqid);
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1);
|
2010-12-21 22:52:24 +07:00
|
|
|
msg.rpc_argp = &data->arg;
|
|
|
|
msg.rpc_resp = &data->res;
|
2007-07-15 02:39:59 +07:00
|
|
|
task_setup_data.callback_data = data;
|
|
|
|
return rpc_run_task(&task_setup_data);
|
2006-01-03 15:55:17 +07:00
|
|
|
}
|
|
|
|
|
2005-10-19 04:20:15 +07:00
|
|
|
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
|
|
|
|
{
|
2013-02-07 22:54:07 +07:00
|
|
|
struct inode *inode = state->inode;
|
|
|
|
struct nfs4_state_owner *sp = state->owner;
|
|
|
|
struct nfs_inode *nfsi = NFS_I(inode);
|
2006-01-03 15:55:16 +07:00
|
|
|
struct nfs_seqid *seqid;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs4_lock_state *lsp;
|
2006-01-03 15:55:07 +07:00
|
|
|
struct rpc_task *task;
|
|
|
|
int status = 0;
|
2008-04-05 02:08:02 +07:00
|
|
|
unsigned char fl_flags = request->fl_flags;
|
2005-10-19 04:20:15 +07:00
|
|
|
|
2005-06-23 00:16:32 +07:00
|
|
|
status = nfs4_set_lock_state(state, request);
|
2006-06-30 03:38:34 +07:00
|
|
|
/* Unlock _before_ we do the RPC call */
|
|
|
|
request->fl_flags |= FL_EXISTS;
|
2013-02-07 22:54:07 +07:00
|
|
|
/* Exclude nfs_delegation_claim_locks() */
|
|
|
|
mutex_lock(&sp->so_delegreturn_mutex);
|
|
|
|
/* Exclude nfs4_reclaim_open_stateid() - note nesting! */
|
2008-12-24 03:21:44 +07:00
|
|
|
down_read(&nfsi->rwsem);
|
|
|
|
if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
|
|
|
|
up_read(&nfsi->rwsem);
|
2013-02-07 22:54:07 +07:00
|
|
|
mutex_unlock(&sp->so_delegreturn_mutex);
|
2006-06-30 03:38:34 +07:00
|
|
|
goto out;
|
2008-12-24 03:21:44 +07:00
|
|
|
}
|
|
|
|
up_read(&nfsi->rwsem);
|
2013-02-07 22:54:07 +07:00
|
|
|
mutex_unlock(&sp->so_delegreturn_mutex);
|
2005-06-23 00:16:32 +07:00
|
|
|
if (status != 0)
|
2006-06-30 03:38:34 +07:00
|
|
|
goto out;
|
|
|
|
/* Is this a delegated lock? */
|
2005-06-23 00:16:32 +07:00
|
|
|
lsp = request->fl_u.nfs4_fl.owner;
|
2013-04-30 23:43:42 +07:00
|
|
|
if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) == 0)
|
|
|
|
goto out;
|
2010-05-13 23:51:01 +07:00
|
|
|
seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
|
2006-06-30 03:38:34 +07:00
|
|
|
status = -ENOMEM;
|
2006-01-03 15:55:16 +07:00
|
|
|
if (seqid == NULL)
|
2006-06-30 03:38:34 +07:00
|
|
|
goto out;
|
2007-08-11 04:44:32 +07:00
|
|
|
task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid);
|
2006-01-03 15:55:17 +07:00
|
|
|
status = PTR_ERR(task);
|
|
|
|
if (IS_ERR(task))
|
2006-06-30 03:38:34 +07:00
|
|
|
goto out;
|
2006-01-03 15:55:17 +07:00
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
2006-11-12 10:18:03 +07:00
|
|
|
rpc_put_task(task);
|
2006-06-30 03:38:34 +07:00
|
|
|
out:
|
2008-04-05 02:08:02 +07:00
|
|
|
request->fl_flags = fl_flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:17 +07:00
|
|
|
struct nfs4_lockdata {
|
|
|
|
struct nfs_lock_args arg;
|
|
|
|
struct nfs_lock_res res;
|
|
|
|
struct nfs4_lock_state *lsp;
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
struct file_lock fl;
|
2006-01-03 15:55:21 +07:00
|
|
|
unsigned long timestamp;
|
2006-01-03 15:55:17 +07:00
|
|
|
int rpc_status;
|
|
|
|
int cancelled;
|
2009-04-01 20:22:22 +07:00
|
|
|
struct nfs_server *server;
|
2006-01-03 15:55:17 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
|
2010-05-13 23:51:01 +07:00
|
|
|
struct nfs_open_context *ctx, struct nfs4_lock_state *lsp,
|
|
|
|
gfp_t gfp_mask)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-03 15:55:17 +07:00
|
|
|
struct nfs4_lockdata *p;
|
|
|
|
struct inode *inode = lsp->ls_state->inode;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2006-01-03 15:55:17 +07:00
|
|
|
|
2010-05-13 23:51:01 +07:00
|
|
|
p = kzalloc(sizeof(*p), gfp_mask);
|
2006-01-03 15:55:17 +07:00
|
|
|
if (p == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p->arg.fh = NFS_FH(inode);
|
|
|
|
p->arg.fl = &p->fl;
|
2010-05-13 23:51:01 +07:00
|
|
|
p->arg.open_seqid = nfs_alloc_seqid(&lsp->ls_state->owner->so_seqid, gfp_mask);
|
2008-01-09 05:56:07 +07:00
|
|
|
if (p->arg.open_seqid == NULL)
|
|
|
|
goto out_free;
|
2010-05-13 23:51:01 +07:00
|
|
|
p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid, gfp_mask);
|
2006-01-03 15:55:17 +07:00
|
|
|
if (p->arg.lock_seqid == NULL)
|
2008-01-09 05:56:07 +07:00
|
|
|
goto out_free_seqid;
|
2006-01-03 15:55:17 +07:00
|
|
|
p->arg.lock_stateid = &lsp->ls_stateid;
|
2006-08-23 07:06:09 +07:00
|
|
|
p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
|
2012-01-18 10:04:25 +07:00
|
|
|
p->arg.lock_owner.id = lsp->ls_seqid.owner_id;
|
2010-12-21 22:45:27 +07:00
|
|
|
p->arg.lock_owner.s_dev = server->s_dev;
|
2008-04-08 00:20:54 +07:00
|
|
|
p->res.lock_seqid = p->arg.lock_seqid;
|
2006-01-03 15:55:17 +07:00
|
|
|
p->lsp = lsp;
|
2009-04-01 20:22:22 +07:00
|
|
|
p->server = server;
|
2006-01-03 15:55:17 +07:00
|
|
|
atomic_inc(&lsp->ls_count);
|
|
|
|
p->ctx = get_nfs_open_context(ctx);
|
|
|
|
memcpy(&p->fl, fl, sizeof(p->fl));
|
|
|
|
return p;
|
2008-01-09 05:56:07 +07:00
|
|
|
out_free_seqid:
|
|
|
|
nfs_free_seqid(p->arg.open_seqid);
|
2006-01-03 15:55:17 +07:00
|
|
|
out_free:
|
|
|
|
kfree(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_lockdata *data = calldata;
|
|
|
|
struct nfs4_state *state = data->lsp->ls_state;
|
2005-10-19 04:20:15 +07:00
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: begin!\n", __func__);
|
2008-01-09 05:56:07 +07:00
|
|
|
if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0)
|
2013-02-12 07:01:21 +07:00
|
|
|
goto out_wait;
|
2006-01-03 15:55:17 +07:00
|
|
|
/* Do we need to do an open_to_lock_owner? */
|
|
|
|
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
|
2012-10-30 06:02:20 +07:00
|
|
|
if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) {
|
2012-10-30 05:37:40 +07:00
|
|
|
goto out_release_lock_seqid;
|
2012-10-30 06:02:20 +07:00
|
|
|
}
|
2013-04-20 12:30:53 +07:00
|
|
|
data->arg.open_stateid = &state->open_stateid;
|
2006-01-03 15:55:17 +07:00
|
|
|
data->arg.new_lock_owner = 1;
|
2008-04-08 00:20:54 +07:00
|
|
|
data->res.open_seqid = data->arg.open_seqid;
|
2008-01-09 05:56:07 +07:00
|
|
|
} else
|
|
|
|
data->arg.new_lock_owner = 0;
|
2013-03-15 03:57:48 +07:00
|
|
|
if (!nfs4_valid_open_stateid(state)) {
|
|
|
|
data->rpc_status = -EBADF;
|
|
|
|
task->tk_action = NULL;
|
|
|
|
goto out_release_open_seqid;
|
|
|
|
}
|
2006-01-03 15:55:21 +07:00
|
|
|
data->timestamp = jiffies;
|
2010-06-16 20:52:26 +07:00
|
|
|
if (nfs4_setup_sequence(data->server,
|
|
|
|
&data->arg.seq_args,
|
2012-10-30 05:37:40 +07:00
|
|
|
&data->res.seq_res,
|
2012-10-23 07:28:44 +07:00
|
|
|
task) == 0)
|
2009-04-01 20:22:22 +07:00
|
|
|
return;
|
2013-03-15 03:57:48 +07:00
|
|
|
out_release_open_seqid:
|
2012-10-30 05:37:40 +07:00
|
|
|
nfs_release_seqid(data->arg.open_seqid);
|
|
|
|
out_release_lock_seqid:
|
|
|
|
nfs_release_seqid(data->arg.lock_seqid);
|
2013-02-12 07:01:21 +07:00
|
|
|
out_wait:
|
|
|
|
nfs4_sequence_done(task, &data->res.seq_res);
|
2012-10-30 06:02:20 +07:00
|
|
|
dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
|
2009-12-15 12:27:57 +07:00
|
|
|
}
|
|
|
|
|
2006-01-03 15:55:17 +07:00
|
|
|
static void nfs4_lock_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_lockdata *data = calldata;
|
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: begin!\n", __func__);
|
2006-01-03 15:55:17 +07:00
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs4_sequence_done(task, &data->res.seq_res))
|
|
|
|
return;
|
2009-04-01 20:22:22 +07:00
|
|
|
|
2006-01-03 15:55:17 +07:00
|
|
|
data->rpc_status = task->tk_status;
|
|
|
|
if (data->arg.new_lock_owner != 0) {
|
|
|
|
if (data->rpc_status == 0)
|
|
|
|
nfs_confirm_seqid(&data->lsp->ls_seqid, 0);
|
|
|
|
else
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (data->rpc_status == 0) {
|
2012-03-05 06:13:56 +07:00
|
|
|
nfs4_stateid_copy(&data->lsp->ls_stateid, &data->res.stateid);
|
2012-09-11 00:26:49 +07:00
|
|
|
set_bit(NFS_LOCK_INITIALIZED, &data->lsp->ls_flags);
|
2011-06-23 05:40:12 +07:00
|
|
|
renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp);
|
2006-01-03 15:55:17 +07:00
|
|
|
}
|
|
|
|
out:
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: done, ret = %d!\n", __func__, data->rpc_status);
|
2006-01-03 15:55:17 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_lock_release(void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_lockdata *data = calldata;
|
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: begin!\n", __func__);
|
2008-01-09 05:56:07 +07:00
|
|
|
nfs_free_seqid(data->arg.open_seqid);
|
2006-01-03 15:55:17 +07:00
|
|
|
if (data->cancelled != 0) {
|
|
|
|
struct rpc_task *task;
|
|
|
|
task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
|
|
|
|
data->arg.lock_seqid);
|
|
|
|
if (!IS_ERR(task))
|
2011-02-22 02:05:41 +07:00
|
|
|
rpc_put_task_async(task);
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: cancelling lock!\n", __func__);
|
2006-01-03 15:55:17 +07:00
|
|
|
} else
|
|
|
|
nfs_free_seqid(data->arg.lock_seqid);
|
|
|
|
nfs4_put_lock_state(data->lsp);
|
|
|
|
put_nfs_open_context(data->ctx);
|
|
|
|
kfree(data);
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: done!\n", __func__);
|
2006-01-03 15:55:17 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_lock_ops = {
|
|
|
|
.rpc_call_prepare = nfs4_lock_prepare,
|
|
|
|
.rpc_call_done = nfs4_lock_done,
|
|
|
|
.rpc_release = nfs4_lock_release,
|
|
|
|
};
|
|
|
|
|
2010-01-27 03:42:21 +07:00
|
|
|
static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error)
|
|
|
|
{
|
|
|
|
switch (error) {
|
|
|
|
case -NFS4ERR_ADMIN_REVOKED:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
2011-03-10 04:00:56 +07:00
|
|
|
lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
|
2010-01-27 03:42:21 +07:00
|
|
|
if (new_lock_owner != 0 ||
|
2012-09-11 00:26:49 +07:00
|
|
|
test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0)
|
2011-03-10 04:00:56 +07:00
|
|
|
nfs4_schedule_stateid_recovery(server, lsp->ls_state);
|
2010-01-27 03:42:47 +07:00
|
|
|
break;
|
|
|
|
case -NFS4ERR_STALE_STATEID:
|
|
|
|
lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
|
2011-03-10 04:00:56 +07:00
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
nfs4_schedule_lease_recovery(server->nfs_client);
|
2010-01-27 03:42:21 +07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2009-12-09 16:50:14 +07:00
|
|
|
static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
|
2006-01-03 15:55:17 +07:00
|
|
|
{
|
|
|
|
struct nfs4_lockdata *data;
|
|
|
|
struct rpc_task *task;
|
2007-07-15 02:40:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK],
|
|
|
|
.rpc_cred = state->owner->so_cred,
|
|
|
|
};
|
2007-07-15 02:39:59 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = NFS_CLIENT(state->inode),
|
2007-07-15 02:40:01 +07:00
|
|
|
.rpc_message = &msg,
|
2007-07-15 02:39:59 +07:00
|
|
|
.callback_ops = &nfs4_lock_ops,
|
2008-02-20 08:04:23 +07:00
|
|
|
.workqueue = nfsiod_workqueue,
|
2007-07-15 02:39:59 +07:00
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2006-01-03 15:55:17 +07:00
|
|
|
int ret;
|
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: begin!\n", __func__);
|
2007-08-11 04:44:32 +07:00
|
|
|
data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file),
|
2010-05-13 23:51:01 +07:00
|
|
|
fl->fl_u.nfs4_fl.owner,
|
|
|
|
recovery_type == NFS_LOCK_NEW ? GFP_KERNEL : GFP_NOFS);
|
2006-01-03 15:55:17 +07:00
|
|
|
if (data == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (IS_SETLKW(cmd))
|
|
|
|
data->arg.block = 1;
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1);
|
2010-12-21 22:52:24 +07:00
|
|
|
msg.rpc_argp = &data->arg;
|
|
|
|
msg.rpc_resp = &data->res;
|
2007-07-15 02:39:59 +07:00
|
|
|
task_setup_data.callback_data = data;
|
2012-10-30 06:02:20 +07:00
|
|
|
if (recovery_type > NFS_LOCK_NEW) {
|
|
|
|
if (recovery_type == NFS_LOCK_RECLAIM)
|
|
|
|
data->arg.reclaim = NFS_LOCK_RECLAIM;
|
|
|
|
nfs4_set_sequence_privileged(&data->arg.seq_args);
|
|
|
|
}
|
2007-07-15 02:39:59 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
2006-03-21 06:11:10 +07:00
|
|
|
if (IS_ERR(task))
|
2006-01-03 15:55:17 +07:00
|
|
|
return PTR_ERR(task);
|
|
|
|
ret = nfs4_wait_for_completion_rpc_task(task);
|
|
|
|
if (ret == 0) {
|
|
|
|
ret = data->rpc_status;
|
2010-01-27 03:42:21 +07:00
|
|
|
if (ret)
|
|
|
|
nfs4_handle_setlk_error(data->server, data->lsp,
|
|
|
|
data->arg.new_lock_owner, ret);
|
2006-01-03 15:55:17 +07:00
|
|
|
} else
|
|
|
|
data->cancelled = 1;
|
2006-11-12 10:18:03 +07:00
|
|
|
rpc_put_task(task);
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: done, ret = %d!\n", __func__, ret);
|
2006-01-03 15:55:17 +07:00
|
|
|
return ret;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
|
|
|
|
{
|
2005-06-23 00:16:29 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
2012-04-18 23:20:10 +07:00
|
|
|
struct nfs4_exception exception = {
|
|
|
|
.inode = state->inode,
|
|
|
|
};
|
2005-06-23 00:16:29 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
2006-06-30 03:38:36 +07:00
|
|
|
/* Cache the lock if possible... */
|
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
|
|
|
|
return 0;
|
2009-12-09 16:50:14 +07:00
|
|
|
err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
|
2010-10-20 06:47:49 +07:00
|
|
|
if (err != -NFS4ERR_DELAY)
|
2005-06-23 00:16:29 +07:00
|
|
|
break;
|
|
|
|
nfs4_handle_exception(server, err, &exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request)
|
|
|
|
{
|
2005-06-23 00:16:29 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
2012-04-18 23:20:10 +07:00
|
|
|
struct nfs4_exception exception = {
|
|
|
|
.inode = state->inode,
|
|
|
|
};
|
2005-06-23 00:16:29 +07:00
|
|
|
int err;
|
|
|
|
|
2005-11-05 03:39:36 +07:00
|
|
|
err = nfs4_set_lock_state(state, request);
|
|
|
|
if (err != 0)
|
|
|
|
return err;
|
2005-06-23 00:16:29 +07:00
|
|
|
do {
|
2006-06-30 03:38:36 +07:00
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
|
|
|
|
return 0;
|
2009-12-09 16:50:14 +07:00
|
|
|
err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED);
|
2009-12-04 03:53:21 +07:00
|
|
|
switch (err) {
|
|
|
|
default:
|
|
|
|
goto out;
|
|
|
|
case -NFS4ERR_GRACE:
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
nfs4_handle_exception(server, err, &exception);
|
|
|
|
err = 0;
|
|
|
|
}
|
2005-06-23 00:16:29 +07:00
|
|
|
} while (exception.retry);
|
2009-12-04 03:53:21 +07:00
|
|
|
out:
|
2005-06-23 00:16:29 +07:00
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2011-06-03 01:59:10 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
2012-07-12 03:30:14 +07:00
|
|
|
/**
|
|
|
|
* nfs41_check_expired_locks - possibly free a lock stateid
|
|
|
|
*
|
|
|
|
* @state: NFSv4 state for an inode
|
|
|
|
*
|
|
|
|
* Returns NFS_OK if recovery for this stateid is now finished.
|
|
|
|
* Otherwise a negative NFS4ERR value is returned.
|
|
|
|
*/
|
2012-01-31 22:39:30 +07:00
|
|
|
static int nfs41_check_expired_locks(struct nfs4_state *state)
|
2011-06-03 01:59:10 +07:00
|
|
|
{
|
2012-07-12 03:30:05 +07:00
|
|
|
int status, ret = -NFS4ERR_BAD_STATEID;
|
2012-01-31 22:39:30 +07:00
|
|
|
struct nfs4_lock_state *lsp;
|
2011-06-03 01:59:10 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
|
|
|
2012-01-31 22:39:30 +07:00
|
|
|
list_for_each_entry(lsp, &state->lock_states, ls_locks) {
|
2012-09-11 00:26:49 +07:00
|
|
|
if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
|
2013-05-20 22:20:27 +07:00
|
|
|
struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
|
|
|
|
|
|
|
|
status = nfs41_test_stateid(server,
|
|
|
|
&lsp->ls_stateid,
|
|
|
|
cred);
|
2012-01-31 22:39:30 +07:00
|
|
|
if (status != NFS_OK) {
|
2012-07-12 03:30:14 +07:00
|
|
|
/* Free the stateid unless the server
|
|
|
|
* informs us the stateid is unrecognized. */
|
2012-07-12 03:29:56 +07:00
|
|
|
if (status != -NFS4ERR_BAD_STATEID)
|
|
|
|
nfs41_free_stateid(server,
|
2013-05-20 22:20:27 +07:00
|
|
|
&lsp->ls_stateid,
|
|
|
|
cred);
|
2012-09-11 00:26:49 +07:00
|
|
|
clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
|
2012-01-31 22:39:30 +07:00
|
|
|
ret = status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
|
|
|
|
{
|
|
|
|
int status = NFS_OK;
|
|
|
|
|
|
|
|
if (test_bit(LK_STATE_IN_USE, &state->flags))
|
|
|
|
status = nfs41_check_expired_locks(state);
|
2012-07-12 03:30:05 +07:00
|
|
|
if (status != NFS_OK)
|
|
|
|
status = nfs4_lock_expired(state, request);
|
|
|
|
return status;
|
2011-06-03 01:59:10 +07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
|
|
|
{
|
2013-02-05 08:17:49 +07:00
|
|
|
struct nfs4_state_owner *sp = state->owner;
|
2008-12-24 03:21:44 +07:00
|
|
|
struct nfs_inode *nfsi = NFS_I(state->inode);
|
2006-06-30 03:38:39 +07:00
|
|
|
unsigned char fl_flags = request->fl_flags;
|
2013-02-05 08:17:49 +07:00
|
|
|
unsigned int seq;
|
2010-01-27 03:42:30 +07:00
|
|
|
int status = -ENOLCK;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-01-27 03:42:30 +07:00
|
|
|
if ((fl_flags & FL_POSIX) &&
|
|
|
|
!test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
|
|
|
|
goto out;
|
2005-11-05 03:39:36 +07:00
|
|
|
/* Is this a delegated open? */
|
|
|
|
status = nfs4_set_lock_state(state, request);
|
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
2006-06-30 03:38:39 +07:00
|
|
|
request->fl_flags |= FL_ACCESS;
|
|
|
|
status = do_vfs_lock(request->fl_file, request);
|
|
|
|
if (status < 0)
|
|
|
|
goto out;
|
2008-12-24 03:21:44 +07:00
|
|
|
down_read(&nfsi->rwsem);
|
2006-06-30 03:38:39 +07:00
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
|
|
|
|
/* Yes: cache locks! */
|
|
|
|
/* ...but avoid races with delegation recall... */
|
2008-12-24 03:21:44 +07:00
|
|
|
request->fl_flags = fl_flags & ~FL_SLEEP;
|
|
|
|
status = do_vfs_lock(request->fl_file, request);
|
|
|
|
goto out_unlock;
|
2006-06-30 03:38:39 +07:00
|
|
|
}
|
2013-02-05 08:17:49 +07:00
|
|
|
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
|
|
|
|
up_read(&nfsi->rwsem);
|
2009-12-09 16:50:14 +07:00
|
|
|
status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW);
|
2005-11-05 03:39:36 +07:00
|
|
|
if (status != 0)
|
2013-02-05 08:17:49 +07:00
|
|
|
goto out;
|
|
|
|
down_read(&nfsi->rwsem);
|
|
|
|
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) {
|
|
|
|
status = -NFS4ERR_DELAY;
|
2006-06-30 03:38:39 +07:00
|
|
|
goto out_unlock;
|
2013-02-05 08:17:49 +07:00
|
|
|
}
|
2005-11-05 03:39:36 +07:00
|
|
|
/* Note: we always want to sleep here! */
|
2006-06-30 03:38:39 +07:00
|
|
|
request->fl_flags = fl_flags | FL_SLEEP;
|
2005-11-05 03:39:36 +07:00
|
|
|
if (do_vfs_lock(request->fl_file, request) < 0)
|
2012-01-27 01:32:23 +07:00
|
|
|
printk(KERN_WARNING "NFS: %s: VFS is out of sync with lock "
|
|
|
|
"manager!\n", __func__);
|
2006-06-30 03:38:39 +07:00
|
|
|
out_unlock:
|
2008-12-24 03:21:44 +07:00
|
|
|
up_read(&nfsi->rwsem);
|
2006-06-30 03:38:39 +07:00
|
|
|
out:
|
|
|
|
request->fl_flags = fl_flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
|
|
|
|
{
|
2012-03-06 07:56:44 +07:00
|
|
|
struct nfs4_exception exception = {
|
|
|
|
.state = state,
|
2012-04-18 23:20:10 +07:00
|
|
|
.inode = state->inode,
|
2012-03-06 07:56:44 +07:00
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
2009-06-18 03:22:59 +07:00
|
|
|
err = _nfs4_proc_setlk(state, cmd, request);
|
|
|
|
if (err == -NFS4ERR_DENIED)
|
|
|
|
err = -EAGAIN;
|
2005-04-17 05:20:36 +07:00
|
|
|
err = nfs4_handle_exception(NFS_SERVER(state->inode),
|
2009-06-18 03:22:59 +07:00
|
|
|
err, &exception);
|
2005-04-17 05:20:36 +07:00
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
|
|
|
|
{
|
|
|
|
struct nfs_open_context *ctx;
|
|
|
|
struct nfs4_state *state;
|
|
|
|
unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
/* verify open state */
|
2007-08-11 04:44:32 +07:00
|
|
|
ctx = nfs_file_open_context(filp);
|
2005-04-17 05:20:36 +07:00
|
|
|
state = ctx->state;
|
|
|
|
|
|
|
|
if (request->fl_start < 0 || request->fl_end < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-07-22 06:22:38 +07:00
|
|
|
if (IS_GETLK(cmd)) {
|
|
|
|
if (state != NULL)
|
|
|
|
return nfs4_proc_getlk(state, F_GETLK, request);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if (!(IS_SETLK(cmd) || IS_SETLKW(cmd)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-07-22 06:22:38 +07:00
|
|
|
if (request->fl_type == F_UNLCK) {
|
|
|
|
if (state != NULL)
|
|
|
|
return nfs4_proc_unlck(state, cmd, request);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2009-07-22 06:22:38 +07:00
|
|
|
if (state == NULL)
|
|
|
|
return -ENOLCK;
|
2012-04-18 23:48:35 +07:00
|
|
|
/*
|
|
|
|
* Don't rely on the VFS having checked the file open mode,
|
|
|
|
* since it won't do this for flock() locks.
|
|
|
|
*/
|
2012-07-24 02:49:56 +07:00
|
|
|
switch (request->fl_type) {
|
2012-04-18 23:48:35 +07:00
|
|
|
case F_RDLCK:
|
|
|
|
if (!(filp->f_mode & FMODE_READ))
|
|
|
|
return -EBADF;
|
|
|
|
break;
|
|
|
|
case F_WRLCK:
|
|
|
|
if (!(filp->f_mode & FMODE_WRITE))
|
|
|
|
return -EBADF;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
|
|
|
status = nfs4_proc_setlk(state, cmd, request);
|
|
|
|
if ((status != -EAGAIN) || IS_SETLK(cmd))
|
|
|
|
break;
|
|
|
|
timeout = nfs4_set_lock_task_retry(timeout);
|
|
|
|
status = -ERESTARTSYS;
|
|
|
|
if (signalled())
|
|
|
|
break;
|
|
|
|
} while(status < 0);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2013-04-02 02:56:46 +07:00
|
|
|
int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid)
|
2005-11-05 03:38:11 +07:00
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(state->inode);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = nfs4_set_lock_state(state, fl);
|
|
|
|
if (err != 0)
|
2013-04-02 02:56:46 +07:00
|
|
|
return err;
|
2013-04-02 01:47:22 +07:00
|
|
|
err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
|
2013-04-02 02:56:46 +07:00
|
|
|
return nfs4_handle_delegation_recall_error(server, state, stateid, err);
|
2005-11-05 03:38:11 +07:00
|
|
|
}
|
2005-06-23 00:16:22 +07:00
|
|
|
|
2012-03-08 01:49:12 +07:00
|
|
|
struct nfs_release_lockowner_data {
|
|
|
|
struct nfs4_lock_state *lsp;
|
2012-03-20 03:17:18 +07:00
|
|
|
struct nfs_server *server;
|
2012-03-08 01:49:12 +07:00
|
|
|
struct nfs_release_lockowner_args args;
|
|
|
|
};
|
|
|
|
|
2010-07-01 23:49:01 +07:00
|
|
|
static void nfs4_release_lockowner_release(void *calldata)
|
|
|
|
{
|
2012-03-08 01:49:12 +07:00
|
|
|
struct nfs_release_lockowner_data *data = calldata;
|
2012-03-20 03:17:18 +07:00
|
|
|
nfs4_free_lock_state(data->server, data->lsp);
|
2010-07-01 23:49:01 +07:00
|
|
|
kfree(calldata);
|
|
|
|
}
|
|
|
|
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct rpc_call_ops nfs4_release_lockowner_ops = {
|
2010-07-01 23:49:01 +07:00
|
|
|
.rpc_release = nfs4_release_lockowner_release,
|
|
|
|
};
|
|
|
|
|
2013-05-04 03:22:55 +07:00
|
|
|
static int nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
|
2010-07-01 23:49:01 +07:00
|
|
|
{
|
2012-03-08 01:49:12 +07:00
|
|
|
struct nfs_release_lockowner_data *data;
|
2010-07-01 23:49:01 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
|
|
|
|
};
|
|
|
|
|
|
|
|
if (server->nfs_client->cl_mvops->minor_version != 0)
|
2012-03-08 01:49:12 +07:00
|
|
|
return -EINVAL;
|
|
|
|
data = kmalloc(sizeof(*data), GFP_NOFS);
|
|
|
|
if (!data)
|
|
|
|
return -ENOMEM;
|
|
|
|
data->lsp = lsp;
|
2012-03-20 03:17:18 +07:00
|
|
|
data->server = server;
|
2012-03-08 01:49:12 +07:00
|
|
|
data->args.lock_owner.clientid = server->nfs_client->cl_clientid;
|
|
|
|
data->args.lock_owner.id = lsp->ls_seqid.owner_id;
|
|
|
|
data->args.lock_owner.s_dev = server->s_dev;
|
|
|
|
msg.rpc_argp = &data->args;
|
|
|
|
rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
|
|
|
|
return 0;
|
2010-07-01 23:49:01 +07:00
|
|
|
}
|
|
|
|
|
2005-06-23 00:16:22 +07:00
|
|
|
#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
|
|
|
|
|
2010-12-09 18:35:25 +07:00
|
|
|
static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
|
|
|
|
const void *buf, size_t buflen,
|
|
|
|
int flags, int type)
|
2005-06-23 00:16:22 +07:00
|
|
|
{
|
2010-12-09 18:35:25 +07:00
|
|
|
if (strcmp(key, "") != 0)
|
|
|
|
return -EINVAL;
|
2005-06-23 00:16:23 +07:00
|
|
|
|
2010-12-09 18:35:25 +07:00
|
|
|
return nfs4_proc_set_acl(dentry->d_inode, buf, buflen);
|
2005-06-23 00:16:22 +07:00
|
|
|
}
|
|
|
|
|
2010-12-09 18:35:25 +07:00
|
|
|
static int nfs4_xattr_get_nfs4_acl(struct dentry *dentry, const char *key,
|
|
|
|
void *buf, size_t buflen, int type)
|
2005-06-23 00:16:22 +07:00
|
|
|
{
|
2010-12-09 18:35:25 +07:00
|
|
|
if (strcmp(key, "") != 0)
|
|
|
|
return -EINVAL;
|
2005-06-23 00:16:22 +07:00
|
|
|
|
2010-12-09 18:35:25 +07:00
|
|
|
return nfs4_proc_get_acl(dentry->d_inode, buf, buflen);
|
2005-06-23 00:16:22 +07:00
|
|
|
}
|
|
|
|
|
2010-12-09 18:35:25 +07:00
|
|
|
static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
|
|
|
|
size_t list_len, const char *name,
|
|
|
|
size_t name_len, int type)
|
2005-06-23 00:16:22 +07:00
|
|
|
{
|
2010-12-09 18:35:25 +07:00
|
|
|
size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
|
2005-06-23 00:16:22 +07:00
|
|
|
|
2006-03-21 11:23:42 +07:00
|
|
|
if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
|
|
|
|
return 0;
|
2010-12-09 18:35:25 +07:00
|
|
|
|
|
|
|
if (list && len <= list_len)
|
|
|
|
memcpy(list, XATTR_NAME_NFSV4_ACL, len);
|
2005-06-23 00:16:22 +07:00
|
|
|
return len;
|
2005-06-23 00:16:22 +07:00
|
|
|
}
|
|
|
|
|
2011-06-14 05:25:56 +07:00
|
|
|
/*
|
|
|
|
* nfs_fhget will use either the mounted_on_fileid or the fileid
|
|
|
|
*/
|
2009-03-12 01:10:28 +07:00
|
|
|
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
|
|
|
|
{
|
2011-06-14 05:25:56 +07:00
|
|
|
if (!(((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) ||
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_FILEID)) &&
|
|
|
|
(fattr->valid & NFS_ATTR_FATTR_FSID) &&
|
2012-03-02 05:01:57 +07:00
|
|
|
(fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)))
|
2009-03-12 01:10:28 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
|
2012-03-02 05:01:57 +07:00
|
|
|
NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_V4_REFERRAL;
|
2009-03-12 01:10:28 +07:00
|
|
|
fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
|
|
|
fattr->nlink = 2;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:41 +07:00
|
|
|
static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
|
|
|
|
const struct qstr *name,
|
|
|
|
struct nfs4_fs_locations *fs_locations,
|
|
|
|
struct page *page)
|
2006-06-09 20:34:22 +07:00
|
|
|
{
|
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
|
|
u32 bitmask[2] = {
|
2006-06-09 20:34:24 +07:00
|
|
|
[0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
|
2006-06-09 20:34:22 +07:00
|
|
|
};
|
|
|
|
struct nfs4_fs_locations_arg args = {
|
|
|
|
.dir_fh = NFS_FH(dir),
|
2007-01-13 14:28:11 +07:00
|
|
|
.name = name,
|
2006-06-09 20:34:22 +07:00
|
|
|
.page = page,
|
|
|
|
.bitmask = bitmask,
|
|
|
|
};
|
2009-04-01 20:22:02 +07:00
|
|
|
struct nfs4_fs_locations_res res = {
|
|
|
|
.fs_locations = fs_locations,
|
|
|
|
};
|
2006-06-09 20:34:22 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
|
|
|
|
.rpc_argp = &args,
|
2009-04-01 20:22:02 +07:00
|
|
|
.rpc_resp = &res,
|
2006-06-09 20:34:22 +07:00
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: start\n", __func__);
|
2011-06-14 05:25:56 +07:00
|
|
|
|
|
|
|
/* Ask for the fileid of the absent filesystem if mounted_on_fileid
|
|
|
|
* is not supported */
|
|
|
|
if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
|
|
|
|
bitmask[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;
|
|
|
|
else
|
|
|
|
bitmask[0] |= FATTR4_WORD0_FILEID;
|
|
|
|
|
2007-01-13 14:28:11 +07:00
|
|
|
nfs_fattr_init(&fs_locations->fattr);
|
2006-06-09 20:34:22 +07:00
|
|
|
fs_locations->server = server;
|
2006-06-09 20:34:25 +07:00
|
|
|
fs_locations->nlocations = 0;
|
2012-04-28 00:27:41 +07:00
|
|
|
status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2008-05-03 03:42:44 +07:00
|
|
|
dprintk("%s: returned status = %d\n", __func__, status);
|
2006-06-09 20:34:22 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:41 +07:00
|
|
|
int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
|
|
|
|
const struct qstr *name,
|
|
|
|
struct nfs4_fs_locations *fs_locations,
|
|
|
|
struct page *page)
|
2012-04-28 00:27:39 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir),
|
2012-04-28 00:27:41 +07:00
|
|
|
_nfs4_proc_fs_locations(client, dir, name, fs_locations, page),
|
2012-04-28 00:27:39 +07:00
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-03-25 00:12:29 +07:00
|
|
|
static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct nfs4_secinfo_arg args = {
|
|
|
|
.dir_fh = NFS_FH(dir),
|
|
|
|
.name = name,
|
|
|
|
};
|
|
|
|
struct nfs4_secinfo_res res = {
|
|
|
|
.flavors = flavors,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
|
|
|
|
dprintk("NFS call secinfo %s\n", name->name);
|
|
|
|
status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
|
|
|
|
dprintk("NFS reply secinfo: %d\n", status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:27:40 +07:00
|
|
|
int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,
|
|
|
|
struct nfs4_secinfo_flavors *flavors)
|
2011-03-25 00:12:29 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(NFS_SERVER(dir),
|
|
|
|
_nfs4_proc_secinfo(dir, name, flavors),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:21:53 +07:00
|
|
|
#ifdef CONFIG_NFS_V4_1
|
2010-12-14 22:11:57 +07:00
|
|
|
/*
|
|
|
|
* Check the exchange flags returned by the server for invalid flags, having
|
|
|
|
* both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or
|
|
|
|
* DS flags set.
|
|
|
|
*/
|
|
|
|
static int nfs4_check_cl_exchange_flags(u32 flags)
|
|
|
|
{
|
|
|
|
if (flags & ~EXCHGID4_FLAG_MASK_R)
|
|
|
|
goto out_inval;
|
|
|
|
if ((flags & EXCHGID4_FLAG_USE_PNFS_MDS) &&
|
|
|
|
(flags & EXCHGID4_FLAG_USE_NON_PNFS))
|
|
|
|
goto out_inval;
|
|
|
|
if (!(flags & (EXCHGID4_FLAG_MASK_PNFS)))
|
|
|
|
goto out_inval;
|
|
|
|
return NFS_OK;
|
|
|
|
out_inval:
|
|
|
|
return -NFS4ERR_INVAL;
|
|
|
|
}
|
|
|
|
|
2011-06-01 06:05:47 +07:00
|
|
|
static bool
|
2012-05-22 09:44:31 +07:00
|
|
|
nfs41_same_server_scope(struct nfs41_server_scope *a,
|
|
|
|
struct nfs41_server_scope *b)
|
2011-06-01 06:05:47 +07:00
|
|
|
{
|
|
|
|
if (a->server_scope_sz == b->server_scope_sz &&
|
|
|
|
memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-25 00:22:50 +07:00
|
|
|
/*
|
|
|
|
* nfs4_proc_bind_conn_to_session()
|
|
|
|
*
|
|
|
|
* The 4.1 client currently uses the same TCP connection for the
|
|
|
|
* fore and backchannel.
|
|
|
|
*/
|
2012-05-26 04:57:41 +07:00
|
|
|
int nfs4_proc_bind_conn_to_session(struct nfs_client *clp, struct rpc_cred *cred)
|
2012-05-25 00:22:50 +07:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct nfs41_bind_conn_to_session_res res;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc =
|
|
|
|
&nfs4_procedures[NFSPROC4_CLNT_BIND_CONN_TO_SESSION],
|
|
|
|
.rpc_argp = clp,
|
|
|
|
.rpc_resp = &res,
|
2012-05-26 04:57:41 +07:00
|
|
|
.rpc_cred = cred,
|
2012-05-25 00:22:50 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
|
|
|
|
res.session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
|
|
|
|
if (unlikely(res.session == NULL)) {
|
|
|
|
status = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
|
|
|
if (status == 0) {
|
|
|
|
if (memcmp(res.session->sess_id.data,
|
|
|
|
clp->cl_session->sess_id.data, NFS4_MAX_SESSIONID_LEN)) {
|
|
|
|
dprintk("NFS: %s: Session ID mismatch\n", __func__);
|
|
|
|
status = -EIO;
|
|
|
|
goto out_session;
|
|
|
|
}
|
|
|
|
if (res.dir != NFS4_CDFS4_BOTH) {
|
|
|
|
dprintk("NFS: %s: Unexpected direction from server\n",
|
|
|
|
__func__);
|
|
|
|
status = -EIO;
|
|
|
|
goto out_session;
|
|
|
|
}
|
|
|
|
if (res.use_conn_in_rdma_mode) {
|
|
|
|
dprintk("NFS: %s: Server returned RDMA mode = true\n",
|
|
|
|
__func__);
|
|
|
|
status = -EIO;
|
|
|
|
goto out_session;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out_session:
|
|
|
|
kfree(res.session);
|
|
|
|
out:
|
|
|
|
dprintk("<-- %s status= %d\n", __func__, status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:29 +07:00
|
|
|
/*
|
|
|
|
* nfs4_proc_exchange_id()
|
|
|
|
*
|
2012-07-12 03:30:59 +07:00
|
|
|
* Returns zero, a negative errno, or a negative NFS4ERR status code.
|
|
|
|
*
|
2009-04-01 20:22:29 +07:00
|
|
|
* Since the clientid has expired, all compounds using sessions
|
|
|
|
* associated with the stale clientid will be returning
|
|
|
|
* NFS4ERR_BADSESSION in the sequence operation, and will therefore
|
|
|
|
* be in some phase of session reset.
|
|
|
|
*/
|
2009-12-05 03:52:24 +07:00
|
|
|
int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
|
2009-04-01 20:22:29 +07:00
|
|
|
{
|
|
|
|
nfs4_verifier verifier;
|
|
|
|
struct nfs41_exchange_id_args args = {
|
2012-03-03 05:14:31 +07:00
|
|
|
.verifier = &verifier,
|
2009-04-01 20:22:29 +07:00
|
|
|
.client = clp,
|
2013-05-20 23:24:03 +07:00
|
|
|
.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
|
|
|
|
EXCHGID4_FLAG_BIND_PRINC_STATEID,
|
2009-04-01 20:22:29 +07:00
|
|
|
};
|
|
|
|
struct nfs41_exchange_id_res res = {
|
2012-05-27 00:41:04 +07:00
|
|
|
0
|
2009-04-01 20:22:29 +07:00
|
|
|
};
|
|
|
|
int status;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
.rpc_cred = cred,
|
|
|
|
};
|
|
|
|
|
NFS: Always use the same SETCLIENTID boot verifier
Currently our NFS client assigns a unique SETCLIENTID boot verifier
for each server IP address it knows about. It's set to CURRENT_TIME
when the struct nfs_client for that server IP is created.
During the SETCLIENTID operation, our client also presents an
nfs_client_id4 string to servers, as an identifier on which the server
can hang all of this client's NFSv4 state. Our client's
nfs_client_id4 string is unique for each server IP address.
An NFSv4 server is obligated to wipe all NFSv4 state associated with
an nfs_client_id4 string when the client presents the same
nfs_client_id4 string along with a changed SETCLIENTID boot verifier.
When our client unmounts the last of a server's shares, it destroys
that server's struct nfs_client. The next time the client mounts that
NFS server, it creates a fresh struct nfs_client with a fresh boot
verifier. On seeing the fresh verifer, the server wipes any previous
NFSv4 state associated with that nfs_client_id4.
However, NFSv4.1 clients are supposed to present the same
nfs_client_id4 string to all servers. And, to support Transparent
State Migration, the same nfs_client_id4 string should be presented
to all NFSv4.0 servers so they recognize that migrated state for this
client belongs with state a server may already have for this client.
(This is known as the Uniform Client String model).
If the nfs_client_id4 string is the same but the boot verifier changes
for each server IP address, SETCLIENTID and EXCHANGE_ID operations
from such a client could unintentionally result in a server wiping a
client's previously obtained lease.
Thus, if our NFS client is going to use a fixed nfs_client_id4 string,
either for NFSv4.0 or NFSv4.1 mounts, our NFS client should use a
boot verifier that does not change depending on server IP address.
Replace our current per-nfs_client boot verifier with a per-nfs_net
boot verifier.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-05-22 09:45:41 +07:00
|
|
|
nfs4_init_boot_verifier(clp, &verifier);
|
NFS: Use the same nfs_client_id4 for every server
Currently, when identifying itself to NFS servers, the Linux NFS
client uses a unique nfs_client_id4.id string for each server IP
address it talks with. For example, when client A talks to server X,
the client identifies itself using a string like "AX". The
requirements for these strings are specified in detail by RFC 3530
(and bis).
This form of client identification presents a problem for Transparent
State Migration. When client A's state on server X is migrated to
server Y, it continues to be associated with string "AX." But,
according to the rules of client string construction above, client
A will present string "AY" when communicating with server Y.
Server Y thus has no way to know that client A should be associated
with the state migrated from server X. "AX" is all but abandoned,
interfering with establishing fresh state for client A on server Y.
To support transparent state migration, then, NFSv4.0 clients must
instead use the same nfs_client_id4.id string to identify themselves
to every NFS server; something like "A".
Now a client identifies itself as "A" to server X. When a file
system on server X transitions to server Y, and client A identifies
itself as "A" to server Y, Y will know immediately that the state
associated with "A," whether it is native or migrated, is owned by
the client, and can merge both into a single lease.
As a pre-requisite to adding support for NFSv4 migration to the Linux
NFS client, this patch changes the way Linux identifies itself to NFS
servers via the SETCLIENTID (NFSv4 minor version 0) and EXCHANGE_ID
(NFSv4 minor version 1) operations.
In addition to removing the server's IP address from nfs_client_id4,
the Linux NFS client will also no longer use its own source IP address
as part of the nfs_client_id4 string. On multi-homed clients, the
value of this address depends on the address family and network
routing used to contact the server, thus it can be different for each
server.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-09-15 04:24:21 +07:00
|
|
|
args.id_len = nfs4_init_uniform_client_string(clp, args.id,
|
|
|
|
sizeof(args.id));
|
2012-07-12 03:30:59 +07:00
|
|
|
dprintk("NFS call exchange_id auth=%s, '%.*s'\n",
|
|
|
|
clp->cl_rpcclient->cl_auth->au_ops->au_name,
|
|
|
|
args.id_len, args.id);
|
2009-04-01 20:22:29 +07:00
|
|
|
|
2012-05-22 09:46:16 +07:00
|
|
|
res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
|
2012-05-25 03:31:39 +07:00
|
|
|
GFP_NOFS);
|
2012-05-22 09:46:16 +07:00
|
|
|
if (unlikely(res.server_owner == NULL)) {
|
2012-02-16 23:17:05 +07:00
|
|
|
status = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-06-01 06:05:47 +07:00
|
|
|
|
2012-05-22 09:44:31 +07:00
|
|
|
res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
|
2012-05-25 03:31:39 +07:00
|
|
|
GFP_NOFS);
|
2012-05-22 09:44:58 +07:00
|
|
|
if (unlikely(res.server_scope == NULL)) {
|
2012-02-16 23:17:05 +07:00
|
|
|
status = -ENOMEM;
|
2012-05-22 09:46:16 +07:00
|
|
|
goto out_server_owner;
|
2012-02-16 23:17:05 +07:00
|
|
|
}
|
2011-06-01 06:05:47 +07:00
|
|
|
|
2012-05-25 03:31:39 +07:00
|
|
|
res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
|
2012-05-22 09:44:58 +07:00
|
|
|
if (unlikely(res.impl_id == NULL)) {
|
2012-02-18 03:20:26 +07:00
|
|
|
status = -ENOMEM;
|
|
|
|
goto out_server_scope;
|
|
|
|
}
|
|
|
|
|
2011-04-25 01:29:33 +07:00
|
|
|
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
2012-05-22 09:44:58 +07:00
|
|
|
if (status == 0)
|
2012-05-27 00:41:04 +07:00
|
|
|
status = nfs4_check_cl_exchange_flags(res.flags);
|
2011-06-01 06:05:47 +07:00
|
|
|
|
2012-05-22 09:46:16 +07:00
|
|
|
if (status == 0) {
|
2012-05-27 00:41:04 +07:00
|
|
|
clp->cl_clientid = res.clientid;
|
|
|
|
clp->cl_exchange_flags = (res.flags & ~EXCHGID4_FLAG_CONFIRMED_R);
|
|
|
|
if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R))
|
|
|
|
clp->cl_seqid = res.seqid;
|
|
|
|
|
2012-05-22 09:46:16 +07:00
|
|
|
kfree(clp->cl_serverowner);
|
|
|
|
clp->cl_serverowner = res.server_owner;
|
|
|
|
res.server_owner = NULL;
|
2011-06-01 06:05:47 +07:00
|
|
|
|
2012-02-18 03:20:26 +07:00
|
|
|
/* use the most recent implementation id */
|
2012-05-22 09:44:41 +07:00
|
|
|
kfree(clp->cl_implid);
|
|
|
|
clp->cl_implid = res.impl_id;
|
2012-02-18 03:20:26 +07:00
|
|
|
|
2012-05-22 09:44:58 +07:00
|
|
|
if (clp->cl_serverscope != NULL &&
|
2012-05-22 09:44:31 +07:00
|
|
|
!nfs41_same_server_scope(clp->cl_serverscope,
|
2011-06-01 06:05:47 +07:00
|
|
|
res.server_scope)) {
|
|
|
|
dprintk("%s: server_scope mismatch detected\n",
|
|
|
|
__func__);
|
|
|
|
set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
|
2012-05-22 09:44:31 +07:00
|
|
|
kfree(clp->cl_serverscope);
|
|
|
|
clp->cl_serverscope = NULL;
|
2011-06-01 06:05:47 +07:00
|
|
|
}
|
|
|
|
|
2012-05-22 09:44:58 +07:00
|
|
|
if (clp->cl_serverscope == NULL) {
|
2012-05-22 09:44:31 +07:00
|
|
|
clp->cl_serverscope = res.server_scope;
|
2012-02-16 23:17:05 +07:00
|
|
|
goto out;
|
|
|
|
}
|
2012-05-27 00:41:04 +07:00
|
|
|
} else
|
|
|
|
kfree(res.impl_id);
|
2012-02-18 03:20:26 +07:00
|
|
|
|
2012-05-22 09:46:16 +07:00
|
|
|
out_server_owner:
|
|
|
|
kfree(res.server_owner);
|
2012-02-18 03:20:26 +07:00
|
|
|
out_server_scope:
|
2012-02-16 23:17:05 +07:00
|
|
|
kfree(res.server_scope);
|
|
|
|
out:
|
2012-05-22 09:44:58 +07:00
|
|
|
if (clp->cl_implid != NULL)
|
2012-07-12 03:30:59 +07:00
|
|
|
dprintk("NFS reply exchange_id: Server Implementation ID: "
|
2012-02-18 03:20:26 +07:00
|
|
|
"domain: %s, name: %s, date: %llu,%u\n",
|
2012-07-12 03:30:59 +07:00
|
|
|
clp->cl_implid->domain, clp->cl_implid->name,
|
2012-05-22 09:44:41 +07:00
|
|
|
clp->cl_implid->date.seconds,
|
|
|
|
clp->cl_implid->date.nseconds);
|
2012-07-12 03:30:59 +07:00
|
|
|
dprintk("NFS reply exchange_id: %d\n", status);
|
2009-04-01 20:22:29 +07:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-05-26 04:18:09 +07:00
|
|
|
static int _nfs4_proc_destroy_clientid(struct nfs_client *clp,
|
|
|
|
struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_CLIENTID],
|
|
|
|
.rpc_argp = clp,
|
|
|
|
.rpc_cred = cred,
|
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
|
|
|
if (status)
|
2012-06-08 00:45:53 +07:00
|
|
|
dprintk("NFS: Got error %d from the server %s on "
|
2012-05-26 04:18:09 +07:00
|
|
|
"DESTROY_CLIENTID.", status, clp->cl_hostname);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_destroy_clientid(struct nfs_client *clp,
|
|
|
|
struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
unsigned int loop;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
|
|
|
|
ret = _nfs4_proc_destroy_clientid(clp, cred);
|
|
|
|
switch (ret) {
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
case -NFS4ERR_CLIENTID_BUSY:
|
|
|
|
ssleep(1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfs4_destroy_clientid(struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
struct rpc_cred *cred;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (clp->cl_mvops->minor_version < 1)
|
|
|
|
goto out;
|
|
|
|
if (clp->cl_exchange_flags == 0)
|
|
|
|
goto out;
|
2012-09-15 04:24:32 +07:00
|
|
|
if (clp->cl_preserve_clid)
|
|
|
|
goto out;
|
2012-05-26 04:18:09 +07:00
|
|
|
cred = nfs4_get_exchange_id_cred(clp);
|
|
|
|
ret = nfs4_proc_destroy_clientid(clp, cred);
|
|
|
|
if (cred)
|
|
|
|
put_rpccred(cred);
|
|
|
|
switch (ret) {
|
|
|
|
case 0:
|
|
|
|
case -NFS4ERR_STALE_CLIENTID:
|
|
|
|
clp->cl_exchange_flags = 0;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:30 +07:00
|
|
|
struct nfs4_get_lease_time_data {
|
|
|
|
struct nfs4_get_lease_time_args *args;
|
|
|
|
struct nfs4_get_lease_time_res *res;
|
|
|
|
struct nfs_client *clp;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void nfs4_get_lease_time_prepare(struct rpc_task *task,
|
|
|
|
void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_get_lease_time_data *data =
|
|
|
|
(struct nfs4_get_lease_time_data *)calldata;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
/* just setup sequence, do not trigger session recovery
|
|
|
|
since we're invoked within one */
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs41_setup_sequence(data->clp->cl_session,
|
|
|
|
&data->args->la_seq_args,
|
|
|
|
&data->res->lr_seq_res,
|
|
|
|
task);
|
2009-04-01 20:22:30 +07:00
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called from nfs4_state_manager thread for session setup, so don't recover
|
|
|
|
* from sequence operation or clientid errors.
|
|
|
|
*/
|
|
|
|
static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_get_lease_time_data *data =
|
|
|
|
(struct nfs4_get_lease_time_data *)calldata;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs41_sequence_done(task, &data->res->lr_seq_res))
|
|
|
|
return;
|
2009-04-01 20:22:30 +07:00
|
|
|
switch (task->tk_status) {
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
case -NFS4ERR_GRACE:
|
|
|
|
dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
|
|
|
|
rpc_delay(task, NFS4_POLL_RETRY_MIN);
|
|
|
|
task->tk_status = 0;
|
2011-05-04 00:43:03 +07:00
|
|
|
/* fall through */
|
|
|
|
case -NFS4ERR_RETRY_UNCACHED_REP:
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2009-04-01 20:22:30 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct rpc_call_ops nfs4_get_lease_time_ops = {
|
2009-04-01 20:22:30 +07:00
|
|
|
.rpc_call_prepare = nfs4_get_lease_time_prepare,
|
|
|
|
.rpc_call_done = nfs4_get_lease_time_done,
|
|
|
|
};
|
|
|
|
|
|
|
|
int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
|
|
|
|
{
|
|
|
|
struct rpc_task *task;
|
|
|
|
struct nfs4_get_lease_time_args args;
|
|
|
|
struct nfs4_get_lease_time_res res = {
|
|
|
|
.lr_fsinfo = fsinfo,
|
|
|
|
};
|
|
|
|
struct nfs4_get_lease_time_data data = {
|
|
|
|
.args = &args,
|
|
|
|
.res = &res,
|
|
|
|
.clp = clp,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
struct rpc_task_setup task_setup = {
|
|
|
|
.rpc_client = clp->cl_rpcclient,
|
|
|
|
.rpc_message = &msg,
|
|
|
|
.callback_ops = &nfs4_get_lease_time_ops,
|
2011-04-25 01:29:33 +07:00
|
|
|
.callback_data = &data,
|
|
|
|
.flags = RPC_TASK_TIMEOUT,
|
2009-04-01 20:22:30 +07:00
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0);
|
2012-10-30 06:02:20 +07:00
|
|
|
nfs4_set_sequence_privileged(&args.la_seq_args);
|
2009-04-01 20:22:30 +07:00
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
task = rpc_run_task(&task_setup);
|
|
|
|
|
|
|
|
if (IS_ERR(task))
|
|
|
|
status = PTR_ERR(task);
|
|
|
|
else {
|
|
|
|
status = task->tk_status;
|
|
|
|
rpc_put_task(task);
|
|
|
|
}
|
|
|
|
dprintk("<-- %s return %d\n", __func__, status);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:31 +07:00
|
|
|
/*
|
|
|
|
* Initialize the values to be used by the client in CREATE_SESSION
|
|
|
|
* If nfs4_init_session set the fore channel request and response sizes,
|
|
|
|
* use them.
|
|
|
|
*
|
|
|
|
* Set the back channel max_resp_sz_cached to zero to force the client to
|
|
|
|
* always set csa_cachethis to FALSE because the current implementation
|
|
|
|
* of the back channel DRC only supports caching the CB_SEQUENCE operation.
|
|
|
|
*/
|
|
|
|
static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
|
|
|
|
{
|
|
|
|
struct nfs4_session *session = args->client->cl_session;
|
2012-11-20 23:02:55 +07:00
|
|
|
unsigned int mxrqst_sz = session->fc_target_max_rqst_sz,
|
|
|
|
mxresp_sz = session->fc_target_max_resp_sz;
|
2009-04-01 20:22:31 +07:00
|
|
|
|
|
|
|
if (mxrqst_sz == 0)
|
|
|
|
mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
|
|
|
|
if (mxresp_sz == 0)
|
|
|
|
mxresp_sz = NFS_MAX_FILE_IO_SIZE;
|
|
|
|
/* Fore channel attributes */
|
|
|
|
args->fc_attrs.max_rqst_sz = mxrqst_sz;
|
|
|
|
args->fc_attrs.max_resp_sz = mxresp_sz;
|
|
|
|
args->fc_attrs.max_ops = NFS4_MAX_OPS;
|
2012-02-07 07:50:40 +07:00
|
|
|
args->fc_attrs.max_reqs = max_session_slots;
|
2009-04-01 20:22:31 +07:00
|
|
|
|
|
|
|
dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u "
|
2009-12-18 00:06:26 +07:00
|
|
|
"max_ops=%u max_reqs=%u\n",
|
2009-04-01 20:22:31 +07:00
|
|
|
__func__,
|
|
|
|
args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz,
|
2009-12-18 00:06:26 +07:00
|
|
|
args->fc_attrs.max_ops, args->fc_attrs.max_reqs);
|
2009-04-01 20:22:31 +07:00
|
|
|
|
|
|
|
/* Back channel attributes */
|
|
|
|
args->bc_attrs.max_rqst_sz = PAGE_SIZE;
|
|
|
|
args->bc_attrs.max_resp_sz = PAGE_SIZE;
|
|
|
|
args->bc_attrs.max_resp_sz_cached = 0;
|
|
|
|
args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
|
|
|
|
args->bc_attrs.max_reqs = 1;
|
|
|
|
|
|
|
|
dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u "
|
|
|
|
"max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
|
|
|
|
__func__,
|
|
|
|
args->bc_attrs.max_rqst_sz, args->bc_attrs.max_resp_sz,
|
|
|
|
args->bc_attrs.max_resp_sz_cached, args->bc_attrs.max_ops,
|
|
|
|
args->bc_attrs.max_reqs);
|
|
|
|
}
|
|
|
|
|
2010-10-03 02:19:01 +07:00
|
|
|
static int nfs4_verify_fore_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session)
|
2009-04-01 20:22:32 +07:00
|
|
|
{
|
2010-10-03 02:19:01 +07:00
|
|
|
struct nfs4_channel_attrs *sent = &args->fc_attrs;
|
|
|
|
struct nfs4_channel_attrs *rcvd = &session->fc_attrs;
|
|
|
|
|
|
|
|
if (rcvd->max_resp_sz > sent->max_resp_sz)
|
|
|
|
return -EINVAL;
|
|
|
|
/*
|
|
|
|
* Our requested max_ops is the minimum we need; we're not
|
|
|
|
* prepared to break up compounds into smaller pieces than that.
|
|
|
|
* So, no point even trying to continue if the server won't
|
|
|
|
* cooperate:
|
|
|
|
*/
|
|
|
|
if (rcvd->max_ops < sent->max_ops)
|
|
|
|
return -EINVAL;
|
|
|
|
if (rcvd->max_reqs == 0)
|
|
|
|
return -EINVAL;
|
2012-02-15 22:38:25 +07:00
|
|
|
if (rcvd->max_reqs > NFS4_MAX_SLOT_TABLE)
|
|
|
|
rcvd->max_reqs = NFS4_MAX_SLOT_TABLE;
|
2010-10-03 02:19:01 +07:00
|
|
|
return 0;
|
2009-04-01 20:22:32 +07:00
|
|
|
}
|
|
|
|
|
2010-10-03 02:19:01 +07:00
|
|
|
static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args, struct nfs4_session *session)
|
|
|
|
{
|
|
|
|
struct nfs4_channel_attrs *sent = &args->bc_attrs;
|
|
|
|
struct nfs4_channel_attrs *rcvd = &session->bc_attrs;
|
2009-04-01 20:22:32 +07:00
|
|
|
|
2010-10-03 02:19:01 +07:00
|
|
|
if (rcvd->max_rqst_sz > sent->max_rqst_sz)
|
|
|
|
return -EINVAL;
|
|
|
|
if (rcvd->max_resp_sz < sent->max_resp_sz)
|
|
|
|
return -EINVAL;
|
|
|
|
if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached)
|
|
|
|
return -EINVAL;
|
|
|
|
/* These would render the backchannel useless: */
|
2012-02-15 22:38:25 +07:00
|
|
|
if (rcvd->max_ops != sent->max_ops)
|
2010-10-03 02:19:01 +07:00
|
|
|
return -EINVAL;
|
2012-02-15 22:38:25 +07:00
|
|
|
if (rcvd->max_reqs != sent->max_reqs)
|
2010-10-03 02:19:01 +07:00
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-01 20:22:32 +07:00
|
|
|
|
|
|
|
static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args,
|
|
|
|
struct nfs4_session *session)
|
|
|
|
{
|
2010-10-03 02:19:01 +07:00
|
|
|
int ret;
|
2009-04-01 20:22:32 +07:00
|
|
|
|
2010-10-03 02:19:01 +07:00
|
|
|
ret = nfs4_verify_fore_channel_attrs(args, session);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
return nfs4_verify_back_channel_attrs(args, session);
|
2009-04-01 20:22:32 +07:00
|
|
|
}
|
|
|
|
|
2012-05-26 04:51:23 +07:00
|
|
|
static int _nfs4_proc_create_session(struct nfs_client *clp,
|
|
|
|
struct rpc_cred *cred)
|
2009-04-01 20:22:31 +07:00
|
|
|
{
|
|
|
|
struct nfs4_session *session = clp->cl_session;
|
|
|
|
struct nfs41_create_session_args args = {
|
|
|
|
.client = clp,
|
|
|
|
.cb_program = NFS4_CALLBACK,
|
|
|
|
};
|
|
|
|
struct nfs41_create_session_res res = {
|
|
|
|
.client = clp,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
2012-05-26 04:51:23 +07:00
|
|
|
.rpc_cred = cred,
|
2009-04-01 20:22:31 +07:00
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
nfs4_init_channel_attrs(&args);
|
2009-04-01 20:23:16 +07:00
|
|
|
args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
|
2009-04-01 20:22:31 +07:00
|
|
|
|
2011-04-25 01:29:33 +07:00
|
|
|
status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
2009-04-01 20:22:31 +07:00
|
|
|
|
2012-11-20 23:13:12 +07:00
|
|
|
if (!status) {
|
2009-04-01 20:22:32 +07:00
|
|
|
/* Verify the session's negotiated channel_attrs values */
|
|
|
|
status = nfs4_verify_channel_attrs(&args, session);
|
2009-04-01 20:22:31 +07:00
|
|
|
/* Increment the clientid slot sequence id */
|
|
|
|
clp->cl_seqid++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issues a CREATE_SESSION operation to the server.
|
|
|
|
* It is the responsibility of the caller to verify the session is
|
|
|
|
* expired before calling this routine.
|
|
|
|
*/
|
2012-05-26 04:51:23 +07:00
|
|
|
int nfs4_proc_create_session(struct nfs_client *clp, struct rpc_cred *cred)
|
2009-04-01 20:22:31 +07:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
unsigned *ptr;
|
|
|
|
struct nfs4_session *session = clp->cl_session;
|
|
|
|
|
|
|
|
dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
|
|
|
|
|
2012-05-26 04:51:23 +07:00
|
|
|
status = _nfs4_proc_create_session(clp, cred);
|
2009-04-01 20:22:31 +07:00
|
|
|
if (status)
|
|
|
|
goto out;
|
|
|
|
|
2011-11-10 01:58:21 +07:00
|
|
|
/* Init or reset the session slot tables */
|
|
|
|
status = nfs4_setup_session_slot_tables(session);
|
|
|
|
dprintk("slot table setup returned %d\n", status);
|
2009-04-01 20:22:31 +07:00
|
|
|
if (status)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ptr = (unsigned *)&session->sess_id.data[0];
|
|
|
|
dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__,
|
|
|
|
clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]);
|
|
|
|
out:
|
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:34 +07:00
|
|
|
/*
|
|
|
|
* Issue the over-the-wire RPC DESTROY_SESSION.
|
|
|
|
* The caller must serialize access to this routine.
|
|
|
|
*/
|
2012-05-26 04:51:23 +07:00
|
|
|
int nfs4_proc_destroy_session(struct nfs4_session *session,
|
|
|
|
struct rpc_cred *cred)
|
2009-04-01 20:22:34 +07:00
|
|
|
{
|
2012-05-26 04:51:23 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION],
|
|
|
|
.rpc_argp = session,
|
|
|
|
.rpc_cred = cred,
|
|
|
|
};
|
2009-04-01 20:22:34 +07:00
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
dprintk("--> nfs4_proc_destroy_session\n");
|
|
|
|
|
|
|
|
/* session is still being setup */
|
|
|
|
if (session->clp->cl_cons_state != NFS_CS_READY)
|
|
|
|
return status;
|
|
|
|
|
2011-04-25 01:29:33 +07:00
|
|
|
status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
2009-04-01 20:22:34 +07:00
|
|
|
|
|
|
|
if (status)
|
2012-06-05 21:08:24 +07:00
|
|
|
dprintk("NFS: Got error %d from the server on DESTROY_SESSION. "
|
2009-04-01 20:22:34 +07:00
|
|
|
"Session has been destroyed regardless...\n", status);
|
|
|
|
|
|
|
|
dprintk("<-- nfs4_proc_destroy_session\n");
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:22:36 +07:00
|
|
|
/*
|
|
|
|
* Renew the cl_session lease.
|
|
|
|
*/
|
2010-06-16 20:52:25 +07:00
|
|
|
struct nfs4_sequence_data {
|
|
|
|
struct nfs_client *clp;
|
|
|
|
struct nfs4_sequence_args args;
|
|
|
|
struct nfs4_sequence_res res;
|
|
|
|
};
|
|
|
|
|
2010-02-05 18:45:04 +07:00
|
|
|
static void nfs41_sequence_release(void *data)
|
|
|
|
{
|
2010-06-16 20:52:25 +07:00
|
|
|
struct nfs4_sequence_data *calldata = data;
|
|
|
|
struct nfs_client *clp = calldata->clp;
|
2010-02-05 18:45:04 +07:00
|
|
|
|
2010-02-05 18:45:05 +07:00
|
|
|
if (atomic_read(&clp->cl_count) > 1)
|
|
|
|
nfs4_schedule_state_renewal(clp);
|
|
|
|
nfs_put_client(clp);
|
2010-06-16 20:52:25 +07:00
|
|
|
kfree(calldata);
|
2010-02-05 18:45:04 +07:00
|
|
|
}
|
|
|
|
|
2010-06-16 20:52:25 +07:00
|
|
|
static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
switch(task->tk_status) {
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
|
|
|
return -EAGAIN;
|
|
|
|
default:
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_lease_recovery(clp);
|
2010-06-16 20:52:25 +07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-05 18:45:04 +07:00
|
|
|
static void nfs41_sequence_call_done(struct rpc_task *task, void *data)
|
2009-04-01 20:22:36 +07:00
|
|
|
{
|
2010-06-16 20:52:25 +07:00
|
|
|
struct nfs4_sequence_data *calldata = data;
|
|
|
|
struct nfs_client *clp = calldata->clp;
|
2009-04-01 20:22:36 +07:00
|
|
|
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs41_sequence_done(task, task->tk_msg.rpc_resp))
|
|
|
|
return;
|
2009-04-01 20:22:36 +07:00
|
|
|
|
|
|
|
if (task->tk_status < 0) {
|
|
|
|
dprintk("%s ERROR %d\n", __func__, task->tk_status);
|
2010-02-05 18:45:05 +07:00
|
|
|
if (atomic_read(&clp->cl_count) == 1)
|
|
|
|
goto out;
|
2009-04-01 20:22:36 +07:00
|
|
|
|
2010-06-16 20:52:25 +07:00
|
|
|
if (nfs41_sequence_handle_errors(task, clp) == -EAGAIN) {
|
|
|
|
rpc_restart_call_prepare(task);
|
2009-04-01 20:22:36 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
|
2010-02-05 18:45:05 +07:00
|
|
|
out:
|
2009-04-01 20:22:36 +07:00
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
|
|
|
|
{
|
2010-06-16 20:52:25 +07:00
|
|
|
struct nfs4_sequence_data *calldata = data;
|
|
|
|
struct nfs_client *clp = calldata->clp;
|
2009-04-01 20:22:36 +07:00
|
|
|
struct nfs4_sequence_args *args;
|
|
|
|
struct nfs4_sequence_res *res;
|
|
|
|
|
|
|
|
args = task->tk_msg.rpc_argp;
|
|
|
|
res = task->tk_msg.rpc_resp;
|
|
|
|
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs41_setup_sequence(clp->cl_session, args, res, task);
|
2009-04-01 20:22:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs41_sequence_ops = {
|
|
|
|
.rpc_call_done = nfs41_sequence_call_done,
|
|
|
|
.rpc_call_prepare = nfs41_sequence_prepare,
|
2010-02-05 18:45:04 +07:00
|
|
|
.rpc_release = nfs41_sequence_release,
|
2009-04-01 20:22:36 +07:00
|
|
|
};
|
|
|
|
|
2012-10-30 06:02:20 +07:00
|
|
|
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
|
|
|
|
struct rpc_cred *cred,
|
|
|
|
bool is_privileged)
|
2009-04-01 20:22:36 +07:00
|
|
|
{
|
2010-06-16 20:52:25 +07:00
|
|
|
struct nfs4_sequence_data *calldata;
|
2009-04-01 20:22:36 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
|
|
|
|
.rpc_cred = cred,
|
|
|
|
};
|
2010-06-16 20:52:26 +07:00
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = clp->cl_rpcclient,
|
|
|
|
.rpc_message = &msg,
|
2012-10-30 06:02:20 +07:00
|
|
|
.callback_ops = &nfs41_sequence_ops,
|
2013-04-09 04:50:28 +07:00
|
|
|
.flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
|
2010-06-16 20:52:26 +07:00
|
|
|
};
|
2009-04-01 20:22:36 +07:00
|
|
|
|
2010-02-05 18:45:05 +07:00
|
|
|
if (!atomic_inc_not_zero(&clp->cl_count))
|
2010-06-16 20:52:26 +07:00
|
|
|
return ERR_PTR(-EIO);
|
2010-09-24 20:17:01 +07:00
|
|
|
calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
|
2010-06-16 20:52:25 +07:00
|
|
|
if (calldata == NULL) {
|
2010-02-05 18:45:05 +07:00
|
|
|
nfs_put_client(clp);
|
2010-06-16 20:52:26 +07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2009-04-01 20:22:36 +07:00
|
|
|
}
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&calldata->args, &calldata->res, 0);
|
2012-10-30 06:02:20 +07:00
|
|
|
if (is_privileged)
|
|
|
|
nfs4_set_sequence_privileged(&calldata->args);
|
2010-06-16 20:52:25 +07:00
|
|
|
msg.rpc_argp = &calldata->args;
|
|
|
|
msg.rpc_resp = &calldata->res;
|
|
|
|
calldata->clp = clp;
|
2010-06-16 20:52:26 +07:00
|
|
|
task_setup_data.callback_data = calldata;
|
2009-04-01 20:22:36 +07:00
|
|
|
|
2010-06-16 20:52:26 +07:00
|
|
|
return rpc_run_task(&task_setup_data);
|
|
|
|
}
|
|
|
|
|
2011-08-25 02:07:37 +07:00
|
|
|
static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags)
|
2010-06-16 20:52:26 +07:00
|
|
|
{
|
|
|
|
struct rpc_task *task;
|
|
|
|
int ret = 0;
|
|
|
|
|
2011-08-25 02:07:37 +07:00
|
|
|
if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
|
|
|
|
return 0;
|
2012-10-30 06:02:20 +07:00
|
|
|
task = _nfs41_proc_sequence(clp, cred, false);
|
2010-06-16 20:52:26 +07:00
|
|
|
if (IS_ERR(task))
|
|
|
|
ret = PTR_ERR(task);
|
|
|
|
else
|
2011-02-22 02:05:41 +07:00
|
|
|
rpc_put_task_async(task);
|
2010-06-16 20:52:26 +07:00
|
|
|
dprintk("<-- %s status=%d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
|
|
|
|
{
|
|
|
|
struct rpc_task *task;
|
|
|
|
int ret;
|
|
|
|
|
2012-10-30 06:02:20 +07:00
|
|
|
task = _nfs41_proc_sequence(clp, cred, true);
|
2010-06-16 20:52:26 +07:00
|
|
|
if (IS_ERR(task)) {
|
|
|
|
ret = PTR_ERR(task);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = rpc_wait_for_completion_task(task);
|
2011-03-10 04:00:55 +07:00
|
|
|
if (!ret) {
|
|
|
|
struct nfs4_sequence_res *res = task->tk_msg.rpc_resp;
|
|
|
|
|
|
|
|
if (task->tk_status == 0)
|
|
|
|
nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
|
2010-06-16 20:52:26 +07:00
|
|
|
ret = task->tk_status;
|
2011-03-10 04:00:55 +07:00
|
|
|
}
|
2010-06-16 20:52:26 +07:00
|
|
|
rpc_put_task(task);
|
|
|
|
out:
|
|
|
|
dprintk("<-- %s status=%d\n", __func__, ret);
|
|
|
|
return ret;
|
2009-04-01 20:22:36 +07:00
|
|
|
}
|
|
|
|
|
2009-12-06 04:08:41 +07:00
|
|
|
struct nfs4_reclaim_complete_data {
|
|
|
|
struct nfs_client *clp;
|
|
|
|
struct nfs41_reclaim_complete_args arg;
|
|
|
|
struct nfs41_reclaim_complete_res res;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data)
|
|
|
|
{
|
|
|
|
struct nfs4_reclaim_complete_data *calldata = data;
|
|
|
|
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs41_setup_sequence(calldata->clp->cl_session,
|
|
|
|
&calldata->arg.seq_args,
|
|
|
|
&calldata->res.seq_res,
|
|
|
|
task);
|
2009-12-06 04:08:41 +07:00
|
|
|
}
|
|
|
|
|
2010-06-16 20:52:25 +07:00
|
|
|
static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nfs_client *clp)
|
|
|
|
{
|
|
|
|
switch(task->tk_status) {
|
|
|
|
case 0:
|
|
|
|
case -NFS4ERR_COMPLETE_ALREADY:
|
|
|
|
case -NFS4ERR_WRONG_CRED: /* What to do here? */
|
|
|
|
break;
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
rpc_delay(task, NFS4_POLL_RETRY_MAX);
|
2011-05-04 00:43:03 +07:00
|
|
|
/* fall through */
|
|
|
|
case -NFS4ERR_RETRY_UNCACHED_REP:
|
2010-06-16 20:52:25 +07:00
|
|
|
return -EAGAIN;
|
|
|
|
default:
|
2011-03-10 04:00:53 +07:00
|
|
|
nfs4_schedule_lease_recovery(clp);
|
2010-06-16 20:52:25 +07:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-06 04:08:41 +07:00
|
|
|
static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data)
|
|
|
|
{
|
|
|
|
struct nfs4_reclaim_complete_data *calldata = data;
|
|
|
|
struct nfs_client *clp = calldata->clp;
|
|
|
|
struct nfs4_sequence_res *res = &calldata->res.seq_res;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2010-08-01 01:29:06 +07:00
|
|
|
if (!nfs41_sequence_done(task, res))
|
|
|
|
return;
|
2009-12-06 04:08:41 +07:00
|
|
|
|
2010-06-16 20:52:25 +07:00
|
|
|
if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) {
|
|
|
|
rpc_restart_call_prepare(task);
|
|
|
|
return;
|
|
|
|
}
|
2009-12-06 04:08:41 +07:00
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_free_reclaim_complete_data(void *data)
|
|
|
|
{
|
|
|
|
struct nfs4_reclaim_complete_data *calldata = data;
|
|
|
|
|
|
|
|
kfree(calldata);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = {
|
|
|
|
.rpc_call_prepare = nfs4_reclaim_complete_prepare,
|
|
|
|
.rpc_call_done = nfs4_reclaim_complete_done,
|
|
|
|
.rpc_release = nfs4_free_reclaim_complete_data,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue a global reclaim complete.
|
|
|
|
*/
|
2013-05-20 22:05:17 +07:00
|
|
|
static int nfs41_proc_reclaim_complete(struct nfs_client *clp,
|
|
|
|
struct rpc_cred *cred)
|
2009-12-06 04:08:41 +07:00
|
|
|
{
|
|
|
|
struct nfs4_reclaim_complete_data *calldata;
|
|
|
|
struct rpc_task *task;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE],
|
2013-05-20 22:05:17 +07:00
|
|
|
.rpc_cred = cred,
|
2009-12-06 04:08:41 +07:00
|
|
|
};
|
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = clp->cl_rpcclient,
|
|
|
|
.rpc_message = &msg,
|
|
|
|
.callback_ops = &nfs4_reclaim_complete_call_ops,
|
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
|
|
|
int status = -ENOMEM;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2010-05-13 23:51:01 +07:00
|
|
|
calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
|
2009-12-06 04:08:41 +07:00
|
|
|
if (calldata == NULL)
|
|
|
|
goto out;
|
|
|
|
calldata->clp = clp;
|
|
|
|
calldata->arg.one_fs = 0;
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 0);
|
2012-10-30 06:02:20 +07:00
|
|
|
nfs4_set_sequence_privileged(&calldata->arg.seq_args);
|
2009-12-06 04:08:41 +07:00
|
|
|
msg.rpc_argp = &calldata->arg;
|
|
|
|
msg.rpc_resp = &calldata->res;
|
|
|
|
task_setup_data.callback_data = calldata;
|
|
|
|
task = rpc_run_task(&task_setup_data);
|
2010-04-22 16:28:39 +07:00
|
|
|
if (IS_ERR(task)) {
|
2009-12-06 04:08:41 +07:00
|
|
|
status = PTR_ERR(task);
|
2010-04-22 16:28:39 +07:00
|
|
|
goto out;
|
|
|
|
}
|
2011-03-10 01:13:46 +07:00
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
|
|
|
if (status == 0)
|
|
|
|
status = task->tk_status;
|
2009-12-06 04:08:41 +07:00
|
|
|
rpc_put_task(task);
|
2010-04-22 16:28:39 +07:00
|
|
|
return 0;
|
2009-12-06 04:08:41 +07:00
|
|
|
out:
|
|
|
|
dprintk("<-- %s status=%d\n", __func__, status);
|
|
|
|
return status;
|
|
|
|
}
|
2010-10-20 11:18:03 +07:00
|
|
|
|
|
|
|
static void
|
|
|
|
nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutget *lgp = calldata;
|
2011-01-06 18:36:24 +07:00
|
|
|
struct nfs_server *server = NFS_SERVER(lgp->args.inode);
|
2012-10-23 07:07:20 +07:00
|
|
|
struct nfs4_session *session = nfs4_get_session(server);
|
2010-10-20 11:18:03 +07:00
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2011-01-06 18:36:24 +07:00
|
|
|
/* Note the is a race here, where a CB_LAYOUTRECALL can come in
|
|
|
|
* right now covering the LAYOUTGET we are about to send.
|
|
|
|
* However, that is not so catastrophic, and there seems
|
|
|
|
* to be no way to prevent it completely.
|
|
|
|
*/
|
2012-10-23 07:07:20 +07:00
|
|
|
if (nfs41_setup_sequence(session, &lgp->args.seq_args,
|
2012-01-18 10:04:25 +07:00
|
|
|
&lgp->res.seq_res, task))
|
2010-10-20 11:18:03 +07:00
|
|
|
return;
|
2011-01-06 18:36:25 +07:00
|
|
|
if (pnfs_choose_layoutget_stateid(&lgp->args.stateid,
|
|
|
|
NFS_I(lgp->args.inode)->layout,
|
|
|
|
lgp->args.ctx->state)) {
|
|
|
|
rpc_exit(task, NFS4_OK);
|
|
|
|
}
|
2010-10-20 11:18:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutget *lgp = calldata;
|
2012-10-02 07:25:48 +07:00
|
|
|
struct inode *inode = lgp->args.inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
|
|
|
struct pnfs_layout_hdr *lo;
|
|
|
|
struct nfs4_state *state = NULL;
|
2013-03-01 08:30:10 +07:00
|
|
|
unsigned long timeo, giveup;
|
2010-10-20 11:18:03 +07:00
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
|
2012-10-23 07:07:20 +07:00
|
|
|
if (!nfs41_sequence_done(task, &lgp->res.seq_res))
|
2012-10-02 07:25:48 +07:00
|
|
|
goto out;
|
2010-10-20 11:18:03 +07:00
|
|
|
|
|
|
|
switch (task->tk_status) {
|
|
|
|
case 0:
|
2012-10-02 07:25:48 +07:00
|
|
|
goto out;
|
2010-10-20 11:18:03 +07:00
|
|
|
case -NFS4ERR_LAYOUTTRYLATER:
|
|
|
|
case -NFS4ERR_RECALLCONFLICT:
|
2013-03-01 08:30:10 +07:00
|
|
|
timeo = rpc_get_timeout(task->tk_client);
|
|
|
|
giveup = lgp->args.timestamp + timeo;
|
|
|
|
if (time_after(giveup, jiffies))
|
|
|
|
task->tk_status = -NFS4ERR_DELAY;
|
2012-10-02 07:25:48 +07:00
|
|
|
break;
|
|
|
|
case -NFS4ERR_EXPIRED:
|
|
|
|
case -NFS4ERR_BAD_STATEID:
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
lo = NFS_I(inode)->layout;
|
|
|
|
if (!lo || list_empty(&lo->plh_segs)) {
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
/* If the open stateid was bad, then recover it. */
|
|
|
|
state = lgp->args.ctx->state;
|
|
|
|
} else {
|
|
|
|
LIST_HEAD(head);
|
|
|
|
|
|
|
|
pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
/* Mark the bad layout state as invalid, then
|
|
|
|
* retry using the open stateid. */
|
|
|
|
pnfs_free_lseg_list(&head);
|
2010-10-20 11:18:03 +07:00
|
|
|
}
|
|
|
|
}
|
2012-10-02 07:25:48 +07:00
|
|
|
if (nfs4_async_handle_error(task, server, state) == -EAGAIN)
|
|
|
|
rpc_restart_call_prepare(task);
|
|
|
|
out:
|
2010-10-20 11:18:03 +07:00
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
2012-08-02 15:47:10 +07:00
|
|
|
static size_t max_response_pages(struct nfs_server *server)
|
|
|
|
{
|
|
|
|
u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
|
|
|
|
return nfs_page_array_len(0, max_resp_sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_free_pages(struct page **pages, size_t size)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!pages)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
if (!pages[i])
|
|
|
|
break;
|
|
|
|
__free_page(pages[i]);
|
|
|
|
}
|
|
|
|
kfree(pages);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags)
|
|
|
|
{
|
|
|
|
struct page **pages;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pages = kcalloc(size, sizeof(struct page *), gfp_flags);
|
|
|
|
if (!pages) {
|
|
|
|
dprintk("%s: can't alloc array of %zu pages\n", __func__, size);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
pages[i] = alloc_page(gfp_flags);
|
|
|
|
if (!pages[i]) {
|
|
|
|
dprintk("%s: failed to allocate page\n", __func__);
|
|
|
|
nfs4_free_pages(pages, size);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pages;
|
|
|
|
}
|
|
|
|
|
2010-10-20 11:18:03 +07:00
|
|
|
static void nfs4_layoutget_release(void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutget *lgp = calldata;
|
2013-02-26 09:27:33 +07:00
|
|
|
struct inode *inode = lgp->args.inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2012-08-02 15:47:10 +07:00
|
|
|
size_t max_pages = max_response_pages(server);
|
2010-10-20 11:18:03 +07:00
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2012-08-02 15:47:10 +07:00
|
|
|
nfs4_free_pages(lgp->args.layout.pages, max_pages);
|
2013-02-26 09:27:33 +07:00
|
|
|
pnfs_put_layout_hdr(NFS_I(inode)->layout);
|
2010-10-20 11:18:03 +07:00
|
|
|
put_nfs_open_context(lgp->args.ctx);
|
|
|
|
kfree(calldata);
|
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_layoutget_call_ops = {
|
|
|
|
.rpc_call_prepare = nfs4_layoutget_prepare,
|
|
|
|
.rpc_call_done = nfs4_layoutget_done,
|
|
|
|
.rpc_release = nfs4_layoutget_release,
|
|
|
|
};
|
|
|
|
|
2012-09-18 04:12:15 +07:00
|
|
|
struct pnfs_layout_segment *
|
|
|
|
nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
|
2010-10-20 11:18:03 +07:00
|
|
|
{
|
2013-02-26 09:27:33 +07:00
|
|
|
struct inode *inode = lgp->args.inode;
|
|
|
|
struct nfs_server *server = NFS_SERVER(inode);
|
2012-08-02 15:47:10 +07:00
|
|
|
size_t max_pages = max_response_pages(server);
|
2010-10-20 11:18:03 +07:00
|
|
|
struct rpc_task *task;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
|
|
|
|
.rpc_argp = &lgp->args,
|
|
|
|
.rpc_resp = &lgp->res,
|
2013-05-20 21:49:34 +07:00
|
|
|
.rpc_cred = lgp->cred,
|
2010-10-20 11:18:03 +07:00
|
|
|
};
|
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = server->client,
|
|
|
|
.rpc_message = &msg,
|
|
|
|
.callback_ops = &nfs4_layoutget_call_ops,
|
|
|
|
.callback_data = lgp,
|
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
2012-09-18 04:12:15 +07:00
|
|
|
struct pnfs_layout_segment *lseg = NULL;
|
2010-10-20 11:18:03 +07:00
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
|
2012-08-02 15:47:10 +07:00
|
|
|
lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
|
|
|
|
if (!lgp->args.layout.pages) {
|
|
|
|
nfs4_layoutget_release(lgp);
|
2012-09-18 04:12:15 +07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2012-08-02 15:47:10 +07:00
|
|
|
}
|
|
|
|
lgp->args.layout.pglen = max_pages * PAGE_SIZE;
|
2013-03-01 08:30:10 +07:00
|
|
|
lgp->args.timestamp = jiffies;
|
2012-08-02 15:47:10 +07:00
|
|
|
|
2011-03-25 03:48:21 +07:00
|
|
|
lgp->res.layoutp = &lgp->args.layout;
|
2010-10-20 11:18:03 +07:00
|
|
|
lgp->res.seq_res.sr_slot = NULL;
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0);
|
2013-02-26 09:27:33 +07:00
|
|
|
|
|
|
|
/* nfs4_layoutget_release calls pnfs_put_layout_hdr */
|
|
|
|
pnfs_get_layout_hdr(NFS_I(inode)->layout);
|
|
|
|
|
2010-10-20 11:18:03 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
|
|
|
if (IS_ERR(task))
|
2012-09-18 04:12:15 +07:00
|
|
|
return ERR_CAST(task);
|
2010-10-20 11:18:03 +07:00
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
2011-01-06 18:36:24 +07:00
|
|
|
if (status == 0)
|
|
|
|
status = task->tk_status;
|
2013-02-16 04:03:46 +07:00
|
|
|
/* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
|
|
|
|
if (status == 0 && lgp->res.layoutp->len)
|
2012-09-18 04:12:15 +07:00
|
|
|
lseg = pnfs_layout_process(lgp);
|
2010-10-20 11:18:03 +07:00
|
|
|
rpc_put_task(task);
|
|
|
|
dprintk("<-- %s status=%d\n", __func__, status);
|
2012-09-18 04:12:15 +07:00
|
|
|
if (status)
|
|
|
|
return ERR_PTR(status);
|
|
|
|
return lseg;
|
2010-10-20 11:18:03 +07:00
|
|
|
}
|
|
|
|
|
2011-05-22 23:52:37 +07:00
|
|
|
static void
|
|
|
|
nfs4_layoutreturn_prepare(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutreturn *lrp = calldata;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs41_setup_sequence(lrp->clp->cl_session,
|
|
|
|
&lrp->args.seq_args,
|
|
|
|
&lrp->res.seq_res,
|
|
|
|
task);
|
2011-05-22 23:52:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutreturn *lrp = calldata;
|
|
|
|
struct nfs_server *server;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
|
2012-10-23 07:07:20 +07:00
|
|
|
if (!nfs41_sequence_done(task, &lrp->res.seq_res))
|
2011-05-22 23:52:37 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
server = NFS_SERVER(lrp->args.inode);
|
|
|
|
if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
|
2011-10-20 02:17:29 +07:00
|
|
|
rpc_restart_call_prepare(task);
|
2011-05-22 23:52:37 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_layoutreturn_release(void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutreturn *lrp = calldata;
|
2012-09-25 01:18:39 +07:00
|
|
|
struct pnfs_layout_hdr *lo = lrp->args.layout;
|
2011-05-22 23:52:37 +07:00
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2012-09-25 01:18:39 +07:00
|
|
|
spin_lock(&lo->plh_inode->i_lock);
|
|
|
|
if (lrp->res.lrs_present)
|
|
|
|
pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
|
|
|
|
lo->plh_block_lgets--;
|
|
|
|
spin_unlock(&lo->plh_inode->i_lock);
|
2012-09-19 07:51:13 +07:00
|
|
|
pnfs_put_layout_hdr(lrp->args.layout);
|
2011-05-22 23:52:37 +07:00
|
|
|
kfree(calldata);
|
|
|
|
dprintk("<-- %s\n", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_layoutreturn_call_ops = {
|
|
|
|
.rpc_call_prepare = nfs4_layoutreturn_prepare,
|
|
|
|
.rpc_call_done = nfs4_layoutreturn_done,
|
|
|
|
.rpc_release = nfs4_layoutreturn_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp)
|
|
|
|
{
|
|
|
|
struct rpc_task *task;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN],
|
|
|
|
.rpc_argp = &lrp->args,
|
|
|
|
.rpc_resp = &lrp->res,
|
2013-05-20 21:43:47 +07:00
|
|
|
.rpc_cred = lrp->cred,
|
2011-05-22 23:52:37 +07:00
|
|
|
};
|
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.rpc_client = lrp->clp->cl_rpcclient,
|
|
|
|
.rpc_message = &msg,
|
|
|
|
.callback_ops = &nfs4_layoutreturn_call_ops,
|
|
|
|
.callback_data = lrp,
|
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1);
|
2011-05-22 23:52:37 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
|
|
|
if (IS_ERR(task))
|
|
|
|
return PTR_ERR(task);
|
|
|
|
status = task->tk_status;
|
|
|
|
dprintk("<-- %s status=%d\n", __func__, status);
|
|
|
|
rpc_put_task(task);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2011-07-31 07:52:35 +07:00
|
|
|
/*
|
|
|
|
* Retrieve the list of Data Server devices from the MDS.
|
|
|
|
*/
|
|
|
|
static int _nfs4_getdevicelist(struct nfs_server *server,
|
|
|
|
const struct nfs_fh *fh,
|
|
|
|
struct pnfs_devicelist *devlist)
|
|
|
|
{
|
|
|
|
struct nfs4_getdevicelist_args args = {
|
|
|
|
.fh = fh,
|
|
|
|
.layoutclass = server->pnfs_curr_ld->id,
|
|
|
|
};
|
|
|
|
struct nfs4_getdevicelist_res res = {
|
|
|
|
.devlist = devlist,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICELIST],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
|
|
|
|
&res.seq_res, 0);
|
|
|
|
dprintk("<-- %s status=%d\n", __func__, status);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nfs4_proc_getdevicelist(struct nfs_server *server,
|
|
|
|
const struct nfs_fh *fh,
|
|
|
|
struct pnfs_devicelist *devlist)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(server,
|
|
|
|
_nfs4_getdevicelist(server, fh, devlist),
|
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
|
|
|
|
dprintk("%s: err=%d, num_devs=%u\n", __func__,
|
|
|
|
err, devlist->num_devs);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist);
|
|
|
|
|
2010-10-20 11:18:03 +07:00
|
|
|
static int
|
2013-05-20 22:42:54 +07:00
|
|
|
_nfs4_proc_getdeviceinfo(struct nfs_server *server,
|
|
|
|
struct pnfs_device *pdev,
|
|
|
|
struct rpc_cred *cred)
|
2010-10-20 11:18:03 +07:00
|
|
|
{
|
|
|
|
struct nfs4_getdeviceinfo_args args = {
|
|
|
|
.pdev = pdev,
|
|
|
|
};
|
|
|
|
struct nfs4_getdeviceinfo_res res = {
|
|
|
|
.pdev = pdev,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
2013-05-20 22:42:54 +07:00
|
|
|
.rpc_cred = cred,
|
2010-10-20 11:18:03 +07:00
|
|
|
};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
2011-03-25 00:12:24 +07:00
|
|
|
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
2010-10-20 11:18:03 +07:00
|
|
|
dprintk("<-- %s status=%d\n", __func__, status);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2013-05-20 22:42:54 +07:00
|
|
|
int nfs4_proc_getdeviceinfo(struct nfs_server *server,
|
|
|
|
struct pnfs_device *pdev,
|
|
|
|
struct rpc_cred *cred)
|
2010-10-20 11:18:03 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
|
|
|
|
do {
|
|
|
|
err = nfs4_handle_exception(server,
|
2013-05-20 22:42:54 +07:00
|
|
|
_nfs4_proc_getdeviceinfo(server, pdev, cred),
|
2010-10-20 11:18:03 +07:00
|
|
|
&exception);
|
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo);
|
|
|
|
|
2011-03-23 20:27:54 +07:00
|
|
|
static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutcommit_data *data = calldata;
|
|
|
|
struct nfs_server *server = NFS_SERVER(data->args.inode);
|
2012-10-23 07:07:20 +07:00
|
|
|
struct nfs4_session *session = nfs4_get_session(server);
|
2011-03-23 20:27:54 +07:00
|
|
|
|
2012-10-23 07:28:44 +07:00
|
|
|
nfs41_setup_sequence(session,
|
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
|
|
|
task);
|
2011-03-23 20:27:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutcommit_data *data = calldata;
|
|
|
|
struct nfs_server *server = NFS_SERVER(data->args.inode);
|
|
|
|
|
2012-10-23 07:07:20 +07:00
|
|
|
if (!nfs41_sequence_done(task, &data->res.seq_res))
|
2011-03-23 20:27:54 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
switch (task->tk_status) { /* Just ignore these failures */
|
2012-03-28 05:22:19 +07:00
|
|
|
case -NFS4ERR_DELEG_REVOKED: /* layout was recalled */
|
|
|
|
case -NFS4ERR_BADIOMODE: /* no IOMODE_RW layout for range */
|
|
|
|
case -NFS4ERR_BADLAYOUT: /* no layout */
|
|
|
|
case -NFS4ERR_GRACE: /* loca_recalim always false */
|
2011-03-23 20:27:54 +07:00
|
|
|
task->tk_status = 0;
|
2012-03-28 05:22:19 +07:00
|
|
|
break;
|
|
|
|
case 0:
|
2011-03-23 20:27:54 +07:00
|
|
|
nfs_post_op_update_inode_force_wcc(data->args.inode,
|
|
|
|
data->res.fattr);
|
2012-03-28 05:22:19 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
|
|
|
|
rpc_restart_call_prepare(task);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-03-23 20:27:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs4_layoutcommit_release(void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs4_layoutcommit_data *data = calldata;
|
|
|
|
|
2011-07-31 07:52:38 +07:00
|
|
|
pnfs_cleanup_layoutcommit(data);
|
2011-03-23 20:27:54 +07:00
|
|
|
put_rpccred(data->cred);
|
|
|
|
kfree(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct rpc_call_ops nfs4_layoutcommit_ops = {
|
|
|
|
.rpc_call_prepare = nfs4_layoutcommit_prepare,
|
|
|
|
.rpc_call_done = nfs4_layoutcommit_done,
|
|
|
|
.rpc_release = nfs4_layoutcommit_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
int
|
2011-03-12 14:58:10 +07:00
|
|
|
nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
|
2011-03-23 20:27:54 +07:00
|
|
|
{
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTCOMMIT],
|
|
|
|
.rpc_argp = &data->args,
|
|
|
|
.rpc_resp = &data->res,
|
|
|
|
.rpc_cred = data->cred,
|
|
|
|
};
|
|
|
|
struct rpc_task_setup task_setup_data = {
|
|
|
|
.task = &data->task,
|
|
|
|
.rpc_client = NFS_CLIENT(data->args.inode),
|
|
|
|
.rpc_message = &msg,
|
|
|
|
.callback_ops = &nfs4_layoutcommit_ops,
|
|
|
|
.callback_data = data,
|
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
|
|
|
struct rpc_task *task;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
dprintk("NFS: %4d initiating layoutcommit call. sync %d "
|
|
|
|
"lbw: %llu inode %lu\n",
|
|
|
|
data->task.tk_pid, sync,
|
|
|
|
data->args.lastbytewritten,
|
|
|
|
data->args.inode->i_ino);
|
|
|
|
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
|
2011-03-23 20:27:54 +07:00
|
|
|
task = rpc_run_task(&task_setup_data);
|
|
|
|
if (IS_ERR(task))
|
|
|
|
return PTR_ERR(task);
|
2011-03-12 14:58:10 +07:00
|
|
|
if (sync == false)
|
2011-03-23 20:27:54 +07:00
|
|
|
goto out;
|
|
|
|
status = nfs4_wait_for_completion_rpc_task(task);
|
|
|
|
if (status != 0)
|
|
|
|
goto out;
|
|
|
|
status = task->tk_status;
|
|
|
|
out:
|
|
|
|
dprintk("%s: status %d\n", __func__, status);
|
|
|
|
rpc_put_task(task);
|
|
|
|
return status;
|
|
|
|
}
|
2011-06-03 01:59:07 +07:00
|
|
|
|
|
|
|
static int
|
|
|
|
_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
|
|
|
|
{
|
|
|
|
struct nfs41_secinfo_no_name_args args = {
|
|
|
|
.style = SECINFO_STYLE_CURRENT_FH,
|
|
|
|
};
|
|
|
|
struct nfs4_secinfo_res res = {
|
|
|
|
.flavors = flavors,
|
|
|
|
};
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
|
|
|
};
|
|
|
|
return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
|
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
|
|
|
err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
|
|
|
|
switch (err) {
|
|
|
|
case 0:
|
|
|
|
case -NFS4ERR_WRONGSEC:
|
|
|
|
case -NFS4ERR_NOTSUPP:
|
2012-03-28 05:13:02 +07:00
|
|
|
goto out;
|
2011-06-03 01:59:07 +07:00
|
|
|
default:
|
|
|
|
err = nfs4_handle_exception(server, err, &exception);
|
|
|
|
}
|
|
|
|
} while (exception.retry);
|
2012-03-28 05:13:02 +07:00
|
|
|
out:
|
2011-06-03 01:59:07 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
|
|
|
struct nfs_fsinfo *info)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct page *page;
|
|
|
|
rpc_authflavor_t flavor;
|
|
|
|
struct nfs4_secinfo_flavors *flavors;
|
|
|
|
|
|
|
|
page = alloc_page(GFP_KERNEL);
|
|
|
|
if (!page) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
flavors = page_address(page);
|
|
|
|
err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fall back on "guess and check" method if
|
|
|
|
* the server doesn't support SECINFO_NO_NAME
|
|
|
|
*/
|
|
|
|
if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
|
|
|
|
err = nfs4_find_root_sec(server, fhandle, info);
|
|
|
|
goto out_freepage;
|
|
|
|
}
|
|
|
|
if (err)
|
|
|
|
goto out_freepage;
|
|
|
|
|
|
|
|
flavor = nfs_find_best_sec(flavors);
|
|
|
|
if (err == 0)
|
|
|
|
err = nfs4_lookup_root_sec(server, fhandle, info, flavor);
|
|
|
|
|
|
|
|
out_freepage:
|
|
|
|
put_page(page);
|
|
|
|
if (err == -EACCES)
|
|
|
|
return -EPERM;
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
2012-01-31 22:39:29 +07:00
|
|
|
|
2013-05-20 22:20:27 +07:00
|
|
|
static int _nfs41_test_stateid(struct nfs_server *server,
|
|
|
|
nfs4_stateid *stateid,
|
|
|
|
struct rpc_cred *cred)
|
2011-06-03 01:59:08 +07:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct nfs41_test_stateid_args args = {
|
2012-01-31 22:39:29 +07:00
|
|
|
.stateid = stateid,
|
2011-06-03 01:59:08 +07:00
|
|
|
};
|
|
|
|
struct nfs41_test_stateid_res res;
|
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
|
|
|
|
.rpc_argp = &args,
|
|
|
|
.rpc_resp = &res,
|
2013-05-20 22:20:27 +07:00
|
|
|
.rpc_cred = cred,
|
2011-06-03 01:59:08 +07:00
|
|
|
};
|
2012-01-31 22:39:29 +07:00
|
|
|
|
2012-07-12 03:30:23 +07:00
|
|
|
dprintk("NFS call test_stateid %p\n", stateid);
|
2012-01-18 10:04:25 +07:00
|
|
|
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
|
2012-10-30 06:02:20 +07:00
|
|
|
nfs4_set_sequence_privileged(&args.seq_args);
|
|
|
|
status = nfs4_call_sync_sequence(server->client, server, &msg,
|
|
|
|
&args.seq_args, &res.seq_res);
|
2012-07-12 03:30:23 +07:00
|
|
|
if (status != NFS_OK) {
|
|
|
|
dprintk("NFS reply test_stateid: failed, %d\n", status);
|
NFS: Fix up TEST_STATEID and FREE_STATEID return code handling
The TEST_STATEID and FREE_STATEID operations can return
-NFS4ERR_BAD_STATEID, -NFS4ERR_OLD_STATEID, or -NFS4ERR_DEADSESSION.
nfs41_{test,free}_stateid() should not pass these errors to
nfs4_handle_exception() during state recovery, since that will
recursively kick off state recovery again, resulting in a deadlock.
In particular, when the TEST_STATEID operation returns NFS4_OK,
res.status can contain one of these errors. _nfs41_test_stateid()
replaces NFS4_OK with the value in res.status, which is then returned
to callers.
But res.status is not passed through nfs4_stat_to_errno(), and thus is
a positive NFS4ERR value. Currently callers are only interested in
!NFS4_OK, and nfs4_handle_exception() ignores positive values.
Thus the res.status values are currently ignored by
nfs4_handle_exception() and won't cause the deadlock above. Thanks to
this missing negative, it is only when these operations fail (which
is very rare) that a deadlock can occur.
Bryan agrees the original intent was to return res.status as a
negative NFS4ERR value to callers of nfs41_test_stateid().
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-07-12 03:29:45 +07:00
|
|
|
return status;
|
2012-07-12 03:30:23 +07:00
|
|
|
}
|
|
|
|
dprintk("NFS reply test_stateid: succeeded, %d\n", -res.status);
|
NFS: Fix up TEST_STATEID and FREE_STATEID return code handling
The TEST_STATEID and FREE_STATEID operations can return
-NFS4ERR_BAD_STATEID, -NFS4ERR_OLD_STATEID, or -NFS4ERR_DEADSESSION.
nfs41_{test,free}_stateid() should not pass these errors to
nfs4_handle_exception() during state recovery, since that will
recursively kick off state recovery again, resulting in a deadlock.
In particular, when the TEST_STATEID operation returns NFS4_OK,
res.status can contain one of these errors. _nfs41_test_stateid()
replaces NFS4_OK with the value in res.status, which is then returned
to callers.
But res.status is not passed through nfs4_stat_to_errno(), and thus is
a positive NFS4ERR value. Currently callers are only interested in
!NFS4_OK, and nfs4_handle_exception() ignores positive values.
Thus the res.status values are currently ignored by
nfs4_handle_exception() and won't cause the deadlock above. Thanks to
this missing negative, it is only when these operations fail (which
is very rare) that a deadlock can occur.
Bryan agrees the original intent was to return res.status as a
negative NFS4ERR value to callers of nfs41_test_stateid().
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-07-12 03:29:45 +07:00
|
|
|
return -res.status;
|
2011-06-03 01:59:08 +07:00
|
|
|
}
|
|
|
|
|
2012-07-12 03:30:23 +07:00
|
|
|
/**
|
|
|
|
* nfs41_test_stateid - perform a TEST_STATEID operation
|
|
|
|
*
|
|
|
|
* @server: server / transport on which to perform the operation
|
|
|
|
* @stateid: state ID to test
|
2013-05-20 22:20:27 +07:00
|
|
|
* @cred: credential
|
2012-07-12 03:30:23 +07:00
|
|
|
*
|
|
|
|
* Returns NFS_OK if the server recognizes that "stateid" is valid.
|
|
|
|
* Otherwise a negative NFS4ERR value is returned if the operation
|
|
|
|
* failed or the state ID is not currently valid.
|
|
|
|
*/
|
2013-05-20 22:20:27 +07:00
|
|
|
static int nfs41_test_stateid(struct nfs_server *server,
|
|
|
|
nfs4_stateid *stateid,
|
|
|
|
struct rpc_cred *cred)
|
2011-06-03 01:59:08 +07:00
|
|
|
{
|
|
|
|
struct nfs4_exception exception = { };
|
|
|
|
int err;
|
|
|
|
do {
|
2013-05-20 22:20:27 +07:00
|
|
|
err = _nfs41_test_stateid(server, stateid, cred);
|
NFS: Fix up TEST_STATEID and FREE_STATEID return code handling
The TEST_STATEID and FREE_STATEID operations can return
-NFS4ERR_BAD_STATEID, -NFS4ERR_OLD_STATEID, or -NFS4ERR_DEADSESSION.
nfs41_{test,free}_stateid() should not pass these errors to
nfs4_handle_exception() during state recovery, since that will
recursively kick off state recovery again, resulting in a deadlock.
In particular, when the TEST_STATEID operation returns NFS4_OK,
res.status can contain one of these errors. _nfs41_test_stateid()
replaces NFS4_OK with the value in res.status, which is then returned
to callers.
But res.status is not passed through nfs4_stat_to_errno(), and thus is
a positive NFS4ERR value. Currently callers are only interested in
!NFS4_OK, and nfs4_handle_exception() ignores positive values.
Thus the res.status values are currently ignored by
nfs4_handle_exception() and won't cause the deadlock above. Thanks to
this missing negative, it is only when these operations fail (which
is very rare) that a deadlock can occur.
Bryan agrees the original intent was to return res.status as a
negative NFS4ERR value to callers of nfs41_test_stateid().
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2012-07-12 03:29:45 +07:00
|
|
|
if (err != -NFS4ERR_DELAY)
|
|
|
|
break;
|
|
|
|
nfs4_handle_exception(server, err, &exception);
|
2011-06-03 01:59:08 +07:00
|
|
|
} while (exception.retry);
|
|
|
|
return err;
|
|
|
|
}
|
2011-06-03 01:59:09 +07:00
|
|
|
|
2013-05-04 01:40:01 +07:00
|
|
|
struct nfs_free_stateid_data {
|
|
|
|
struct nfs_server *server;
|
|
|
|
struct nfs41_free_stateid_args args;
|
2011-06-03 01:59:09 +07:00
|
|
|
struct nfs41_free_stateid_res res;
|
2013-05-04 01:40:01 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void nfs41_free_stateid_prepare(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs_free_stateid_data *data = calldata;
|
|
|
|
nfs41_setup_sequence(nfs4_get_session(data->server),
|
|
|
|
&data->args.seq_args,
|
|
|
|
&data->res.seq_res,
|
|
|
|
task);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs41_free_stateid_done(struct rpc_task *task, void *calldata)
|
|
|
|
{
|
|
|
|
struct nfs_free_stateid_data *data = calldata;
|
|
|
|
|
|
|
|
nfs41_sequence_done(task, &data->res.seq_res);
|
|
|
|
|
|
|
|
switch (task->tk_status) {
|
|
|
|
case -NFS4ERR_DELAY:
|
|
|
|
if (nfs4_async_handle_error(task, data->server, NULL) == -EAGAIN)
|
|
|
|
rpc_restart_call_prepare(task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nfs41_free_stateid_release(void *calldata)
|
|
|
|
{
|
|
|
|
kfree(calldata);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct rpc_call_ops nfs41_free_stateid_ops = {
|
|
|
|
.rpc_call_prepare = nfs41_free_stateid_prepare,
|
|
|
|
.rpc_call_done = nfs41_free_stateid_done,
|
|
|
|
.rpc_release = nfs41_free_stateid_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
|
|
|
|
nfs4_stateid *stateid,
|
2013-05-20 22:20:27 +07:00
|
|
|
struct rpc_cred *cred,
|
2013-05-04 01:40:01 +07:00
|
|
|
bool privileged)
|
|
|
|
{
|
2011-06-03 01:59:09 +07:00
|
|
|
struct rpc_message msg = {
|
|
|
|
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
|
2013-05-20 22:20:27 +07:00
|
|
|
.rpc_cred = cred,
|
2011-06-03 01:59:09 +07:00
|
|
|
};
|
2013-05-04 01:40:01 +07:00
|
|
|
struct rpc_task_setup task_setup = {
|
|
|
|
.rpc_client = server->client,
|
|
|
|
.rpc_message = &msg,
|
|
|
|
.callback_ops = &nfs41_free_stateid_ops,
|
|
|
|
.flags = RPC_TASK_ASYNC,
|
|
|
|
};
|
|
|
|
struct nfs_free_stateid_data *data;
|
2011-06-03 01:59:09 +07:00
|
|
|
|
2012-07-12 03:30:23 +07:00
|
|
|
dprintk("NFS call free_stateid %p\n", stateid);
|
2013-05-04 01:40:01 +07:00
|
|
|
data = kmalloc(sizeof(*data), GFP_NOFS);
|
|
|
|
if (!data)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
data->server = server;
|
|
|
|
nfs4_stateid_copy(&data->args.stateid, stateid);
|
|
|
|
|
|
|
|
task_setup.callback_data = data;
|
|
|
|
|
|
|
|
msg.rpc_argp = &data->args;
|
|
|
|
msg.rpc_resp = &data->res;
|
|
|
|
nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);
|
|
|
|
if (privileged)
|
|
|
|
nfs4_set_sequence_privileged(&data->args.seq_args);
|
|
|
|
|
|
|
|
return rpc_run_task(&task_setup);
|
2011-06-03 01:59:09 +07:00
|
|
|
}
|
|
|
|
|
2012-07-12 03:30:23 +07:00
|
|
|
/**
|
|
|
|
* nfs41_free_stateid - perform a FREE_STATEID operation
|
|
|
|
*
|
|
|
|
* @server: server / transport on which to perform the operation
|
|
|
|
* @stateid: state ID to release
|
2013-05-20 22:20:27 +07:00
|
|
|
* @cred: credential
|
2012-07-12 03:30:23 +07:00
|
|
|
*
|
|
|
|
* Returns NFS_OK if the server freed "stateid". Otherwise a
|
|
|
|
* negative NFS4ERR value is returned.
|
|
|
|
*/
|
2013-05-20 22:20:27 +07:00
|
|
|
static int nfs41_free_stateid(struct nfs_server *server,
|
|
|
|
nfs4_stateid *stateid,
|
|
|
|
struct rpc_cred *cred)
|
2011-06-03 01:59:09 +07:00
|
|
|
{
|
2013-05-04 01:40:01 +07:00
|
|
|
struct rpc_task *task;
|
|
|
|
int ret;
|
|
|
|
|
2013-05-20 22:20:27 +07:00
|
|
|
task = _nfs41_free_stateid(server, stateid, cred, true);
|
2013-05-04 01:40:01 +07:00
|
|
|
if (IS_ERR(task))
|
|
|
|
return PTR_ERR(task);
|
|
|
|
ret = rpc_wait_for_completion_task(task);
|
|
|
|
if (!ret)
|
|
|
|
ret = task->tk_status;
|
|
|
|
rpc_put_task(task);
|
|
|
|
return ret;
|
2011-06-03 01:59:09 +07:00
|
|
|
}
|
2012-03-05 06:13:56 +07:00
|
|
|
|
2013-05-04 03:22:55 +07:00
|
|
|
static int nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
|
|
|
|
{
|
|
|
|
struct rpc_task *task;
|
2013-05-20 22:20:27 +07:00
|
|
|
struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
|
2013-05-04 03:22:55 +07:00
|
|
|
|
2013-05-20 22:20:27 +07:00
|
|
|
task = _nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
|
2013-05-04 03:22:55 +07:00
|
|
|
nfs4_free_lock_state(server, lsp);
|
|
|
|
if (IS_ERR(task))
|
|
|
|
return PTR_ERR(task);
|
|
|
|
rpc_put_task(task);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-05 06:13:56 +07:00
|
|
|
static bool nfs41_match_stateid(const nfs4_stateid *s1,
|
|
|
|
const nfs4_stateid *s2)
|
|
|
|
{
|
2012-03-05 06:13:57 +07:00
|
|
|
if (memcmp(s1->other, s2->other, sizeof(s1->other)) != 0)
|
2012-03-05 06:13:56 +07:00
|
|
|
return false;
|
|
|
|
|
2012-03-05 06:13:57 +07:00
|
|
|
if (s1->seqid == s2->seqid)
|
2012-03-05 06:13:56 +07:00
|
|
|
return true;
|
2012-03-05 06:13:57 +07:00
|
|
|
if (s1->seqid == 0 || s2->seqid == 0)
|
2012-03-05 06:13:56 +07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-04-01 20:21:53 +07:00
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
2012-03-05 06:13:56 +07:00
|
|
|
static bool nfs4_match_stateid(const nfs4_stateid *s1,
|
|
|
|
const nfs4_stateid *s2)
|
|
|
|
{
|
2012-03-05 06:13:56 +07:00
|
|
|
return nfs4_stateid_match(s1, s2);
|
2012-03-05 06:13:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
|
2008-12-24 03:21:43 +07:00
|
|
|
.owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
|
2008-12-24 03:21:41 +07:00
|
|
|
.state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
|
2005-04-17 05:20:36 +07:00
|
|
|
.recover_open = nfs4_open_reclaim,
|
|
|
|
.recover_lock = nfs4_lock_reclaim,
|
2009-04-01 20:22:47 +07:00
|
|
|
.establish_clid = nfs4_init_clientid,
|
2009-04-01 20:22:48 +07:00
|
|
|
.get_clid_cred = nfs4_get_setclientid_cred,
|
2012-09-15 04:24:32 +07:00
|
|
|
.detect_trunking = nfs40_discover_server_trunking,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2009-04-01 20:22:47 +07:00
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
|
2009-04-01 20:22:47 +07:00
|
|
|
.owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
|
|
|
|
.state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
|
|
|
|
.recover_open = nfs4_open_reclaim,
|
|
|
|
.recover_lock = nfs4_lock_reclaim,
|
2009-12-05 03:52:24 +07:00
|
|
|
.establish_clid = nfs41_init_clientid,
|
2009-04-01 20:22:49 +07:00
|
|
|
.get_clid_cred = nfs4_get_exchange_id_cred,
|
2009-12-06 04:08:41 +07:00
|
|
|
.reclaim_complete = nfs41_proc_reclaim_complete,
|
2012-09-15 04:24:32 +07:00
|
|
|
.detect_trunking = nfs41_discover_server_trunking,
|
2009-04-01 20:22:47 +07:00
|
|
|
};
|
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
|
|
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
|
2009-04-01 20:22:47 +07:00
|
|
|
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
|
|
|
|
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
|
|
|
|
.recover_open = nfs4_open_expired,
|
|
|
|
.recover_lock = nfs4_lock_expired,
|
|
|
|
.establish_clid = nfs4_init_clientid,
|
2009-04-01 20:22:48 +07:00
|
|
|
.get_clid_cred = nfs4_get_setclientid_cred,
|
2009-04-01 20:22:47 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
|
2008-12-24 03:21:43 +07:00
|
|
|
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
|
2008-12-24 03:21:41 +07:00
|
|
|
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
|
2011-06-03 01:59:10 +07:00
|
|
|
.recover_open = nfs41_open_expired,
|
|
|
|
.recover_lock = nfs41_lock_expired,
|
2009-12-05 03:52:24 +07:00
|
|
|
.establish_clid = nfs41_init_clientid,
|
2009-04-01 20:22:49 +07:00
|
|
|
.get_clid_cred = nfs4_get_exchange_id_cred,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
2009-04-01 20:22:47 +07:00
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = {
|
2009-04-01 20:22:44 +07:00
|
|
|
.sched_state_renewal = nfs4_proc_async_renew,
|
2009-04-01 20:22:46 +07:00
|
|
|
.get_state_renewal_cred_locked = nfs4_get_renew_cred_locked,
|
2009-04-01 20:22:45 +07:00
|
|
|
.renew_lease = nfs4_proc_renew,
|
2009-04-01 20:22:44 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
2012-03-12 00:11:00 +07:00
|
|
|
static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
|
2009-04-01 20:22:44 +07:00
|
|
|
.sched_state_renewal = nfs41_proc_async_sequence,
|
2009-04-01 20:22:46 +07:00
|
|
|
.get_state_renewal_cred_locked = nfs4_get_machine_cred_locked,
|
2009-04-01 20:22:45 +07:00
|
|
|
.renew_lease = nfs4_proc_sequence,
|
2009-04-01 20:22:44 +07:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2010-06-16 20:52:26 +07:00
|
|
|
static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
|
|
|
|
.minor_version = 0,
|
2013-03-16 03:11:57 +07:00
|
|
|
.init_caps = NFS_CAP_READDIRPLUS
|
|
|
|
| NFS_CAP_ATOMIC_OPEN
|
|
|
|
| NFS_CAP_CHANGE_ATTR
|
|
|
|
| NFS_CAP_POSIX_LOCK,
|
2010-06-16 20:52:26 +07:00
|
|
|
.call_sync = _nfs4_call_sync,
|
2012-03-05 06:13:56 +07:00
|
|
|
.match_stateid = nfs4_match_stateid,
|
2011-06-03 01:59:07 +07:00
|
|
|
.find_root_sec = nfs4_find_root_sec,
|
2013-05-04 03:22:55 +07:00
|
|
|
.free_lock_state = nfs4_release_lockowner,
|
2010-06-16 20:52:27 +07:00
|
|
|
.reboot_recovery_ops = &nfs40_reboot_recovery_ops,
|
|
|
|
.nograce_recovery_ops = &nfs40_nograce_recovery_ops,
|
|
|
|
.state_renewal_ops = &nfs40_state_renewal_ops,
|
2010-06-16 20:52:26 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
|
|
|
|
.minor_version = 1,
|
2013-03-16 03:11:57 +07:00
|
|
|
.init_caps = NFS_CAP_READDIRPLUS
|
|
|
|
| NFS_CAP_ATOMIC_OPEN
|
|
|
|
| NFS_CAP_CHANGE_ATTR
|
2013-03-18 02:31:15 +07:00
|
|
|
| NFS_CAP_POSIX_LOCK
|
2013-03-16 03:44:28 +07:00
|
|
|
| NFS_CAP_STATEID_NFSV41
|
|
|
|
| NFS_CAP_ATOMIC_OPEN_V1,
|
2012-11-13 02:13:13 +07:00
|
|
|
.call_sync = nfs4_call_sync_sequence,
|
2012-03-05 06:13:56 +07:00
|
|
|
.match_stateid = nfs41_match_stateid,
|
2011-06-03 01:59:07 +07:00
|
|
|
.find_root_sec = nfs41_find_root_sec,
|
2013-05-04 03:22:55 +07:00
|
|
|
.free_lock_state = nfs41_free_lock_state,
|
2010-06-16 20:52:27 +07:00
|
|
|
.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
|
|
|
|
.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
|
|
|
|
.state_renewal_ops = &nfs41_state_renewal_ops,
|
2010-06-16 20:52:26 +07:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
|
|
|
|
[0] = &nfs_v4_0_minor_ops,
|
|
|
|
#if defined(CONFIG_NFS_V4_1)
|
|
|
|
[1] = &nfs_v4_1_minor_ops,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2012-07-17 03:39:12 +07:00
|
|
|
const struct inode_operations nfs4_dir_inode_operations = {
|
|
|
|
.create = nfs_create,
|
|
|
|
.lookup = nfs_lookup,
|
|
|
|
.atomic_open = nfs_atomic_open,
|
|
|
|
.link = nfs_link,
|
|
|
|
.unlink = nfs_unlink,
|
|
|
|
.symlink = nfs_symlink,
|
|
|
|
.mkdir = nfs_mkdir,
|
|
|
|
.rmdir = nfs_rmdir,
|
|
|
|
.mknod = nfs_mknod,
|
|
|
|
.rename = nfs_rename,
|
|
|
|
.permission = nfs_permission,
|
|
|
|
.getattr = nfs_getattr,
|
|
|
|
.setattr = nfs_setattr,
|
|
|
|
.getxattr = generic_getxattr,
|
|
|
|
.setxattr = generic_setxattr,
|
|
|
|
.listxattr = generic_listxattr,
|
|
|
|
.removexattr = generic_removexattr,
|
|
|
|
};
|
|
|
|
|
2007-02-12 15:55:39 +07:00
|
|
|
static const struct inode_operations nfs4_file_inode_operations = {
|
2005-06-23 00:16:22 +07:00
|
|
|
.permission = nfs_permission,
|
|
|
|
.getattr = nfs_getattr,
|
|
|
|
.setattr = nfs_setattr,
|
2010-12-09 18:35:25 +07:00
|
|
|
.getxattr = generic_getxattr,
|
|
|
|
.setxattr = generic_setxattr,
|
|
|
|
.listxattr = generic_listxattr,
|
|
|
|
.removexattr = generic_removexattr,
|
2005-06-23 00:16:22 +07:00
|
|
|
};
|
|
|
|
|
2006-08-23 07:06:11 +07:00
|
|
|
const struct nfs_rpc_ops nfs_v4_clientops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.version = 4, /* protocol version */
|
|
|
|
.dentry_ops = &nfs4_dentry_operations,
|
|
|
|
.dir_inode_ops = &nfs4_dir_inode_operations,
|
2005-06-23 00:16:22 +07:00
|
|
|
.file_inode_ops = &nfs4_file_inode_operations,
|
nfs: when attempting to open a directory, fall back on normal lookup (try #5)
commit d953126 changed how nfs_atomic_lookup handles an -EISDIR return
from an OPEN call. Prior to that patch, that caused the client to fall
back to doing a normal lookup. When that patch went in, the code began
returning that error to userspace. The d_revalidate codepath however
never had the corresponding change, so it was still possible to end up
with a NULL ctx->state pointer after that.
That patch caused a regression. When we attempt to open a directory that
does not have a cached dentry, that open now errors out with EISDIR. If
you attempt the same open with a cached dentry, it will succeed.
Fix this by reverting the change in nfs_atomic_lookup and allowing
attempts to open directories to fall back to a normal lookup
Also, add a NFSv4-specific f_ops->open routine that just returns
-ENOTDIR. This should never be called if things are working properly,
but if it ever is, then the dprintk may help in debugging.
To facilitate this, a new file_operations field is also added to the
nfs_rpc_ops struct.
Cc: stable@kernel.org
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
2011-11-05 00:31:21 +07:00
|
|
|
.file_ops = &nfs4_file_operations,
|
2005-04-17 05:20:36 +07:00
|
|
|
.getroot = nfs4_proc_get_root,
|
2012-04-28 00:27:45 +07:00
|
|
|
.submount = nfs4_submount,
|
2012-07-31 03:05:18 +07:00
|
|
|
.try_mount = nfs4_try_mount,
|
2005-04-17 05:20:36 +07:00
|
|
|
.getattr = nfs4_proc_getattr,
|
|
|
|
.setattr = nfs4_proc_setattr,
|
|
|
|
.lookup = nfs4_proc_lookup,
|
|
|
|
.access = nfs4_proc_access,
|
|
|
|
.readlink = nfs4_proc_readlink,
|
|
|
|
.create = nfs4_proc_create,
|
|
|
|
.remove = nfs4_proc_remove,
|
|
|
|
.unlink_setup = nfs4_proc_unlink_setup,
|
2012-03-20 01:54:41 +07:00
|
|
|
.unlink_rpc_prepare = nfs4_proc_unlink_rpc_prepare,
|
2005-04-17 05:20:36 +07:00
|
|
|
.unlink_done = nfs4_proc_unlink_done,
|
|
|
|
.rename = nfs4_proc_rename,
|
2010-09-18 04:31:57 +07:00
|
|
|
.rename_setup = nfs4_proc_rename_setup,
|
2012-03-20 01:54:42 +07:00
|
|
|
.rename_rpc_prepare = nfs4_proc_rename_rpc_prepare,
|
2010-09-18 04:31:57 +07:00
|
|
|
.rename_done = nfs4_proc_rename_done,
|
2005-04-17 05:20:36 +07:00
|
|
|
.link = nfs4_proc_link,
|
|
|
|
.symlink = nfs4_proc_symlink,
|
|
|
|
.mkdir = nfs4_proc_mkdir,
|
|
|
|
.rmdir = nfs4_proc_remove,
|
|
|
|
.readdir = nfs4_proc_readdir,
|
|
|
|
.mknod = nfs4_proc_mknod,
|
|
|
|
.statfs = nfs4_proc_statfs,
|
|
|
|
.fsinfo = nfs4_proc_fsinfo,
|
|
|
|
.pathconf = nfs4_proc_pathconf,
|
2006-08-23 07:06:10 +07:00
|
|
|
.set_capabilities = nfs4_server_capabilities,
|
2005-04-17 05:20:36 +07:00
|
|
|
.decode_dirent = nfs4_decode_dirent,
|
|
|
|
.read_setup = nfs4_proc_read_setup,
|
2012-06-21 02:53:47 +07:00
|
|
|
.read_pageio_init = pnfs_pageio_init_read,
|
2012-03-20 01:54:40 +07:00
|
|
|
.read_rpc_prepare = nfs4_proc_read_rpc_prepare,
|
2006-03-21 01:44:27 +07:00
|
|
|
.read_done = nfs4_read_done,
|
2005-04-17 05:20:36 +07:00
|
|
|
.write_setup = nfs4_proc_write_setup,
|
2012-06-21 02:53:48 +07:00
|
|
|
.write_pageio_init = pnfs_pageio_init_write,
|
2012-03-20 01:54:39 +07:00
|
|
|
.write_rpc_prepare = nfs4_proc_write_rpc_prepare,
|
2006-03-21 01:44:27 +07:00
|
|
|
.write_done = nfs4_write_done,
|
2005-04-17 05:20:36 +07:00
|
|
|
.commit_setup = nfs4_proc_commit_setup,
|
2012-04-21 01:47:39 +07:00
|
|
|
.commit_rpc_prepare = nfs4_proc_commit_rpc_prepare,
|
2006-03-21 01:44:27 +07:00
|
|
|
.commit_done = nfs4_commit_done,
|
2005-04-17 05:20:36 +07:00
|
|
|
.lock = nfs4_proc_lock,
|
2005-06-23 00:16:23 +07:00
|
|
|
.clear_acl_cache = nfs4_zap_acl_attr,
|
2009-03-20 02:35:50 +07:00
|
|
|
.close_context = nfs4_close_context,
|
2010-09-17 21:56:51 +07:00
|
|
|
.open_context = nfs4_atomic_open,
|
2012-06-21 02:53:43 +07:00
|
|
|
.have_delegation = nfs4_have_delegation,
|
2012-06-21 02:53:44 +07:00
|
|
|
.return_delegation = nfs4_inode_return_delegation,
|
2012-06-21 02:53:46 +07:00
|
|
|
.alloc_client = nfs4_alloc_client,
|
2011-03-01 08:34:08 +07:00
|
|
|
.init_client = nfs4_init_client,
|
2012-06-21 02:53:45 +07:00
|
|
|
.free_client = nfs4_free_client,
|
2012-07-31 03:05:19 +07:00
|
|
|
.create_server = nfs4_create_server,
|
|
|
|
.clone_server = nfs_clone_server,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2010-12-09 18:35:25 +07:00
|
|
|
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
|
|
|
|
.prefix = XATTR_NAME_NFSV4_ACL,
|
|
|
|
.list = nfs4_xattr_list_nfs4_acl,
|
|
|
|
.get = nfs4_xattr_get_nfs4_acl,
|
|
|
|
.set = nfs4_xattr_set_nfs4_acl,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct xattr_handler *nfs4_xattr_handlers[] = {
|
|
|
|
&nfs4_xattr_nfs4_acl_handler,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 8
|
|
|
|
* End:
|
|
|
|
*/
|