Commit 8cbc95ee authored by Linus Torvalds's avatar Linus Torvalds

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
parents ea44083a c23266d5
...@@ -47,6 +47,8 @@ struct nfs4_minor_version_ops { ...@@ -47,6 +47,8 @@ struct nfs4_minor_version_ops {
const nfs4_stateid *); const nfs4_stateid *);
int (*find_root_sec)(struct nfs_server *, struct nfs_fh *, int (*find_root_sec)(struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *); 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 *reboot_recovery_ops;
const struct nfs4_state_recovery_ops *nograce_recovery_ops; const struct nfs4_state_recovery_ops *nograce_recovery_ops;
const struct nfs4_state_maintenance_ops *state_renewal_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 ...@@ -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 *, extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *); struct nfs_fh *, struct nfs_fattr *);
extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); 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 const struct xattr_handler *nfs4_xattr_handlers[];
extern int nfs4_set_rw_stateid(nfs4_stateid *stateid, extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
const struct nfs_open_context *ctx, const struct nfs_open_context *ctx,
......
...@@ -70,6 +70,8 @@ struct nfs4_pnfs_ds { ...@@ -70,6 +70,8 @@ struct nfs4_pnfs_ds {
struct list_head ds_addrs; struct list_head ds_addrs;
struct nfs_client *ds_clp; struct nfs_client *ds_clp;
atomic_t ds_count; atomic_t ds_count;
unsigned long ds_state;
#define NFS4DS_CONNECTING 0 /* ds is establishing connection */
}; };
struct nfs4_file_layout_dsaddr { struct nfs4_file_layout_dsaddr {
......
...@@ -775,6 +775,22 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j) ...@@ -775,6 +775,22 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j)
return flseg->fh_array[i]; 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 * struct nfs4_pnfs_ds *
nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) 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) ...@@ -791,16 +807,22 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
filelayout_mark_devid_invalid(devid); filelayout_mark_devid_invalid(devid);
return NULL; 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); struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode);
int err; int err;
err = nfs4_ds_connect(s, ds); err = nfs4_ds_connect(s, ds);
if (err) { if (err) {
nfs4_mark_deviceid_unavailable(devid); 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; return ds;
} }
......
...@@ -4766,9 +4766,9 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * ...@@ -4766,9 +4766,9 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
if (status != 0) if (status != 0)
goto out; goto out;
/* Is this a delegated lock? */ /* Is this a delegated lock? */
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
goto out;
lsp = request->fl_u.nfs4_fl.owner; 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); seqid = nfs_alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
status = -ENOMEM; status = -ENOMEM;
if (seqid == NULL) if (seqid == NULL)
...@@ -5238,9 +5238,8 @@ static const struct rpc_call_ops nfs4_release_lockowner_ops = { ...@@ -5238,9 +5238,8 @@ static const struct rpc_call_ops nfs4_release_lockowner_ops = {
.rpc_release = nfs4_release_lockowner_release, .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 nfs_release_lockowner_data *data;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
...@@ -6783,26 +6782,76 @@ static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) ...@@ -6783,26 +6782,76 @@ static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
return err; return err;
} }
static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) struct nfs_free_stateid_data {
{ struct nfs_server *server;
struct nfs41_free_stateid_args args = { struct nfs41_free_stateid_args args;
.stateid = stateid,
};
struct nfs41_free_stateid_res res; 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 = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID], .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); dprintk("NFS call free_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); data = kmalloc(sizeof(*data), GFP_NOFS);
nfs4_set_sequence_privileged(&args.seq_args); if (!data)
status = nfs4_call_sync_sequence(server->client, server, &msg, return ERR_PTR(-ENOMEM);
&args.seq_args, &res.seq_res); data->server = server;
dprintk("NFS reply free_stateid: %d\n", status); nfs4_stateid_copy(&data->args.stateid, stateid);
return status;
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) ...@@ -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) static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
{ {
struct nfs4_exception exception = { }; struct rpc_task *task;
int err; int ret;
do {
err = _nfs4_free_stateid(server, stateid); task = _nfs41_free_stateid(server, stateid, true);
if (err != -NFS4ERR_DELAY) if (IS_ERR(task))
break; return PTR_ERR(task);
nfs4_handle_exception(server, err, &exception); ret = rpc_wait_for_completion_task(task);
} while (exception.retry); if (!ret)
return err; 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, 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 = { ...@@ -6916,6 +6979,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
.call_sync = _nfs4_call_sync, .call_sync = _nfs4_call_sync,
.match_stateid = nfs4_match_stateid, .match_stateid = nfs4_match_stateid,
.find_root_sec = nfs4_find_root_sec, .find_root_sec = nfs4_find_root_sec,
.free_lock_state = nfs4_release_lockowner,
.reboot_recovery_ops = &nfs40_reboot_recovery_ops, .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
.nograce_recovery_ops = &nfs40_nograce_recovery_ops, .nograce_recovery_ops = &nfs40_nograce_recovery_ops,
.state_renewal_ops = &nfs40_state_renewal_ops, .state_renewal_ops = &nfs40_state_renewal_ops,
...@@ -6933,6 +6997,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { ...@@ -6933,6 +6997,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
.call_sync = nfs4_call_sync_sequence, .call_sync = nfs4_call_sync_sequence,
.match_stateid = nfs41_match_stateid, .match_stateid = nfs41_match_stateid,
.find_root_sec = nfs41_find_root_sec, .find_root_sec = nfs41_find_root_sec,
.free_lock_state = nfs41_free_lock_state,
.reboot_recovery_ops = &nfs41_reboot_recovery_ops, .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
.nograce_recovery_ops = &nfs41_nograce_recovery_ops, .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
.state_renewal_ops = &nfs41_state_renewal_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_ ...@@ -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) void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
{ {
struct nfs_server *server;
struct nfs4_state *state; struct nfs4_state *state;
if (lsp == NULL) if (lsp == NULL)
...@@ -932,11 +933,13 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp) ...@@ -932,11 +933,13 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp)
if (list_empty(&state->lock_states)) if (list_empty(&state->lock_states))
clear_bit(LK_STATE_IN_USE, &state->flags); clear_bit(LK_STATE_IN_USE, &state->flags);
spin_unlock(&state->state_lock); spin_unlock(&state->state_lock);
server = state->owner->so_server;
if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) { if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
if (nfs4_release_lockowner(lsp) == 0) struct nfs_client *clp = server->nfs_client;
return;
} clp->cl_mvops->free_lock_state(server, lsp);
nfs4_free_lock_state(lsp->ls_state->owner->so_server, lsp); } else
nfs4_free_lock_state(server, lsp);
} }
static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src) 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, ...@@ -2003,7 +2003,7 @@ static void encode_free_stateid(struct xdr_stream *xdr,
struct compound_hdr *hdr) struct compound_hdr *hdr)
{ {
encode_op_hdr(xdr, OP_FREE_STATEID, decode_free_stateid_maxsz, 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 */ #endif /* CONFIG_NFS_V4_1 */
......
...@@ -1610,16 +1610,15 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1610,16 +1610,15 @@ static int nfs_parse_mount_options(char *raw,
/* /*
* Select a security flavor for this mount. The selected flavor * Select a security flavor for this mount. The selected flavor
* is planted in args->auth_flavors[0]. * 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) struct nfs_mount_request *request)
{ {
unsigned int i, count = *(request->auth_flav_len); unsigned int i, count = *(request->auth_flav_len);
rpc_authflavor_t flavor; rpc_authflavor_t flavor;
if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR)
goto out;
/* /*
* The NFSv2 MNT operation does not return a flavor list. * 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, ...@@ -1633,6 +1632,25 @@ static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
if (count == 0) if (count == 0)
goto out_default; 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 * RFC 2623, section 2.7 suggests we SHOULD prefer the
* flavor listed first. However, some servers list * flavor listed first. However, some servers list
...@@ -1653,12 +1671,29 @@ static void nfs_select_flavor(struct nfs_parsed_mount_data *args, ...@@ -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: 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: out_set:
args->auth_flavors[0] = flavor; args->auth_flavors[0] = flavor;
out: out:
dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]); 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, ...@@ -1721,8 +1756,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
return status; return status;
} }
nfs_select_flavor(args, &request); return nfs_select_flavor(args, &request);
return 0;
} }
struct dentry *nfs_try_mount(int flags, const char *dev_name, struct dentry *nfs_try_mount(int flags, const char *dev_name,
......
...@@ -1176,7 +1176,7 @@ struct nfs41_test_stateid_res { ...@@ -1176,7 +1176,7 @@ struct nfs41_test_stateid_res {
struct nfs41_free_stateid_args { struct nfs41_free_stateid_args {
struct nfs4_sequence_args seq_args; struct nfs4_sequence_args seq_args;
nfs4_stateid *stateid; nfs4_stateid stateid;
}; };
struct nfs41_free_stateid_res { struct nfs41_free_stateid_res {
......
...@@ -867,8 +867,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) ...@@ -867,8 +867,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
err = -EINVAL; err = -EINVAL;
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor); gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
if (!gss_auth->mech) { if (!gss_auth->mech) {
printk(KERN_WARNING "%s: Pseudoflavor %d not found!\n", dprintk("RPC: Pseudoflavor %d not found!\n", flavor);
__func__, flavor);
goto err_free; goto err_free;
} }
gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor); 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 ...@@ -360,7 +360,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
auth = rpcauth_create(args->authflavor, clnt); auth = rpcauth_create(args->authflavor, clnt);
if (IS_ERR(auth)) { 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); args->authflavor);
err = PTR_ERR(auth); err = PTR_ERR(auth);
goto out_no_auth; goto out_no_auth;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment