]> err.no Git - linux-2.6/blobdiff - fs/nfs/nfs4proc.c
NFS: support large reads and writes on the wire
[linux-2.6] / fs / nfs / nfs4proc.c
index 718fcc7225566a44a18e6a84401b41cd537ee1f5..b3349154994b5459bf797751be7923cc3f0e5334 100644 (file)
@@ -63,6 +63,7 @@ static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinf
 static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
 static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
 static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
+static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp);
 extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
 extern struct rpc_procinfo nfs4_procedures[];
 
@@ -174,8 +175,7 @@ static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
        kunmap_atomic(start, KM_USER0);
 }
 
-static void
-renew_lease(struct nfs_server *server, unsigned long timestamp)
+static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
 {
        struct nfs4_client *clp = server->nfs4_state;
        spin_lock(&clp->cl_lock);
@@ -207,6 +207,7 @@ struct nfs4_opendata {
        struct dentry *dir;
        struct nfs4_state_owner *owner;
        struct iattr attrs;
+       unsigned long timestamp;
        int rpc_status;
        int cancelled;
 };
@@ -548,6 +549,7 @@ static void nfs4_open_confirm_prepare(struct rpc_task *task, void *calldata)
                .rpc_resp = &data->c_res,
                .rpc_cred = data->owner->so_cred,
        };
+       data->timestamp = jiffies;
        rpc_call_setup(task, &msg, 0);
 }
 
@@ -558,9 +560,11 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
        data->rpc_status = task->tk_status;
        if (RPC_ASSASSINATED(task))
                return;
-       if (data->rpc_status == 0)
+       if (data->rpc_status == 0) {
                memcpy(data->o_res.stateid.data, data->c_res.stateid.data,
                                sizeof(data->o_res.stateid.data));
+               renew_lease(data->o_res.server, data->timestamp);
+       }
        nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid);
        nfs_confirm_seqid(&data->owner->so_seqid, data->rpc_status);
 }
@@ -633,6 +637,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
        data->o_arg.clientid = sp->so_client->cl_clientid;
        if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
                msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
+       data->timestamp = jiffies;
        rpc_call_setup(task, &msg, 0);
 }
 
@@ -656,6 +661,7 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
                        default:
                                data->rpc_status = -ENOTDIR;
                }
+               renew_lease(data->o_res.server, data->timestamp);
        }
        nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid);
 }
@@ -760,6 +766,15 @@ out:
        return -EACCES;
 }
 
+int nfs4_recover_expired_lease(struct nfs_server *server)
+{
+       struct nfs4_client *clp = server->nfs4_state;
+
+       if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+               nfs4_schedule_state_recovery(clp);
+       return nfs4_wait_clnt_recover(server->client, clp);
+}
+
 /*
  * OPEN_EXPIRED:
  *     reclaim state on the server after a network partition.
@@ -835,6 +850,14 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
        int open_flags = flags & (FMODE_READ|FMODE_WRITE);
        int err;
 
+       err = -ENOMEM;
+       if (!(sp = nfs4_get_state_owner(server, cred))) {
+               dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
+               return err;
+       }
+       err = nfs4_recover_expired_lease(server);
+       if (err != 0)
+               goto out_put_state_owner;
        /* Protect against reboot recovery - NOTE ORDER! */
        down_read(&clp->cl_sem);
        /* Protect against delegation recall */
@@ -844,10 +867,6 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
        if (delegation == NULL || (delegation->type & open_flags) != open_flags)
                goto out_err;
        err = -ENOMEM;
-       if (!(sp = nfs4_get_state_owner(server, cred))) {
-               dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
-               goto out_err;
-       }
        state = nfs4_get_open_state(inode, sp);
        if (state == NULL)
                goto out_err;
@@ -859,13 +878,13 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
                spin_unlock(&inode->i_lock);
                goto out_ok;
        } else if (state->state != 0)
-               goto out_err;
+               goto out_put_open_state;
 
        lock_kernel();
        err = _nfs4_do_access(inode, cred, open_flags);
        unlock_kernel();
        if (err != 0)
-               goto out_err;
+               goto out_put_open_state;
        set_bit(NFS_DELEGATED_STATE, &state->flags);
        update_open_stateid(state, &delegation->stateid, open_flags);
 out_ok:
@@ -873,17 +892,16 @@ out_ok:
        up_read(&nfsi->rwsem);
        up_read(&clp->cl_sem);
        *res = state;
-       return 0; 
+       return 0;
+out_put_open_state:
+       nfs4_put_open_state(state);
 out_err:
-       if (sp != NULL) {
-               if (state != NULL)
-                       nfs4_put_open_state(state);
-               nfs4_put_state_owner(sp);
-       }
        up_read(&nfsi->rwsem);
        up_read(&clp->cl_sem);
        if (err != -EACCES)
                nfs_inode_return_delegation(inode);
+out_put_state_owner:
+       nfs4_put_state_owner(sp);
        return err;
 }
 
@@ -916,12 +934,16 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, st
        int                     status;
 
        /* Protect against reboot recovery conflicts */
-       down_read(&clp->cl_sem);
        status = -ENOMEM;
        if (!(sp = nfs4_get_state_owner(server, cred))) {
                dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
                goto out_err;
        }
+       status = nfs4_recover_expired_lease(server);
+       if (status != 0)
+               goto err_put_state_owner;
+       down_read(&clp->cl_sem);
+       status = -ENOMEM;
        opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr);
        if (opendata == NULL)
                goto err_put_state_owner;
@@ -1014,6 +1036,7 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
                 .rpc_argp       = &arg,
                 .rpc_resp       = &res,
         };
+       unsigned long timestamp = jiffies;
        int status;
 
        nfs_fattr_init(fattr);
@@ -1025,6 +1048,8 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
                memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
 
        status = rpc_call_sync(server->client, &msg, 0);
+       if (status == 0 && state != NULL)
+               renew_lease(server, timestamp);
        return status;
 }
 
@@ -1049,6 +1074,7 @@ struct nfs4_closedata {
        struct nfs_closeargs arg;
        struct nfs_closeres res;
        struct nfs_fattr fattr;
+       unsigned long timestamp;
 };
 
 static void nfs4_free_closedata(void *data)
@@ -1078,6 +1104,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                case 0:
                        memcpy(&state->stateid, &calldata->res.stateid,
                                        sizeof(state->stateid));
+                       renew_lease(server, calldata->timestamp);
                        break;
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
@@ -1130,6 +1157,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        if (mode != 0)
                msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
        calldata->arg.open_flags = mode;
+       calldata->timestamp = jiffies;
        rpc_call_setup(task, &msg, 0);
 }
 
@@ -1694,11 +1722,13 @@ static int _nfs4_proc_write(struct nfs_write_data *wdata)
 
        wdata->args.bitmask = server->attr_bitmask;
        wdata->res.server = server;
+       wdata->timestamp = jiffies;
        nfs_fattr_init(fattr);
        status = rpc_call_sync(server->client, &msg, rpcflags);
        dprintk("NFS reply write: %d\n", status);
        if (status < 0)
                return status;
+       renew_lease(server, wdata->timestamp);
        nfs_post_op_update_inode(inode, fattr);
        return wdata->res.count;
 }
@@ -1733,8 +1763,11 @@ static int _nfs4_proc_commit(struct nfs_write_data *cdata)
 
        cdata->args.bitmask = server->attr_bitmask;
        cdata->res.server = server;
+       cdata->timestamp = jiffies;
        nfs_fattr_init(fattr);
        status = rpc_call_sync(server->client, &msg, 0);
+       if (status >= 0)
+               renew_lease(server, cdata->timestamp);
        dprintk("NFS reply commit: %d\n", status);
        if (status >= 0)
                nfs_post_op_update_inode(inode, fattr);
@@ -2485,26 +2518,24 @@ static const struct rpc_call_ops nfs4_renew_ops = {
        .rpc_call_done = nfs4_renew_done,
 };
 
-int
-nfs4_proc_async_renew(struct nfs4_client *clp)
+int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
                .rpc_argp       = clp,
-               .rpc_cred       = clp->cl_cred,
+               .rpc_cred       = cred,
        };
 
        return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
                        &nfs4_renew_ops, (void *)jiffies);
 }
 
-int
-nfs4_proc_renew(struct nfs4_client *clp)
+int nfs4_proc_renew(struct nfs4_client *clp, struct rpc_cred *cred)
 {
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
                .rpc_argp       = clp,
-               .rpc_cred       = clp->cl_cred,
+               .rpc_cred       = cred,
        };
        unsigned long now = jiffies;
        int status;
@@ -2720,7 +2751,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
                case -NFS4ERR_EXPIRED:
                        rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL);
                        nfs4_schedule_state_recovery(clp);
-                       if (test_bit(NFS4CLNT_OK, &clp->cl_state))
+                       if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
                                rpc_wake_up_task(task);
                        task->tk_status = 0;
                        return -EAGAIN;
@@ -2737,25 +2768,25 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
        return 0;
 }
 
+static int nfs4_wait_bit_interruptible(void *word)
+{
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+       schedule();
+       return 0;
+}
+
 static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
 {
-       DEFINE_WAIT(wait);
        sigset_t oldset;
-       int interruptible, res = 0;
+       int res;
 
        might_sleep();
 
        rpc_clnt_sigmask(clnt, &oldset);
-       interruptible = TASK_UNINTERRUPTIBLE;
-       if (clnt->cl_intr)
-               interruptible = TASK_INTERRUPTIBLE;
-       prepare_to_wait(&clp->cl_waitq, &wait, interruptible);
-       nfs4_schedule_state_recovery(clp);
-       if (clnt->cl_intr && signalled())
-               res = -ERESTARTSYS;
-       else if (!test_bit(NFS4CLNT_OK, &clp->cl_state))
-               schedule();
-       finish_wait(&clp->cl_waitq, &wait);
+       res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
+                       nfs4_wait_bit_interruptible,
+                       TASK_INTERRUPTIBLE);
        rpc_clnt_sigunmask(clnt, &oldset);
        return res;
 }
@@ -2798,6 +2829,7 @@ int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
+                       nfs4_schedule_state_recovery(clp);
                        ret = nfs4_wait_clnt_recover(server->client, clp);
                        if (ret == 0)
                                exception->retry = 1;
@@ -2814,7 +2846,7 @@ int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct
        return nfs4_map_errors(ret);
 }
 
-int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port)
+int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
 {
        nfs4_verifier sc_verifier;
        struct nfs4_setclientid setclientid = {
@@ -2825,7 +2857,7 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID],
                .rpc_argp = &setclientid,
                .rpc_resp = clp,
-               .rpc_cred = clp->cl_cred,
+               .rpc_cred = cred,
        };
        u32 *p;
        int loop = 0;
@@ -2839,7 +2871,7 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
                setclientid.sc_name_len = scnprintf(setclientid.sc_name,
                                sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u",
                                clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr),
-                               clp->cl_cred->cr_ops->cr_name,
+                               cred->cr_ops->cr_name,
                                clp->cl_id_uniquifier);
                setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
                                sizeof(setclientid.sc_netid), "tcp");
@@ -2862,14 +2894,14 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
 }
 
 int
-nfs4_proc_setclientid_confirm(struct nfs4_client *clp)
+nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred)
 {
        struct nfs_fsinfo fsinfo;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
                .rpc_argp = clp,
                .rpc_resp = &fsinfo,
-               .rpc_cred = clp->cl_cred,
+               .rpc_cred = cred,
        };
        unsigned long now;
        int status;
@@ -2880,24 +2912,83 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp)
                spin_lock(&clp->cl_lock);
                clp->cl_lease_time = fsinfo.lease_time * HZ;
                clp->cl_last_renewal = now;
+               clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
                spin_unlock(&clp->cl_lock);
        }
        return status;
 }
 
-static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
+struct nfs4_delegreturndata {
+       struct nfs4_delegreturnargs args;
+       struct nfs_fh fh;
+       nfs4_stateid stateid;
+       struct rpc_cred *cred;
+       unsigned long timestamp;
+       const struct nfs_server *server;
+       int rpc_status;
+};
+
+static void nfs4_delegreturn_prepare(struct rpc_task *task, void *calldata)
 {
-       struct nfs4_delegreturnargs args = {
-               .fhandle = NFS_FH(inode),
-               .stateid = stateid,
-       };
+       struct nfs4_delegreturndata *data = calldata;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN],
-               .rpc_argp = &args,
-               .rpc_cred = cred,
+               .rpc_argp = &data->args,
+               .rpc_cred = data->cred,
        };
+       rpc_call_setup(task, &msg, 0);
+}
 
-       return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_delegreturndata *data = calldata;
+       data->rpc_status = task->tk_status;
+       if (data->rpc_status == 0)
+               renew_lease(data->server, data->timestamp);
+}
+
+static void nfs4_delegreturn_release(void *calldata)
+{
+       struct nfs4_delegreturndata *data = calldata;
+
+       put_rpccred(data->cred);
+       kfree(calldata);
+}
+
+const static struct rpc_call_ops nfs4_delegreturn_ops = {
+       .rpc_call_prepare = nfs4_delegreturn_prepare,
+       .rpc_call_done = nfs4_delegreturn_done,
+       .rpc_release = nfs4_delegreturn_release,
+};
+
+static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
+{
+       struct nfs4_delegreturndata *data;
+       struct rpc_task *task;
+       int status;
+
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+       data->args.fhandle = &data->fh;
+       data->args.stateid = &data->stateid;
+       nfs_copy_fh(&data->fh, NFS_FH(inode));
+       memcpy(&data->stateid, stateid, sizeof(data->stateid));
+       data->cred = get_rpccred(cred);
+       data->timestamp = jiffies;
+       data->server = NFS_SERVER(inode);
+       data->rpc_status = 0;
+
+       task = rpc_run_task(NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs4_delegreturn_ops, data);
+       if (IS_ERR(task)) {
+               nfs4_delegreturn_release(data);
+               return PTR_ERR(task);
+       }
+       status = nfs4_wait_for_completion_rpc_task(task);
+       if (status == 0)
+               status = data->rpc_status;
+       rpc_release_task(task);
+       return status;
 }
 
 int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
@@ -3015,6 +3106,7 @@ struct nfs4_unlockdata {
        struct nfs_open_context *ctx;
        struct file_lock fl;
        const struct nfs_server *server;
+       unsigned long timestamp;
 };
 
 static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
@@ -3062,6 +3154,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        memcpy(calldata->lsp->ls_stateid.data,
                                        calldata->res.stateid.data,
                                        sizeof(calldata->lsp->ls_stateid.data));
+                       renew_lease(calldata->server, calldata->timestamp);
                        break;
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
@@ -3091,6 +3184,7 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
                task->tk_action = NULL;
                return;
        }
+       calldata->timestamp = jiffies;
        rpc_call_setup(task, &msg, 0);
 }
 
@@ -3162,6 +3256,7 @@ struct nfs4_lockdata {
        struct nfs4_lock_state *lsp;
        struct nfs_open_context *ctx;
        struct file_lock fl;
+       unsigned long timestamp;
        int rpc_status;
        int cancelled;
 };
@@ -3221,6 +3316,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
                data->arg.open_stateid = &state->stateid;
                data->arg.new_lock_owner = 1;
        }
+       data->timestamp = jiffies;
        rpc_call_setup(task, &msg, 0);
 out:
        dprintk("%s: done!, ret = %d\n", __FUNCTION__, data->rpc_status);
@@ -3246,6 +3342,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
                memcpy(data->lsp->ls_stateid.data, data->res.stateid.data,
                                        sizeof(data->lsp->ls_stateid.data));
                data->lsp->ls_flags |= NFS_LOCK_INITIALIZED;
+               renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp);
        }
        nfs_increment_lock_seqid(data->rpc_status, data->arg.lock_seqid);
 out: