NFSv4.1: Reset the sequence number for slots that have been deallocated
[linux-2.6-block.git] / fs / nfs / nfs4proc.c
index 68b21d81b7acfa79bef010023195c1f45023c82b..52435ec441932b878e2824a9cd5ad3a24714328e 100644 (file)
@@ -206,7 +206,6 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
 {
        __be32 *start, *p;
 
-       BUG_ON(readdir->count < 80);
        if (cookie > 2) {
                readdir->cookie = cookie;
                memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
@@ -339,8 +338,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
                        dprintk("%s ERROR: %d Reset session\n", __func__,
                                errorcode);
                        nfs4_schedule_session_recovery(clp->cl_session, errorcode);
-                       exception->retry = 1;
-                       break;
+                       goto wait_on_recovery;
 #endif /* defined(CONFIG_NFS_V4_1) */
                case -NFS4ERR_FILE_OPEN:
                        if (exception->timeout > HZ) {
@@ -414,17 +412,18 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
  * Must be called while holding tbl->slot_tbl_lock
  */
 static void
-nfs4_free_slot(struct nfs4_slot_table *tbl, u32 slotid)
+nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *slot)
 {
-       BUG_ON(slotid >= NFS4_MAX_SLOT_TABLE);
+       u32 slotid = slot->slot_nr;
+
        /* clear used bit in bitmap */
        __clear_bit(slotid, tbl->used_slots);
 
        /* update highest_used_slotid when it is freed */
        if (slotid == tbl->highest_used_slotid) {
-               slotid = find_last_bit(tbl->used_slots, tbl->max_slots);
-               if (slotid < tbl->max_slots)
-                       tbl->highest_used_slotid = slotid;
+               u32 new_max = find_last_bit(tbl->used_slots, slotid);
+               if (new_max < slotid)
+                       tbl->highest_used_slotid = new_max;
                else
                        tbl->highest_used_slotid = NFS4_NO_SLOT;
        }
@@ -470,25 +469,68 @@ void nfs4_check_drain_bc_complete(struct nfs4_session *ses)
 
 static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
 {
+       struct nfs4_session *session;
        struct nfs4_slot_table *tbl;
 
-       tbl = &res->sr_session->fc_slot_table;
        if (!res->sr_slot) {
                /* just wake up the next guy waiting since
                 * we may have not consumed a slot after all */
                dprintk("%s: No slot\n", __func__);
                return;
        }
+       tbl = res->sr_slot->table;
+       session = tbl->session;
 
        spin_lock(&tbl->slot_tbl_lock);
-       nfs4_free_slot(tbl, res->sr_slot - tbl->slots);
-       nfs4_check_drain_fc_complete(res->sr_session);
+       nfs4_free_slot(tbl, res->sr_slot);
+       nfs4_check_drain_fc_complete(session);
        spin_unlock(&tbl->slot_tbl_lock);
        res->sr_slot = NULL;
 }
 
+/* Update the client's idea of target_highest_slotid */
+static void nfs41_set_target_slotid_locked(struct nfs4_slot_table *tbl,
+               u32 target_highest_slotid)
+{
+       if (tbl->target_highest_slotid == target_highest_slotid)
+               return;
+       tbl->target_highest_slotid = target_highest_slotid;
+       tbl->generation++;
+}
+
+static void nfs41_set_server_slotid_locked(struct nfs4_slot_table *tbl,
+               u32 highest_slotid)
+{
+       unsigned int max_slotid, i;
+
+       if (tbl->server_highest_slotid == highest_slotid)
+               return;
+       if (tbl->highest_used_slotid > highest_slotid)
+               return;
+       max_slotid = min(tbl->max_slots - 1, highest_slotid);
+       /* Reset the seq_nr for deallocated slots */
+       for (i = tbl->server_highest_slotid + 1; i <= max_slotid; i++)
+               tbl->slots[i].seq_nr = 1;
+       tbl->server_highest_slotid = highest_slotid;
+}
+
+static void nfs41_update_target_slotid(struct nfs4_slot_table *tbl,
+               struct nfs4_slot *slot,
+               struct nfs4_sequence_res *res)
+{
+       spin_lock(&tbl->slot_tbl_lock);
+       if (tbl->generation != slot->generation)
+               goto out;
+       nfs41_set_server_slotid_locked(tbl, res->sr_highest_slotid);
+       nfs41_set_target_slotid_locked(tbl, res->sr_target_highest_slotid);
+out:
+       spin_unlock(&tbl->slot_tbl_lock);
+}
+
 static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
 {
+       struct nfs4_session *session;
+       struct nfs4_slot *slot;
        unsigned long timestamp;
        struct nfs_client *clp;
 
@@ -505,31 +547,35 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *
        if (!RPC_WAS_SENT(task))
                goto out;
 
+       slot = res->sr_slot;
+       session = slot->table->session;
+
        /* Check the SEQUENCE operation status */
        switch (res->sr_status) {
        case 0:
                /* Update the slot's sequence and clientid lease timer */
-               ++res->sr_slot->seq_nr;
-               timestamp = res->sr_renewal_time;
-               clp = res->sr_session->clp;
+               ++slot->seq_nr;
+               timestamp = slot->renewal_time;
+               clp = session->clp;
                do_renew_lease(clp, timestamp);
                /* Check sequence flags */
                if (res->sr_status_flags != 0)
                        nfs4_schedule_lease_recovery(clp);
+               nfs41_update_target_slotid(slot->table, slot, res);
                break;
        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.
                 */
-               dprintk("%s: slot=%td seq=%d: Operation in progress\n",
+               dprintk("%s: slot=%u seq=%u: Operation in progress\n",
                        __func__,
-                       res->sr_slot - res->sr_session->fc_slot_table.slots,
-                       res->sr_slot->seq_nr);
+                       slot->slot_nr,
+                       slot->seq_nr);
                goto out_retry;
        default:
                /* Just update the slot sequence no. */
-               ++res->sr_slot->seq_nr;
+               ++slot->seq_nr;
        }
 out:
        /* The session may be reset by one of the error handlers. */
@@ -546,26 +592,24 @@ out_retry:
 static int nfs4_sequence_done(struct rpc_task *task,
                               struct nfs4_sequence_res *res)
 {
-       if (res->sr_session == NULL)
+       if (res->sr_slot == NULL)
                return 1;
        return nfs41_sequence_done(task, res);
 }
 
 /*
- * nfs4_find_slot - efficiently look for a free slot
+ * nfs4_alloc_slot - efficiently look for a free slot
  *
- * nfs4_find_slot looks for an unset bit in the used_slots bitmap.
+ * nfs4_alloc_slot looks for an unset bit in the used_slots bitmap.
  * If found, we mark the slot as used, update the highest_used_slotid,
  * and respectively set up the sequence operation args.
- * The slot number is returned if found, or NFS4_NO_SLOT otherwise.
  *
  * Note: must be called with under the slot_tbl_lock.
  */
-static u32
-nfs4_find_slot(struct nfs4_slot_table *tbl)
+static struct nfs4_slot *nfs4_alloc_slot(struct nfs4_slot_table *tbl)
 {
+       struct nfs4_slot *ret = NULL;
        u32 slotid;
-       u32 ret_id = NFS4_NO_SLOT;
 
        dprintk("--> %s used_slots=%04lx highest_used=%u max_slots=%u\n",
                __func__, tbl->used_slots[0], tbl->highest_used_slotid,
@@ -577,21 +621,24 @@ nfs4_find_slot(struct nfs4_slot_table *tbl)
        if (slotid > tbl->highest_used_slotid ||
                        tbl->highest_used_slotid == NFS4_NO_SLOT)
                tbl->highest_used_slotid = slotid;
-       ret_id = slotid;
+       ret = &tbl->slots[slotid];
+       ret->renewal_time = jiffies;
+       ret->generation = tbl->generation;
+
 out:
        dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n",
-               __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id);
-       return ret_id;
+               __func__, tbl->used_slots[0], tbl->highest_used_slotid,
+               ret ? ret->slot_nr : -1);
+       return ret;
 }
 
 static void nfs41_init_sequence(struct nfs4_sequence_args *args,
                struct nfs4_sequence_res *res, int cache_reply)
 {
-       args->sa_session = NULL;
+       args->sa_slot = NULL;
        args->sa_cache_this = 0;
        if (cache_reply)
                args->sa_cache_this = 1;
-       res->sr_session = NULL;
        res->sr_slot = NULL;
 }
 
@@ -602,7 +649,6 @@ int nfs41_setup_sequence(struct nfs4_session *session,
 {
        struct nfs4_slot *slot;
        struct nfs4_slot_table *tbl;
-       u32 slotid;
 
        dprintk("--> %s\n", __func__);
        /* slot already allocated? */
@@ -629,8 +675,8 @@ int nfs41_setup_sequence(struct nfs4_session *session,
                return -EAGAIN;
        }
 
-       slotid = nfs4_find_slot(tbl);
-       if (slotid == NFS4_NO_SLOT) {
+       slot = nfs4_alloc_slot(tbl);
+       if (slot == NULL) {
                rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
                spin_unlock(&tbl->slot_tbl_lock);
                dprintk("<-- %s: no free slots\n", __func__);
@@ -639,15 +685,13 @@ 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;
 
-       dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr);
+       args->sa_slot = slot;
+
+       dprintk("<-- %s slotid=%d seqid=%d\n", __func__,
+                       slot->slot_nr, slot->seq_nr);
 
-       res->sr_session = session;
        res->sr_slot = slot;
-       res->sr_renewal_time = jiffies;
        res->sr_status_flags = 0;
        /*
         * sr_status is only set in decode_sequence, and so will remain
@@ -669,9 +713,9 @@ int nfs4_setup_sequence(const struct nfs_server *server,
        if (session == NULL)
                goto out;
 
-       dprintk("--> %s clp %p session %p sr_slot %td\n",
+       dprintk("--> %s clp %p session %p sr_slot %d\n",
                __func__, session->clp, session, res->sr_slot ?
-                       res->sr_slot - session->fc_slot_table.slots : -1);
+                       res->sr_slot->slot_nr : -1);
 
        ret = nfs41_setup_sequence(session, args, res, task);
 out:
@@ -1572,9 +1616,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
        data->timestamp = jiffies;
        if (nfs4_setup_sequence(data->o_arg.server,
                                &data->o_arg.seq_args,
-                               &data->o_res.seq_res, task))
-               return;
-       rpc_call_start(task);
+                               &data->o_res.seq_res,
+                               task) != 0)
+               nfs_release_seqid(data->o_arg.seqid);
+       else
+               rpc_call_start(task);
        return;
 unlock_no_action:
        rcu_read_unlock();
@@ -1748,7 +1794,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred,
 
        /* even though OPEN succeeded, access is denied. Close the file */
        nfs4_close_state(state, fmode);
-       return -NFS4ERR_ACCESS;
+       return -EACCES;
 }
 
 /*
@@ -2196,7 +2242,7 @@ static void nfs4_free_closedata(void *data)
        nfs4_put_open_state(calldata->state);
        nfs_free_seqid(calldata->arg.seqid);
        nfs4_put_state_owner(sp);
-       nfs_sb_deactive(sb);
+       nfs_sb_deactive_async(sb);
        kfree(calldata);
 }
 
@@ -2296,9 +2342,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        if (nfs4_setup_sequence(NFS_SERVER(inode),
                                &calldata->arg.seq_args,
                                &calldata->res.seq_res,
-                               task))
-               goto out;
-       rpc_call_start(task);
+                               task) != 0)
+               nfs_release_seqid(calldata->arg.seqid);
+       else
+               rpc_call_start(task);
 out:
        dprintk("%s: done!\n", __func__);
 }
@@ -2531,7 +2578,8 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
        rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
 
        len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array));
-       BUG_ON(len < 0);
+       if (len < 0)
+               return len;
 
        for (i = 0; i < len; i++) {
                /* AUTH_UNIX is the default flavor if none was specified,
@@ -3360,9 +3408,6 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
        int mode = sattr->ia_mode;
        int status = -ENOMEM;
 
-       BUG_ON(!(sattr->ia_valid & ATTR_MODE));
-       BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
-
        data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK);
        if (data == NULL)
                goto out;
@@ -3378,10 +3423,13 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                data->arg.ftype = NF4CHR;
                data->arg.u.device.specdata1 = MAJOR(rdev);
                data->arg.u.device.specdata2 = MINOR(rdev);
+       } else if (!S_ISSOCK(mode)) {
+               status = -EINVAL;
+               goto out_free;
        }
        
        status = nfs4_do_create(dir, dentry, data);
-
+out_free:
        nfs4_free_createdata(data);
 out:
        return status;
@@ -4529,6 +4577,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
                                rpc_restart_call_prepare(task);
        }
+       nfs_release_seqid(calldata->arg.seqid);
 }
 
 static void nfs4_locku_prepare(struct rpc_task *task, void *data)
@@ -4545,9 +4594,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
        calldata->timestamp = jiffies;
        if (nfs4_setup_sequence(calldata->server,
                                &calldata->arg.seq_args,
-                               &calldata->res.seq_res, task))
-               return;
-       rpc_call_start(task);
+                               &calldata->res.seq_res,
+                               task) != 0)
+               nfs_release_seqid(calldata->arg.seqid);
+       else
+               rpc_call_start(task);
 }
 
 static const struct rpc_call_ops nfs4_locku_ops = {
@@ -4692,7 +4743,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
        /* Do we need to do an open_to_lock_owner? */
        if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
                if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0)
-                       return;
+                       goto out_release_lock_seqid;
                data->arg.open_stateid = &state->stateid;
                data->arg.new_lock_owner = 1;
                data->res.open_seqid = data->arg.open_seqid;
@@ -4701,10 +4752,15 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
        data->timestamp = jiffies;
        if (nfs4_setup_sequence(data->server,
                                &data->arg.seq_args,
-                               &data->res.seq_res, task))
+                               &data->res.seq_res,
+                               task) == 0) {
+               rpc_call_start(task);
                return;
-       rpc_call_start(task);
-       dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
+       }
+       nfs_release_seqid(data->arg.open_seqid);
+out_release_lock_seqid:
+       nfs_release_seqid(data->arg.lock_seqid);
+       dprintk("%s: done!, ret = %d\n", __func__, task->tk_status);
 }
 
 static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
@@ -5347,7 +5403,6 @@ int nfs4_proc_bind_conn_to_session(struct nfs_client *clp, struct rpc_cred *cred
        };
 
        dprintk("--> %s\n", __func__);
-       BUG_ON(clp == NULL);
 
        res.session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
        if (unlikely(res.session == NULL)) {
@@ -5571,8 +5626,8 @@ static void nfs4_get_lease_time_prepare(struct rpc_task *task,
                                   &data->args->la_seq_args,
                                   &data->res->lr_seq_res, task);
 
-       BUG_ON(ret == -EAGAIN);
-       rpc_call_start(task);
+       if (ret != -EAGAIN)
+               rpc_call_start(task);
        dprintk("<-- %s\n", __func__);
 }
 
@@ -5648,9 +5703,20 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
        return status;
 }
 
-static struct nfs4_slot *nfs4_alloc_slots(u32 max_slots, gfp_t gfp_flags)
+struct nfs4_slot *nfs4_alloc_slots(struct nfs4_slot_table *table,
+               u32 max_slots, gfp_t gfp_flags)
 {
-       return kcalloc(max_slots, sizeof(struct nfs4_slot), gfp_flags);
+       struct nfs4_slot *tbl;
+       u32 i;
+
+       tbl = kmalloc_array(max_slots, sizeof(*tbl), gfp_flags);
+       if (tbl != NULL) {
+               for (i = 0; i < max_slots; i++) {
+                       tbl[i].table = table;
+                       tbl[i].slot_nr = i;
+               }
+       }
+       return tbl;
 }
 
 static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl,
@@ -5667,7 +5733,9 @@ static void nfs4_add_and_init_slots(struct nfs4_slot_table *tbl,
                tbl->slots = new;
                tbl->max_slots = max_slots;
        }
-       tbl->highest_used_slotid = -1;  /* no slot is currently used */
+       tbl->highest_used_slotid = NFS4_NO_SLOT;
+       tbl->target_highest_slotid = max_slots - 1;
+       tbl->server_highest_slotid = max_slots - 1;
        for (i = 0; i < tbl->max_slots; i++)
                tbl->slots[i].seq_nr = ivalue;
        spin_unlock(&tbl->slot_tbl_lock);
@@ -5688,7 +5756,7 @@ static int nfs4_realloc_slot_table(struct nfs4_slot_table *tbl, u32 max_reqs,
 
        /* Does the newly negotiated max_reqs match the existing slot table? */
        if (max_reqs != tbl->max_slots) {
-               new = nfs4_alloc_slots(max_reqs, GFP_NOFS);
+               new = nfs4_alloc_slots(tbl, max_reqs, GFP_NOFS);
                if (!new)
                        goto out;
        }
@@ -5727,11 +5795,13 @@ static int nfs4_setup_session_slot_tables(struct nfs4_session *ses)
        dprintk("--> %s\n", __func__);
        /* Fore channel */
        tbl = &ses->fc_slot_table;
+       tbl->session = ses;
        status = nfs4_realloc_slot_table(tbl, ses->fc_attrs.max_reqs, 1);
        if (status) /* -ENOMEM */
                return status;
        /* Back channel */
        tbl = &ses->bc_slot_table;
+       tbl->session = ses;
        status = nfs4_realloc_slot_table(tbl, ses->bc_attrs.max_reqs, 0);
        if (status && tbl->slots == NULL)
                /* Fore and back channel share a connection so get
@@ -5799,8 +5869,8 @@ void nfs4_destroy_session(struct nfs4_session *session)
 static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
 {
        struct nfs4_session *session = args->client->cl_session;
-       unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz,
-                    mxresp_sz = session->fc_attrs.max_resp_sz;
+       unsigned int mxrqst_sz = session->fc_target_max_rqst_sz,
+                    mxresp_sz = session->fc_target_max_resp_sz;
 
        if (mxrqst_sz == 0)
                mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
@@ -5909,10 +5979,9 @@ static int _nfs4_proc_create_session(struct nfs_client *clp,
 
        status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
 
-       if (!status)
+       if (!status) {
                /* Verify the session's negotiated channel_attrs values */
                status = nfs4_verify_channel_attrs(&args, session);
-       if (!status) {
                /* Increment the clientid slot sequence id */
                clp->cl_seqid++;
        }
@@ -6007,27 +6076,44 @@ int nfs4_init_session(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_session *session;
-       unsigned int rsize, wsize;
+       unsigned int target_max_rqst_sz = NFS_MAX_FILE_IO_SIZE;
+       unsigned int target_max_resp_sz = NFS_MAX_FILE_IO_SIZE;
 
        if (!nfs4_has_session(clp))
                return 0;
 
+       if (server->rsize != 0)
+               target_max_resp_sz = server->rsize;
+       target_max_resp_sz += nfs41_maxread_overhead;
+
+       if (server->wsize != 0)
+               target_max_rqst_sz = server->wsize;
+       target_max_rqst_sz += nfs41_maxwrite_overhead;
+
        session = clp->cl_session;
        spin_lock(&clp->cl_lock);
        if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
-
-               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->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
-               session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
+               /* Initialise targets and channel attributes */
+               session->fc_target_max_rqst_sz = target_max_rqst_sz;
+               session->fc_attrs.max_rqst_sz = target_max_rqst_sz;
+               session->fc_target_max_resp_sz = target_max_resp_sz;
+               session->fc_attrs.max_resp_sz = target_max_resp_sz;
+       } else {
+               /* Just adjust the targets */
+               if (target_max_rqst_sz > session->fc_target_max_rqst_sz) {
+                       session->fc_target_max_rqst_sz = target_max_rqst_sz;
+                       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+               }
+               if (target_max_resp_sz > session->fc_target_max_resp_sz) {
+                       session->fc_target_max_resp_sz = target_max_resp_sz;
+                       set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state);
+               }
        }
        spin_unlock(&clp->cl_lock);
 
+       if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state))
+               nfs4_schedule_lease_recovery(clp);
+
        return nfs41_check_session_ready(clp);
 }
 
@@ -6128,13 +6214,26 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
        rpc_call_start(task);
 }
 
+static void nfs41_sequence_prepare_privileged(struct rpc_task *task, void *data)
+{
+       rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
+       nfs41_sequence_prepare(task, data);
+}
+
 static const struct rpc_call_ops nfs41_sequence_ops = {
        .rpc_call_done = nfs41_sequence_call_done,
        .rpc_call_prepare = nfs41_sequence_prepare,
        .rpc_release = nfs41_sequence_release,
 };
 
-static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
+static const struct rpc_call_ops nfs41_sequence_privileged_ops = {
+       .rpc_call_done = nfs41_sequence_call_done,
+       .rpc_call_prepare = nfs41_sequence_prepare_privileged,
+       .rpc_release = nfs41_sequence_release,
+};
+
+static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred,
+                                            const struct rpc_call_ops *seq_ops)
 {
        struct nfs4_sequence_data *calldata;
        struct rpc_message msg = {
@@ -6144,7 +6243,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clp->cl_rpcclient,
                .rpc_message = &msg,
-               .callback_ops = &nfs41_sequence_ops,
+               .callback_ops = seq_ops,
                .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
        };
 
@@ -6171,7 +6270,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr
 
        if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
                return 0;
-       task = _nfs41_proc_sequence(clp, cred);
+       task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_ops);
        if (IS_ERR(task))
                ret = PTR_ERR(task);
        else
@@ -6185,7 +6284,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
        struct rpc_task *task;
        int ret;
 
-       task = _nfs41_proc_sequence(clp, cred);
+       task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_privileged_ops);
        if (IS_ERR(task)) {
                ret = PTR_ERR(task);
                goto out;