More NFS client bugfixes for 3.10

- Ensure that we match the 'sec=' mount flavour against the server list
 - Fix the NFSv4 byte range locking in the presence of delegations
 - Ensure that we conform to the NFSv4.1 spec w.r.t. freeing lock stateids
 - Fix a pNFS data server connection race
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.13 (GNU/Linux)
 
 iQIcBAABAgAGBQJRit1yAAoJEGcL54qWCgDyD9EQAKgb37dXhGt7OXBRBP4EY/T8
 xJZ2tmdDZ6etLFJVftqCv05hBvyfilPLK0E9zg/zW/kvkKxYQ/fykvpzBR/+Q7KF
 quOmjDHLhDTXBnXzPg1HEoeTaXI2/a8CdjpxxEkthD4+FaKlyCXM+EFtA9orT9ZI
 oM+aNaqEzTjoQyryTFMcHxAvsrqjnZBa0MT6Fh45HaLaijV7CdDWoj6gjy6Lc3Al
 4wHeT8QrZTp/NfIN16uykFZjeWwul4N9upu+CI2V8ZDMEit6JDYX4sl5tB41PzYW
 audDBcu0waSqoVQ2mJ5OHoYGZf0wopMUFaAst+tn0pQvwWUfTjD8XtO8uOgeMNoz
 2S+XxUC2qhSMszwNBVSmwe2LtSAyHiw32Md4hqkLYDH2c7tk8bJPKDXZJACBzJS7
 O1aMmOgWar8+nmzvmXFeU804SxBykV1V8UgtXWp5IwC36V0HAYnM5xtHwXBR7HWe
 lnuVHVdux7ySeAyrs2aMdKk7SAw5OC//WW8qoEF5USDEIljeoBzA+IYu9n91Hg2b
 ufnsyxumGJ6dZ0iU2nJVoLagRaZcm6kOhnxcegMpb9IH2+RLCQNef09lj2iklm2j
 mJA4o2lkVEHOswg/NwKn/I4ho8tbNNb8v//S5KiqrYhiiqZhOzu3RRtFeZi91iac
 P/g+hPzfuGnmwcoCEUSa
 =5zpc
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-3.10-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull more NFS client bugfixes from Trond Myklebust:

 - Ensure that we match the 'sec=' mount flavour against the server list

 - Fix the NFSv4 byte range locking in the presence of delegations

 - Ensure that we conform to the NFSv4.1 spec w.r.t.  freeing lock
   stateids

 - Fix a pNFS data server connection race

* tag 'nfs-for-3.10-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs:
  NFS4.1 Fix data server connection race
  NFSv3: match sec= flavor against server list
  NFSv4.1: Ensure that we free the lock stateid on the server
  NFSv4: Convert nfs41_free_stateid to use an asynchronous RPC call
  SUNRPC: Don't spam syslog with "Pseudoflavor not found" messages
  NFSv4.x: Fix handling of partially delegated locks
This commit is contained in:
Linus Torvalds 2013-05-09 10:24:54 -07:00
commit 8cbc95ee74
10 changed files with 172 additions and 46 deletions

View File

@ -47,6 +47,8 @@ struct nfs4_minor_version_ops {
const nfs4_stateid *);
int (*find_root_sec)(struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *);
int (*free_lock_state)(struct nfs_server *,
struct nfs4_lock_state *);
const struct nfs4_state_recovery_ops *reboot_recovery_ops;
const struct nfs4_state_recovery_ops *nograce_recovery_ops;
const struct nfs4_state_maintenance_ops *state_renewal_ops;
@ -234,7 +236,6 @@ extern int nfs4_proc_fs_locations(struct rpc_clnt *, struct inode *, const struc
extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *);
extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
extern int nfs4_release_lockowner(struct nfs4_lock_state *);
extern const struct xattr_handler *nfs4_xattr_handlers[];
extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
const struct nfs_open_context *ctx,

View File

@ -70,6 +70,8 @@ struct nfs4_pnfs_ds {
struct list_head ds_addrs;
struct nfs_client *ds_clp;
atomic_t ds_count;
unsigned long ds_state;
#define NFS4DS_CONNECTING 0 /* ds is establishing connection */
};
struct nfs4_file_layout_dsaddr {

View File

@ -775,6 +775,22 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j)
return flseg->fh_array[i];
}
static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
{
might_sleep();
wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING,
nfs_wait_bit_killable, TASK_KILLABLE);
}
static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
{
smp_mb__before_clear_bit();
clear_bit(NFS4DS_CONNECTING, &ds->ds_state);
smp_mb__after_clear_bit();
wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING);
}
struct nfs4_pnfs_ds *
nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
{
@ -791,16 +807,22 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
filelayout_mark_devid_invalid(devid);
return NULL;
}
if (ds->ds_clp)
return ds;
if (!ds->ds_clp) {
if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) {
struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode);
int err;
err = nfs4_ds_connect(s, ds);
if (err) {
nfs4_mark_deviceid_unavailable(devid);
return NULL;
ds = NULL;
}
nfs4_clear_ds_conn_bit(ds);
} else {
/* Either ds is connected, or ds is NULL */
nfs4_wait_ds_connect(ds);
}
return ds;
}

View File

@ -4766,9 +4766,9 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
if (status != 0)
goto out;
/* Is this a delegated lock? */
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
goto out;
lsp = request->fl_u.nfs4_fl.owner;
if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) == 0)
goto out;
seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
status = -ENOMEM;
if (seqid == NULL)
@ -5238,9 +5238,8 @@ static const struct rpc_call_ops nfs4_release_lockowner_ops = {
.rpc_release = nfs4_release_lockowner_release,
};
int nfs4_release_lockowner(struct nfs4_lock_state *lsp)
static int nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
{
struct nfs_server *server = lsp->ls_state->owner->so_server;
struct nfs_release_lockowner_data *data;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
@ -6783,26 +6782,76 @@ static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
return err;
}
static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
{
struct nfs41_free_stateid_args args = {
.stateid = stateid,
};
struct nfs_free_stateid_data {
struct nfs_server *server;
struct nfs41_free_stateid_args args;
struct nfs41_free_stateid_res res;
};
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,
bool privileged)
{
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
.rpc_argp = &args,
.rpc_resp = &res,
};
int status;
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;
dprintk("NFS call free_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
nfs4_set_sequence_privileged(&args.seq_args);
status = nfs4_call_sync_sequence(server->client, server, &msg,
&args.seq_args, &res.seq_res);
dprintk("NFS reply free_stateid: %d\n", status);
return status;
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);
}
/**
@ -6816,15 +6865,29 @@ static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
*/
static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
{
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_free_stateid(server, stateid);
if (err != -NFS4ERR_DELAY)
break;
nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
struct rpc_task *task;
int ret;
task = _nfs41_free_stateid(server, stateid, true);
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;
}
static int nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
{
struct rpc_task *task;
task = _nfs41_free_stateid(server, &lsp->ls_stateid, false);
nfs4_free_lock_state(server, lsp);
if (IS_ERR(task))
return PTR_ERR(task);
rpc_put_task(task);
return 0;
}
static bool nfs41_match_stateid(const nfs4_stateid *s1,
@ -6916,6 +6979,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
.call_sync = _nfs4_call_sync,
.match_stateid = nfs4_match_stateid,
.find_root_sec = nfs4_find_root_sec,
.free_lock_state = nfs4_release_lockowner,
.reboot_recovery_ops = &nfs40_reboot_recovery_ops,
.nograce_recovery_ops = &nfs40_nograce_recovery_ops,
.state_renewal_ops = &nfs40_state_renewal_ops,
@ -6933,6 +6997,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.call_sync = nfs4_call_sync_sequence,
.match_stateid = nfs41_match_stateid,
.find_root_sec = nfs41_find_root_sec,
.free_lock_state = nfs41_free_lock_state,
.reboot_recovery_ops = &nfs41_reboot_recovery_ops,
.nograce_recovery_ops = &nfs41_nograce_recovery_ops,
.state_renewal_ops = &nfs41_state_renewal_ops,

View File

@ -921,6 +921,7 @@ static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_
*/
void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
{
struct nfs_server *server;
struct nfs4_state *state;
if (lsp == NULL)
@ -932,11 +933,13 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
if (list_empty(&state->lock_states))
clear_bit(LK_STATE_IN_USE, &state->flags);
spin_unlock(&state->state_lock);
server = state->owner->so_server;
if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
if (nfs4_release_lockowner(lsp) == 0)
return;
}
nfs4_free_lock_state(lsp->ls_state->owner->so_server, lsp);
struct nfs_client *clp = server->nfs_client;
clp->cl_mvops->free_lock_state(server, lsp);
} else
nfs4_free_lock_state(server, lsp);
}
static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)

View File

@ -2003,7 +2003,7 @@ static void encode_free_stateid(struct xdr_stream *xdr,
struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_FREE_STATEID, decode_free_stateid_maxsz, hdr);
encode_nfs4_stateid(xdr, args->stateid);
encode_nfs4_stateid(xdr, &args->stateid);
}
#endif /* CONFIG_NFS_V4_1 */

View File

@ -1610,16 +1610,15 @@ static int nfs_parse_mount_options(char *raw,
/*
* Select a security flavor for this mount. The selected flavor
* is planted in args->auth_flavors[0].
*
* Returns 0 on success, -EACCES on failure.
*/
static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
static int nfs_select_flavor(struct nfs_parsed_mount_data *args,
struct nfs_mount_request *request)
{
unsigned int i, count = *(request->auth_flav_len);
rpc_authflavor_t flavor;
if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR)
goto out;
/*
* The NFSv2 MNT operation does not return a flavor list.
*/
@ -1633,6 +1632,25 @@ static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
if (count == 0)
goto out_default;
/*
* If the sec= mount option is used, the specified flavor or AUTH_NULL
* must be in the list returned by the server.
*
* AUTH_NULL has a special meaning when it's in the server list - it
* means that the server will ignore the rpc creds, so any flavor
* can be used.
*/
if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) {
for (i = 0; i < count; i++) {
if (args->auth_flavors[0] == request->auth_flavs[i] ||
request->auth_flavs[i] == RPC_AUTH_NULL)
goto out;
}
dfprintk(MOUNT, "NFS: auth flavor %d not supported by server\n",
args->auth_flavors[0]);
goto out_err;
}
/*
* RFC 2623, section 2.7 suggests we SHOULD prefer the
* flavor listed first. However, some servers list
@ -1653,12 +1671,29 @@ static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
}
}
/*
* As a last chance, see if the server list contains AUTH_NULL -
* if it does, use the default flavor.
*/
for (i = 0; i < count; i++) {
if (request->auth_flavs[i] == RPC_AUTH_NULL)
goto out_default;
}
dfprintk(MOUNT, "NFS: no auth flavors in common with server\n");
goto out_err;
out_default:
flavor = RPC_AUTH_UNIX;
/* use default if flavor not already set */
flavor = (args->auth_flavors[0] == RPC_AUTH_MAXFLAVOR) ?
RPC_AUTH_UNIX : args->auth_flavors[0];
out_set:
args->auth_flavors[0] = flavor;
out:
dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]);
return 0;
out_err:
return -EACCES;
}
/*
@ -1721,8 +1756,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
return status;
}
nfs_select_flavor(args, &request);
return 0;
return nfs_select_flavor(args, &request);
}
struct dentry *nfs_try_mount(int flags, const char *dev_name,

View File

@ -1176,7 +1176,7 @@ struct nfs41_test_stateid_res {
struct nfs41_free_stateid_args {
struct nfs4_sequence_args seq_args;
nfs4_stateid *stateid;
nfs4_stateid stateid;
};
struct nfs41_free_stateid_res {

View File

@ -867,8 +867,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
err = -EINVAL;
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
if (!gss_auth->mech) {
printk(KERN_WARNING "%s: Pseudoflavor %d not found!\n",
__func__, flavor);
dprintk("RPC: Pseudoflavor %d not found!\n", flavor);
goto err_free;
}
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);

View File

@ -360,7 +360,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
auth = rpcauth_create(args->authflavor, clnt);
if (IS_ERR(auth)) {
printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n",
dprintk("RPC: Couldn't create auth handle (flavor %u)\n",
args->authflavor);
err = PTR_ERR(auth);
goto out_no_auth;