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:
Коммит
8cbc95ee74
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -1610,16 +1610,15 @@ out_security_failure:
|
|||
/*
|
||||
* 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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче