|
@@ -64,6 +64,7 @@
|
|
|
|
|
|
struct nfs4_opendata;
|
|
|
static int _nfs4_proc_open(struct nfs4_opendata *data);
|
|
|
+static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
|
|
|
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
|
|
|
static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
|
|
|
static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
|
|
@@ -341,6 +342,27 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid)
|
|
|
free_slotid, tbl->highest_used_slotid);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Signal state manager thread if session is drained
|
|
|
+ */
|
|
|
+static void nfs41_check_drain_session_complete(struct nfs4_session *ses)
|
|
|
+{
|
|
|
+ struct rpc_task *task;
|
|
|
+
|
|
|
+ if (!test_bit(NFS4CLNT_SESSION_DRAINING, &ses->clp->cl_state)) {
|
|
|
+ task = rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq);
|
|
|
+ if (task)
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ses->fc_slot_table.highest_used_slotid != -1)
|
|
|
+ return;
|
|
|
+
|
|
|
+ dprintk("%s COMPLETE: Session Drained\n", __func__);
|
|
|
+ complete(&ses->complete);
|
|
|
+}
|
|
|
+
|
|
|
static void nfs41_sequence_free_slot(const struct nfs_client *clp,
|
|
|
struct nfs4_sequence_res *res)
|
|
|
{
|
|
@@ -356,15 +378,7 @@ static void nfs41_sequence_free_slot(const struct nfs_client *clp,
|
|
|
|
|
|
spin_lock(&tbl->slot_tbl_lock);
|
|
|
nfs4_free_slot(tbl, res->sr_slotid);
|
|
|
-
|
|
|
- /* Signal state manager thread if session is drained */
|
|
|
- if (test_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) {
|
|
|
- if (tbl->highest_used_slotid == -1) {
|
|
|
- dprintk("%s COMPLETE: Session Drained\n", __func__);
|
|
|
- complete(&clp->cl_session->complete);
|
|
|
- }
|
|
|
- } else
|
|
|
- rpc_wake_up_next(&tbl->slot_tbl_waitq);
|
|
|
+ nfs41_check_drain_session_complete(clp->cl_session);
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
|
|
res->sr_slotid = NFS4_MAX_SLOT_TABLE;
|
|
|
}
|
|
@@ -421,7 +435,7 @@ out:
|
|
|
* Note: must be called with under the slot_tbl_lock.
|
|
|
*/
|
|
|
static u8
|
|
|
-nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task)
|
|
|
+nfs4_find_slot(struct nfs4_slot_table *tbl)
|
|
|
{
|
|
|
int slotid;
|
|
|
u8 ret_id = NFS4_MAX_SLOT_TABLE;
|
|
@@ -463,7 +477,8 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
|
|
|
tbl = &session->fc_slot_table;
|
|
|
|
|
|
spin_lock(&tbl->slot_tbl_lock);
|
|
|
- if (test_bit(NFS4CLNT_SESSION_DRAINING, &session->clp->cl_state)) {
|
|
|
+ if (test_bit(NFS4CLNT_SESSION_DRAINING, &session->clp->cl_state) &&
|
|
|
+ !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) {
|
|
|
/*
|
|
|
* The state manager will wait until the slot table is empty.
|
|
|
* Schedule the reset thread
|
|
@@ -474,7 +489,15 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
|
|
|
return -EAGAIN;
|
|
|
}
|
|
|
|
|
|
- slotid = nfs4_find_slot(tbl, task);
|
|
|
+ if (!rpc_queue_empty(&tbl->slot_tbl_waitq) &&
|
|
|
+ !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) {
|
|
|
+ rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
|
|
|
+ spin_unlock(&tbl->slot_tbl_lock);
|
|
|
+ dprintk("%s enforce FIFO order\n", __func__);
|
|
|
+ return -EAGAIN;
|
|
|
+ }
|
|
|
+
|
|
|
+ slotid = nfs4_find_slot(tbl);
|
|
|
if (slotid == NFS4_MAX_SLOT_TABLE) {
|
|
|
rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
|
@@ -483,6 +506,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
|
|
|
}
|
|
|
spin_unlock(&tbl->slot_tbl_lock);
|
|
|
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_NORMAL);
|
|
|
slot = tbl->slots + slotid;
|
|
|
args->sa_session = session;
|
|
|
args->sa_slotid = slotid;
|
|
@@ -545,6 +569,12 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
|
|
|
rpc_call_start(task);
|
|
|
}
|
|
|
|
|
|
+static void nfs41_call_priv_sync_prepare(struct rpc_task *task, void *calldata)
|
|
|
+{
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
|
|
|
+ nfs41_call_sync_prepare(task, calldata);
|
|
|
+}
|
|
|
+
|
|
|
static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
|
|
|
{
|
|
|
struct nfs41_call_sync_data *data = calldata;
|
|
@@ -557,12 +587,18 @@ struct rpc_call_ops nfs41_call_sync_ops = {
|
|
|
.rpc_call_done = nfs41_call_sync_done,
|
|
|
};
|
|
|
|
|
|
+struct rpc_call_ops nfs41_call_priv_sync_ops = {
|
|
|
+ .rpc_call_prepare = nfs41_call_priv_sync_prepare,
|
|
|
+ .rpc_call_done = nfs41_call_sync_done,
|
|
|
+};
|
|
|
+
|
|
|
static int nfs4_call_sync_sequence(struct nfs_client *clp,
|
|
|
struct rpc_clnt *clnt,
|
|
|
struct rpc_message *msg,
|
|
|
struct nfs4_sequence_args *args,
|
|
|
struct nfs4_sequence_res *res,
|
|
|
- int cache_reply)
|
|
|
+ int cache_reply,
|
|
|
+ int privileged)
|
|
|
{
|
|
|
int ret;
|
|
|
struct rpc_task *task;
|
|
@@ -580,6 +616,8 @@ static int nfs4_call_sync_sequence(struct nfs_client *clp,
|
|
|
};
|
|
|
|
|
|
res->sr_slotid = NFS4_MAX_SLOT_TABLE;
|
|
|
+ if (privileged)
|
|
|
+ task_setup.callback_ops = &nfs41_call_priv_sync_ops;
|
|
|
task = rpc_run_task(&task_setup);
|
|
|
if (IS_ERR(task))
|
|
|
ret = PTR_ERR(task);
|
|
@@ -597,7 +635,7 @@ int _nfs4_call_sync_session(struct nfs_server *server,
|
|
|
int cache_reply)
|
|
|
{
|
|
|
return nfs4_call_sync_sequence(server->nfs_client, server->client,
|
|
|
- msg, args, res, cache_reply);
|
|
|
+ msg, args, res, cache_reply, 0);
|
|
|
}
|
|
|
|
|
|
#endif /* CONFIG_NFS_V4_1 */
|
|
@@ -1035,7 +1073,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod
|
|
|
memset(&opendata->o_res, 0, sizeof(opendata->o_res));
|
|
|
memset(&opendata->c_res, 0, sizeof(opendata->c_res));
|
|
|
nfs4_init_opendata_res(opendata);
|
|
|
- ret = _nfs4_proc_open(opendata);
|
|
|
+ ret = _nfs4_recover_proc_open(opendata);
|
|
|
if (ret != 0)
|
|
|
return ret;
|
|
|
newstate = nfs4_opendata_to_nfs4_state(opendata);
|
|
@@ -1326,6 +1364,12 @@ out_no_action:
|
|
|
|
|
|
}
|
|
|
|
|
|
+static void nfs4_recover_open_prepare(struct rpc_task *task, void *calldata)
|
|
|
+{
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
|
|
|
+ nfs4_open_prepare(task, calldata);
|
|
|
+}
|
|
|
+
|
|
|
static void nfs4_open_done(struct rpc_task *task, void *calldata)
|
|
|
{
|
|
|
struct nfs4_opendata *data = calldata;
|
|
@@ -1384,10 +1428,13 @@ static const struct rpc_call_ops nfs4_open_ops = {
|
|
|
.rpc_release = nfs4_open_release,
|
|
|
};
|
|
|
|
|
|
-/*
|
|
|
- * Note: On error, nfs4_proc_open will free the struct nfs4_opendata
|
|
|
- */
|
|
|
-static int _nfs4_proc_open(struct nfs4_opendata *data)
|
|
|
+static const struct rpc_call_ops nfs4_recover_open_ops = {
|
|
|
+ .rpc_call_prepare = nfs4_recover_open_prepare,
|
|
|
+ .rpc_call_done = nfs4_open_done,
|
|
|
+ .rpc_release = nfs4_open_release,
|
|
|
+};
|
|
|
+
|
|
|
+static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
|
|
|
{
|
|
|
struct inode *dir = data->dir->d_inode;
|
|
|
struct nfs_server *server = NFS_SERVER(dir);
|
|
@@ -1414,21 +1461,57 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
|
|
|
data->rpc_done = 0;
|
|
|
data->rpc_status = 0;
|
|
|
data->cancelled = 0;
|
|
|
+ if (isrecover)
|
|
|
+ task_setup_data.callback_ops = &nfs4_recover_open_ops;
|
|
|
task = rpc_run_task(&task_setup_data);
|
|
|
- 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);
|
|
|
+ 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;
|
|
|
|
|
|
- if (o_res->fh.size == 0)
|
|
|
- _nfs4_proc_lookup(dir, o_arg->name, &o_res->fh, o_res->f_attr);
|
|
|
+ nfs_refresh_inode(dir, o_res->dir_attr);
|
|
|
+
|
|
|
+ if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
|
|
|
+ status = _nfs4_proc_open_confirm(data);
|
|
|
+ if (status != 0)
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+
|
|
|
+ return status;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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);
|
|
|
+ if (status != 0 || !data->rpc_done)
|
|
|
+ return status;
|
|
|
|
|
|
if (o_arg->open_flags & O_CREAT) {
|
|
|
update_changeattr(dir, &o_res->cinfo);
|
|
@@ -1752,11 +1835,10 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
|
|
|
if (calldata->arg.fmode == 0)
|
|
|
break;
|
|
|
default:
|
|
|
- if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
|
|
|
- nfs_restart_rpc(task, server->nfs_client);
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (nfs4_async_handle_error(task, server, state) == -EAGAIN)
|
|
|
+ rpc_restart_call_prepare(task);
|
|
|
}
|
|
|
+ nfs_release_seqid(calldata->arg.seqid);
|
|
|
nfs_refresh_inode(calldata->inode, calldata->res.fattr);
|
|
|
}
|
|
|
|
|
@@ -1848,8 +1930,6 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
|
|
|
calldata->state = state;
|
|
|
calldata->arg.fh = NFS_FH(state->inode);
|
|
|
calldata->arg.stateid = &state->open_stateid;
|
|
|
- if (nfs4_has_session(server->nfs_client))
|
|
|
- memset(calldata->arg.stateid->data, 0, 4); /* clear seqid */
|
|
|
/* Serialization for the sequence id */
|
|
|
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
|
|
|
if (calldata->arg.seqid == NULL)
|
|
@@ -3941,6 +4021,12 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
|
|
|
dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
|
|
|
}
|
|
|
|
|
|
+static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
|
|
|
+{
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
|
|
|
+ nfs4_lock_prepare(task, calldata);
|
|
|
+}
|
|
|
+
|
|
|
static void nfs4_lock_done(struct rpc_task *task, void *calldata)
|
|
|
{
|
|
|
struct nfs4_lockdata *data = calldata;
|
|
@@ -3996,7 +4082,13 @@ static const struct rpc_call_ops nfs4_lock_ops = {
|
|
|
.rpc_release = nfs4_lock_release,
|
|
|
};
|
|
|
|
|
|
-static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int reclaim)
|
|
|
+static const struct rpc_call_ops nfs4_recover_lock_ops = {
|
|
|
+ .rpc_call_prepare = nfs4_recover_lock_prepare,
|
|
|
+ .rpc_call_done = nfs4_lock_done,
|
|
|
+ .rpc_release = nfs4_lock_release,
|
|
|
+};
|
|
|
+
|
|
|
+static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
|
|
|
{
|
|
|
struct nfs4_lockdata *data;
|
|
|
struct rpc_task *task;
|
|
@@ -4020,8 +4112,11 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
|
|
|
return -ENOMEM;
|
|
|
if (IS_SETLKW(cmd))
|
|
|
data->arg.block = 1;
|
|
|
- if (reclaim != 0)
|
|
|
- data->arg.reclaim = 1;
|
|
|
+ if (recovery_type > NFS_LOCK_NEW) {
|
|
|
+ if (recovery_type == NFS_LOCK_RECLAIM)
|
|
|
+ data->arg.reclaim = NFS_LOCK_RECLAIM;
|
|
|
+ task_setup_data.callback_ops = &nfs4_recover_lock_ops;
|
|
|
+ }
|
|
|
msg.rpc_argp = &data->arg,
|
|
|
msg.rpc_resp = &data->res,
|
|
|
task_setup_data.callback_data = data;
|
|
@@ -4048,7 +4143,7 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
|
|
|
/* Cache the lock if possible... */
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
|
|
|
return 0;
|
|
|
- err = _nfs4_do_setlk(state, F_SETLK, request, 1);
|
|
|
+ err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
|
|
|
if (err != -NFS4ERR_DELAY)
|
|
|
break;
|
|
|
nfs4_handle_exception(server, err, &exception);
|
|
@@ -4068,7 +4163,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
|
|
|
do {
|
|
|
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
|
|
|
return 0;
|
|
|
- err = _nfs4_do_setlk(state, F_SETLK, request, 0);
|
|
|
+ err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED);
|
|
|
switch (err) {
|
|
|
default:
|
|
|
goto out;
|
|
@@ -4104,7 +4199,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
|
|
|
status = do_vfs_lock(request->fl_file, request);
|
|
|
goto out_unlock;
|
|
|
}
|
|
|
- status = _nfs4_do_setlk(state, cmd, request, 0);
|
|
|
+ status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW);
|
|
|
if (status != 0)
|
|
|
goto out_unlock;
|
|
|
/* Note: we always want to sleep here! */
|
|
@@ -4187,7 +4282,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
|
|
|
if (err != 0)
|
|
|
goto out;
|
|
|
do {
|
|
|
- err = _nfs4_do_setlk(state, F_SETLK, fl, 0);
|
|
|
+ err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
|
|
|
switch (err) {
|
|
|
default:
|
|
|
printk(KERN_ERR "%s: unhandled error %d.\n",
|
|
@@ -4395,11 +4490,12 @@ static void nfs4_get_lease_time_prepare(struct rpc_task *task,
|
|
|
(struct nfs4_get_lease_time_data *)calldata;
|
|
|
|
|
|
dprintk("--> %s\n", __func__);
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
|
|
|
/* just setup sequence, do not trigger session recovery
|
|
|
since we're invoked within one */
|
|
|
ret = nfs41_setup_sequence(data->clp->cl_session,
|
|
|
- &data->args->la_seq_args,
|
|
|
- &data->res->lr_seq_res, 0, task);
|
|
|
+ &data->args->la_seq_args,
|
|
|
+ &data->res->lr_seq_res, 0, task);
|
|
|
|
|
|
BUG_ON(ret == -EAGAIN);
|
|
|
rpc_call_start(task);
|
|
@@ -4619,7 +4715,7 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
|
|
|
tbl = &session->fc_slot_table;
|
|
|
tbl->highest_used_slotid = -1;
|
|
|
spin_lock_init(&tbl->slot_tbl_lock);
|
|
|
- rpc_init_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
|
|
|
+ rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
|
|
|
|
|
|
tbl = &session->bc_slot_table;
|
|
|
tbl->highest_used_slotid = -1;
|
|
@@ -4838,14 +4934,22 @@ int nfs4_init_session(struct nfs_server *server)
|
|
|
{
|
|
|
struct nfs_client *clp = server->nfs_client;
|
|
|
struct nfs4_session *session;
|
|
|
+ unsigned int rsize, wsize;
|
|
|
int ret;
|
|
|
|
|
|
if (!nfs4_has_session(clp))
|
|
|
return 0;
|
|
|
|
|
|
+ rsize = server->rsize;
|
|
|
+ if (rsize == 0)
|
|
|
+ rsize = NFS_MAX_FILE_IO_SIZE;
|
|
|
+ wsize = server->wsize;
|
|
|
+ if (wsize == 0)
|
|
|
+ wsize = NFS_MAX_FILE_IO_SIZE;
|
|
|
+
|
|
|
session = clp->cl_session;
|
|
|
- session->fc_attrs.max_rqst_sz = server->wsize + nfs41_maxwrite_overhead;
|
|
|
- session->fc_attrs.max_resp_sz = server->rsize + nfs41_maxread_overhead;
|
|
|
+ session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
|
|
|
+ session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
|
|
|
|
|
|
ret = nfs4_recover_expired_lease(server);
|
|
|
if (!ret)
|
|
@@ -4871,7 +4975,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
|
|
|
args.sa_cache_this = 0;
|
|
|
|
|
|
return nfs4_call_sync_sequence(clp, clp->cl_rpcclient, &msg, &args,
|
|
|
- &res, 0);
|
|
|
+ &res, args.sa_cache_this, 1);
|
|
|
}
|
|
|
|
|
|
void nfs41_sequence_call_done(struct rpc_task *task, void *data)
|
|
@@ -4953,6 +5057,7 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data)
|
|
|
{
|
|
|
struct nfs4_reclaim_complete_data *calldata = data;
|
|
|
|
|
|
+ rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
|
|
|
if (nfs4_setup_sequence(calldata->clp, &calldata->arg.seq_args,
|
|
|
&calldata->res.seq_res, 0, task))
|
|
|
return;
|