#include "nfs4_fs.h"
#include "delegation.h"
-#include "iostat.h"
#define NFSDBG_FACILITY NFSDBG_PROC
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 nfs_client *clp);
+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[];
/* Prevent leaks of NFSv4 errors into userland */
int nfs4_map_errors(int err)
0
};
-const u32 nfs4_fs_locations_bitmap[2] = {
- FATTR4_WORD0_TYPE
- | FATTR4_WORD0_CHANGE
- | FATTR4_WORD0_SIZE
- | FATTR4_WORD0_FSID
- | FATTR4_WORD0_FILEID
- | FATTR4_WORD0_FS_LOCATIONS,
- FATTR4_WORD1_MODE
- | FATTR4_WORD1_NUMLINKS
- | FATTR4_WORD1_OWNER
- | FATTR4_WORD1_OWNER_GROUP
- | FATTR4_WORD1_RAWDEV
- | FATTR4_WORD1_SPACE_USED
- | FATTR4_WORD1_TIME_ACCESS
- | FATTR4_WORD1_TIME_METADATA
- | FATTR4_WORD1_TIME_MODIFY
- | FATTR4_WORD1_MOUNTED_ON_FILEID
-};
-
static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry,
struct nfs4_readdir_arg *readdir)
{
static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
spin_lock(&clp->cl_lock);
if (time_before(clp->cl_last_renewal,timestamp))
clp->cl_last_renewal = timestamp;
spin_unlock(&clp->cl_lock);
}
-static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
+static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo)
{
- struct nfs_inode *nfsi = NFS_I(dir);
+ struct nfs_inode *nfsi = NFS_I(inode);
- spin_lock(&dir->i_lock);
- nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
if (cinfo->before == nfsi->change_attr && cinfo->atomic)
nfsi->change_attr = cinfo->after;
- spin_unlock(&dir->i_lock);
+ spin_unlock(&inode->i_lock);
}
struct nfs4_opendata {
atomic_inc(&sp->so_count);
p->o_arg.fh = NFS_FH(dir);
p->o_arg.open_flags = flags,
- p->o_arg.clientid = server->nfs_client->cl_clientid;
+ p->o_arg.clientid = server->nfs4_state->cl_clientid;
p->o_arg.id = sp->so_id;
p->o_arg.name = &dentry->d_name;
p->o_arg.server = server;
if (!(data->f_attr.valid & NFS_ATTR_FATTR))
goto out;
inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
- if (IS_ERR(inode))
+ if (inode == NULL)
goto out;
state = nfs4_get_open_state(inode, data->owner);
if (state == NULL)
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
/* Don't recall a delegation if it was lost */
- nfs4_schedule_state_recovery(server->nfs_client);
+ nfs4_schedule_state_recovery(server->nfs4_state);
return err;
}
err = nfs4_handle_exception(server, err, &exception);
int status;
atomic_inc(&data->count);
- /*
- * If rpc_run_task() ends up calling ->rpc_release(), we
- * want to ensure that it takes the 'error' code path.
- */
- data->rpc_status = -ENOMEM;
task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_confirm_ops, data);
- if (IS_ERR(task))
+ if (IS_ERR(task)) {
+ nfs4_opendata_free(data);
return PTR_ERR(task);
+ }
status = nfs4_wait_for_completion_rpc_task(task);
if (status != 0) {
data->cancelled = 1;
int status;
atomic_inc(&data->count);
- /*
- * If rpc_run_task() ends up calling ->rpc_release(), we
- * want to ensure that it takes the 'error' code path.
- */
- data->rpc_status = -ENOMEM;
task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_ops, data);
- if (IS_ERR(task))
+ if (IS_ERR(task)) {
+ nfs4_opendata_free(data);
return PTR_ERR(task);
+ }
status = nfs4_wait_for_completion_rpc_task(task);
if (status != 0) {
data->cancelled = 1;
}
nfs_confirm_seqid(&data->owner->so_seqid, 0);
if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
- return server->nfs_client->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
+ return server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr);
return 0;
}
int nfs4_recover_expired_lease(struct nfs_server *server)
{
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
nfs4_schedule_state_recovery(clp);
{
struct nfs_delegation *delegation;
struct nfs_server *server = NFS_SERVER(inode);
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state_owner *sp = NULL;
struct nfs4_state *state = NULL;
static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
- struct nfs4_state *res = ERR_PTR(-EIO);
+ struct nfs4_state *res;
int err;
do {
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
struct nfs_server *server = NFS_SERVER(dir);
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
struct nfs4_opendata *opendata;
int status;
status = -ENOMEM;
opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr);
if (opendata == NULL)
- goto err_release_rwsem;
+ goto err_put_state_owner;
status = _nfs4_proc_open(opendata);
if (status != 0)
return 0;
err_opendata_free:
nfs4_opendata_free(opendata);
-err_release_rwsem:
- up_read(&clp->cl_sem);
err_put_state_owner:
nfs4_put_state_owner(sp);
out_err:
+ /* Note: clp->cl_sem must be released before nfs4_put_open_state()! */
+ up_read(&clp->cl_sem);
*res = NULL;
return status;
}
return res;
}
-static int _nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
- struct iattr *sattr, struct nfs4_state *state)
+static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
+ struct nfs_fh *fhandle, struct iattr *sattr,
+ struct nfs4_state *state)
{
- struct nfs_server *server = NFS_SERVER(inode);
struct nfs_setattrargs arg = {
- .fh = NFS_FH(inode),
+ .fh = fhandle,
.iap = sattr,
.server = server,
.bitmask = server->attr_bitmask,
nfs_fattr_init(fattr);
- if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) {
- /* Use that stateid */
- } else if (state != NULL) {
+ if (state != NULL) {
msg.rpc_cred = state->owner->so_cred;
nfs4_copy_stateid(&arg.stateid, state, current->files);
} else
return status;
}
-static int nfs4_do_setattr(struct inode *inode, struct nfs_fattr *fattr,
- struct iattr *sattr, struct nfs4_state *state)
+static int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
+ struct nfs_fh *fhandle, struct iattr *sattr,
+ struct nfs4_state *state)
{
- struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
- _nfs4_do_setattr(inode, fattr, sattr, state),
+ _nfs4_do_setattr(server, fattr, fhandle, sattr,
+ state),
&exception);
} while (exception.retry);
return err;
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(server->nfs_client);
+ nfs4_schedule_state_recovery(server->nfs4_state);
break;
default:
if (nfs4_async_handle_error(task, server) == -EAGAIN) {
return status;
}
-static int nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
+static void nfs4_intent_set_file(struct nameidata *nd, struct dentry *dentry, struct nfs4_state *state)
{
struct file *filp;
struct nfs_open_context *ctx;
ctx = (struct nfs_open_context *)filp->private_data;
ctx->state = state;
- return 0;
- }
- nfs4_close_state(state, nd->intent.open.flags);
- return PTR_ERR(filp);
+ } else
+ nfs4_close_state(state, nd->intent.open.flags);
}
struct dentry *
BUG_ON(nd->intent.open.flags & O_CREAT);
}
- cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
if (IS_ERR(cred))
return (struct dentry *)cred;
state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
struct rpc_cred *cred;
struct nfs4_state *state;
- cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
if (IS_ERR(cred))
return PTR_ERR(cred);
state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
return status;
}
-int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
+static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
{
struct nfs4_exception exception = { };
int err;
return err;
}
-/*
- * get the file handle for the "/" directory on the server
- */
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fsinfo *info)
+ struct nfs_fsinfo *info)
{
+ struct nfs_fattr * fattr = info->fattr;
+ unsigned char * p;
+ struct qstr q;
+ struct nfs4_lookup_arg args = {
+ .dir_fh = fhandle,
+ .name = &q,
+ .bitmask = nfs4_fattr_bitmap,
+ };
+ struct nfs4_lookup_res res = {
+ .server = server,
+ .fattr = fattr,
+ .fh = fhandle,
+ };
+ struct rpc_message msg = {
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
+ .rpc_argp = &args,
+ .rpc_resp = &res,
+ };
int status;
+ /*
+ * Now we do a separate LOOKUP for each component of the mount path.
+ * The LOOKUPs are done separately so that we can conveniently
+ * catch an ERR_WRONGSEC if it occurs along the way...
+ */
status = nfs4_lookup_root(server, fhandle, info);
+ if (status)
+ goto out;
+
+ p = server->mnt_path;
+ for (;;) {
+ struct nfs4_exception exception = { };
+
+ while (*p == '/')
+ p++;
+ if (!*p)
+ break;
+ q.name = p;
+ while (*p && (*p != '/'))
+ p++;
+ q.len = p - q.name;
+
+ do {
+ nfs_fattr_init(fattr);
+ status = nfs4_handle_exception(server,
+ rpc_call_sync(server->client, &msg, 0),
+ &exception);
+ } while (exception.retry);
+ if (status == 0)
+ continue;
+ if (status == -ENOENT) {
+ printk(KERN_NOTICE "NFS: mount path %s does not exist!\n", server->mnt_path);
+ printk(KERN_NOTICE "NFS: suggestion: try mounting '/' instead.\n");
+ }
+ break;
+ }
if (status == 0)
status = nfs4_server_capabilities(server, fhandle);
if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info);
- return nfs4_map_errors(status);
-}
-
-/*
- * Get locations and (maybe) other attributes of a referral.
- * Note that we'll actually follow the referral later when
- * we detect fsid mismatch in inode revalidation
- */
-static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle)
-{
- int status = -ENOMEM;
- struct page *page = NULL;
- struct nfs4_fs_locations *locations = NULL;
- struct dentry dentry = {};
-
- page = alloc_page(GFP_KERNEL);
- if (page == NULL)
- goto out;
- locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
- if (locations == NULL)
- goto out;
-
- dentry.d_name.name = name->name;
- dentry.d_name.len = name->len;
- status = nfs4_proc_fs_locations(dir, &dentry, locations, page);
- if (status != 0)
- goto out;
- /* Make sure server returned a different fsid for the referral */
- if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
- dprintk("%s: server did not return a different fsid for a referral at %s\n", __FUNCTION__, name->name);
- status = -EIO;
- goto out;
- }
-
- memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
- fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL;
- if (!fattr->mode)
- fattr->mode = S_IFDIR;
- memset(fhandle, 0, sizeof(struct nfs_fh));
out:
- if (page)
- __free_page(page);
- if (locations)
- kfree(locations);
- return status;
+ return nfs4_map_errors(status);
}
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
nfs_fattr_init(fattr);
- cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+ cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
if (IS_ERR(cred))
return PTR_ERR(cred);
if (ctx != NULL)
state = ctx->state;
- status = nfs4_do_setattr(inode, fattr, sattr, state);
+ status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
+ NFS_FH(inode), sattr, state);
if (status == 0)
nfs_setattr_update_inode(inode, sattr);
if (ctx != NULL)
return status;
}
-static int _nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
- struct qstr *name, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
-{
- int status;
- struct nfs4_lookup_arg args = {
- .bitmask = server->attr_bitmask,
- .dir_fh = dirfh,
- .name = name,
- };
- struct nfs4_lookup_res res = {
- .server = server,
- .fattr = fattr,
- .fh = fhandle,
- };
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOOKUP],
- .rpc_argp = &args,
- .rpc_resp = &res,
- };
-
- nfs_fattr_init(fattr);
-
- dprintk("NFS call lookupfh %s\n", name->name);
- status = rpc_call_sync(server->client, &msg, 0);
- dprintk("NFS reply lookupfh: %d\n", status);
- if (status == -NFS4ERR_MOVED)
- status = -EREMOTE;
- return status;
-}
-
-static int nfs4_proc_lookupfh(struct nfs_server *server, struct nfs_fh *dirfh,
- struct qstr *name, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
-{
- struct nfs4_exception exception = { };
- int err;
- do {
- err = nfs4_handle_exception(server,
- _nfs4_proc_lookupfh(server, dirfh, name,
- fhandle, fattr),
- &exception);
- } while (exception.retry);
- return err;
-}
-
static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
dprintk("NFS call lookup %s\n", name->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
- if (status == -NFS4ERR_MOVED)
- status = nfs4_get_referral(dir, name, fattr, fhandle);
dprintk("NFS reply lookup: %d\n", status);
return status;
}
struct rpc_cred *cred;
int status = 0;
- cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0);
+ cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
if (IS_ERR(cred)) {
status = PTR_ERR(cred);
goto out;
d_instantiate(dentry, igrab(state->inode));
if (flags & O_EXCL) {
struct nfs_fattr fattr;
- status = nfs4_do_setattr(state->inode, &fattr, sattr, state);
+ status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
+ NFS_FH(state->inode), sattr, state);
if (status == 0)
nfs_setattr_update_inode(state->inode, sattr);
}
if (status == 0 && nd != NULL && (nd->flags & LOOKUP_OPEN))
- status = nfs4_intent_set_file(nd, dentry, state);
+ nfs4_intent_set_file(nd, dentry, state);
else
nfs4_close_state(state, flags);
out:
if (!status) {
update_changeattr(dir, &res.cinfo);
nfs_post_op_update_inode(dir, res.dir_attr);
- nfs_post_op_update_inode(inode, res.fattr);
+ nfs_refresh_inode(inode, res.fattr);
}
return status;
return err;
}
-static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
+static void nfs4_read_done(struct rpc_task *task, void *calldata)
{
- struct nfs_server *server = NFS_SERVER(data->inode);
+ struct nfs_read_data *data = calldata;
+ struct inode *inode = data->inode;
- if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+ if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
rpc_restart_call(task);
- return -EAGAIN;
+ return;
}
if (task->tk_status > 0)
- renew_lease(server, data->timestamp);
- return 0;
+ renew_lease(NFS_SERVER(inode), data->timestamp);
+ /* Call back common NFS readpage processing */
+ nfs_readpage_result(task, calldata);
}
-static void nfs4_proc_read_setup(struct nfs_read_data *data)
+static const struct rpc_call_ops nfs4_read_ops = {
+ .rpc_call_done = nfs4_read_done,
+ .rpc_release = nfs_readdata_release,
+};
+
+static void
+nfs4_proc_read_setup(struct nfs_read_data *data)
{
+ struct rpc_task *task = &data->task;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ],
.rpc_argp = &data->args,
.rpc_resp = &data->res,
.rpc_cred = data->cred,
};
+ struct inode *inode = data->inode;
+ int flags;
data->timestamp = jiffies;
- rpc_call_setup(&data->task, &msg, 0);
+ /* N.B. Do we need to test? Never called for swapfile inode */
+ flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+
+ /* Finalize the task. */
+ rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_read_ops, data);
+ rpc_call_setup(task, &msg, 0);
}
-static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
+static void nfs4_write_done(struct rpc_task *task, void *calldata)
{
+ struct nfs_write_data *data = calldata;
struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
rpc_restart_call(task);
- return -EAGAIN;
+ return;
}
if (task->tk_status >= 0) {
renew_lease(NFS_SERVER(inode), data->timestamp);
nfs_post_op_update_inode(inode, data->res.fattr);
}
- return 0;
+ /* Call back common NFS writeback processing */
+ nfs_writeback_done(task, calldata);
}
-static void nfs4_proc_write_setup(struct nfs_write_data *data, int how)
+static const struct rpc_call_ops nfs4_write_ops = {
+ .rpc_call_done = nfs4_write_done,
+ .rpc_release = nfs_writedata_release,
+};
+
+static void
+nfs4_proc_write_setup(struct nfs_write_data *data, int how)
{
+ struct rpc_task *task = &data->task;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE],
.rpc_argp = &data->args,
struct inode *inode = data->inode;
struct nfs_server *server = NFS_SERVER(inode);
int stable;
+ int flags;
if (how & FLUSH_STABLE) {
if (!NFS_I(inode)->ncommit)
data->timestamp = jiffies;
+ /* Set the initial flags for the task. */
+ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
+
/* Finalize the task. */
- rpc_call_setup(&data->task, &msg, 0);
+ rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_write_ops, data);
+ rpc_call_setup(task, &msg, 0);
}
-static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+static void nfs4_commit_done(struct rpc_task *task, void *calldata)
{
+ struct nfs_write_data *data = calldata;
struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
rpc_restart_call(task);
- return -EAGAIN;
+ return;
}
if (task->tk_status >= 0)
nfs_post_op_update_inode(inode, data->res.fattr);
- return 0;
+ /* Call back common NFS writeback processing */
+ nfs_commit_done(task, calldata);
}
-static void nfs4_proc_commit_setup(struct nfs_write_data *data, int how)
+static const struct rpc_call_ops nfs4_commit_ops = {
+ .rpc_call_done = nfs4_commit_done,
+ .rpc_release = nfs_commit_release,
+};
+
+static void
+nfs4_proc_commit_setup(struct nfs_write_data *data, int how)
{
+ struct rpc_task *task = &data->task;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT],
.rpc_argp = &data->args,
.rpc_resp = &data->res,
.rpc_cred = data->cred,
};
- struct nfs_server *server = NFS_SERVER(data->inode);
+ struct inode *inode = data->inode;
+ struct nfs_server *server = NFS_SERVER(inode);
+ int flags;
data->args.bitmask = server->attr_bitmask;
data->res.server = server;
- rpc_call_setup(&data->task, &msg, 0);
+ /* Set the initial flags for the task. */
+ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
+
+ /* Finalize the task. */
+ rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_commit_ops, data);
+ rpc_call_setup(task, &msg, 0);
}
/*
*/
static void nfs4_renew_done(struct rpc_task *task, void *data)
{
- struct nfs_client *clp = (struct nfs_client *)task->tk_msg.rpc_argp;
+ struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
unsigned long timestamp = (unsigned long)data;
if (task->tk_status < 0) {
.rpc_call_done = nfs4_renew_done,
};
-int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred)
+int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred)
{
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
&nfs4_renew_ops, (void *)jiffies);
}
-int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
+int nfs4_proc_renew(struct nfs4_client *clp, struct rpc_cred *cred)
{
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
nfs4_set_cached_acl(inode, acl);
}
-static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+static inline ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
struct page *pages[NFS4ACL_MAXPAGES];
struct nfs_getaclargs args = {
return ret;
}
-static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
-{
- struct nfs4_exception exception = { };
- ssize_t ret;
- do {
- ret = __nfs4_get_acl_uncached(inode, buf, buflen);
- if (ret >= 0)
- break;
- ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception);
- } while (exception.retry);
- return ret;
-}
-
static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
{
struct nfs_server *server = NFS_SERVER(inode);
return nfs4_get_acl_uncached(inode, buf, buflen);
}
-static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
{
struct nfs_server *server = NFS_SERVER(inode);
struct page *pages[NFS4ACL_MAXPAGES];
return -EOPNOTSUPP;
nfs_inode_return_delegation(inode);
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
- ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ ret = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
if (ret == 0)
nfs4_write_cached_acl(inode, buf, buflen);
return ret;
}
-static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
-{
- struct nfs4_exception exception = { };
- int err;
- do {
- err = nfs4_handle_exception(NFS_SERVER(inode),
- __nfs4_proc_set_acl(inode, buf, buflen),
- &exception);
- } while (exception.retry);
- return err;
-}
-
static int
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
{
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
if (!clp || task->tk_status >= 0)
return 0;
rpc_wake_up_task(task);
task->tk_status = 0;
return -EAGAIN;
- case -NFS4ERR_DELAY:
- nfs_inc_server_stats((struct nfs_server *) server,
- NFSIOS_DELAY);
case -NFS4ERR_GRACE:
+ case -NFS4ERR_DELAY:
rpc_delay(task, NFS4_POLL_RETRY_MAX);
task->tk_status = 0;
return -EAGAIN;
return 0;
}
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
+static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
{
sigset_t oldset;
int res;
*/
int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
int ret = errorcode;
exception->retry = 0;
return nfs4_map_errors(ret);
}
-int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
+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 = {
for(;;) {
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.sin_addr),
+ clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr),
cred->cr_ops->cr_name,
clp->cl_id_uniquifier);
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
return status;
}
-static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
+int
+nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred)
{
struct nfs_fsinfo fsinfo;
struct rpc_message msg = {
return status;
}
-int nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cred *cred)
-{
- long timeout;
- int err;
- do {
- err = _nfs4_proc_setclientid_confirm(clp, cred);
- switch (err) {
- case 0:
- return err;
- case -NFS4ERR_RESOURCE:
- /* The IBM lawyers misread another document! */
- case -NFS4ERR_DELAY:
- err = nfs4_delay(clp->cl_rpcclient, &timeout);
- }
- } while (err == 0);
- return err;
-}
-
struct nfs4_delegreturndata {
struct nfs4_delegreturnargs args;
struct nfs4_delegreturnres res;
kfree(calldata);
}
-static const struct rpc_call_ops nfs4_delegreturn_ops = {
+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,
data->rpc_status = 0;
task = rpc_run_task(NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs4_delegreturn_ops, data);
- if (IS_ERR(task))
+ 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;
switch (err) {
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(server->nfs_client);
+ nfs4_schedule_state_recovery(server->nfs4_state);
case 0:
return 0;
}
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
- struct nfs_client *clp = server->nfs_client;
+ struct nfs4_client *clp = server->nfs4_state;
struct nfs_lockt_args arg = {
.fh = NFS_FH(inode),
.fl = request,
default:
BUG();
}
+ if (res < 0)
+ printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n",
+ __FUNCTION__);
return res;
}
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(calldata->server->nfs_client);
+ nfs4_schedule_state_recovery(calldata->server->nfs4_state);
break;
default:
if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) {
struct nfs_seqid *seqid)
{
struct nfs4_unlockdata *data;
+ struct rpc_task *task;
data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
if (data == NULL) {
return ERR_PTR(-ENOMEM);
}
- return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
+ /* Unlock _before_ we do the RPC call */
+ do_vfs_lock(fl->fl_file, fl);
+ task = rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
+ if (IS_ERR(task))
+ nfs4_locku_release_calldata(data);
+ return task;
}
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
struct rpc_task *task;
int status = 0;
- status = nfs4_set_lock_state(state, request);
- /* Unlock _before_ we do the RPC call */
- request->fl_flags |= FL_EXISTS;
- if (do_vfs_lock(request->fl_file, request) == -ENOENT)
- goto out;
- if (status != 0)
- goto out;
/* Is this a delegated lock? */
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- goto out;
+ goto out_unlock;
+ /* Is this open_owner holding any locks on the server? */
+ if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
+ goto out_unlock;
+
+ status = nfs4_set_lock_state(state, request);
+ if (status != 0)
+ goto out_unlock;
lsp = request->fl_u.nfs4_fl.owner;
- seqid = nfs_alloc_seqid(&lsp->ls_seqid);
status = -ENOMEM;
+ seqid = nfs_alloc_seqid(&lsp->ls_seqid);
if (seqid == NULL)
- goto out;
+ goto out_unlock;
task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid);
status = PTR_ERR(task);
if (IS_ERR(task))
- goto out;
+ goto out_unlock;
status = nfs4_wait_for_completion_rpc_task(task);
rpc_release_task(task);
-out:
+ return status;
+out_unlock:
+ do_vfs_lock(request->fl_file, request);
return status;
}
if (p->arg.lock_seqid == NULL)
goto out_free;
p->arg.lock_stateid = &lsp->ls_stateid;
- p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
+ p->arg.lock_owner.clientid = server->nfs4_state->cl_clientid;
p->arg.lock_owner.id = lsp->ls_id;
p->lsp = lsp;
atomic_inc(&lsp->ls_count);
data->arg.reclaim = 1;
task = rpc_run_task(NFS_CLIENT(state->inode), RPC_TASK_ASYNC,
&nfs4_lock_ops, data);
- if (IS_ERR(task))
+ if (IS_ERR(task)) {
+ nfs4_lock_release(data);
return PTR_ERR(task);
+ }
ret = nfs4_wait_for_completion_rpc_task(task);
if (ret == 0) {
ret = data->rpc_status;
struct nfs4_exception exception = { };
int err;
+ /* Cache the lock if possible... */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+ return 0;
do {
- /* 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);
if (err != -NFS4ERR_DELAY)
break;
if (err != 0)
return err;
do {
- if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
- return 0;
err = _nfs4_do_setlk(state, F_SETLK, request, 0);
if (err != -NFS4ERR_DELAY)
break;
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
- struct nfs_client *clp = state->owner->so_client;
- unsigned char fl_flags = request->fl_flags;
+ struct nfs4_client *clp = state->owner->so_client;
int status;
/* Is this a delegated open? */
- status = nfs4_set_lock_state(state, request);
- if (status != 0)
- goto out;
- request->fl_flags |= FL_ACCESS;
- status = do_vfs_lock(request->fl_file, request);
- if (status < 0)
- goto out;
- down_read(&clp->cl_sem);
- if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
- struct nfs_inode *nfsi = NFS_I(state->inode);
+ if (NFS_I(state->inode)->delegation_state != 0) {
/* Yes: cache locks! */
- down_read(&nfsi->rwsem);
+ status = do_vfs_lock(request->fl_file, request);
/* ...but avoid races with delegation recall... */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
- request->fl_flags = fl_flags & ~FL_SLEEP;
- status = do_vfs_lock(request->fl_file, request);
- up_read(&nfsi->rwsem);
- goto out_unlock;
- }
- up_read(&nfsi->rwsem);
+ if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags))
+ return status;
}
+ down_read(&clp->cl_sem);
+ status = nfs4_set_lock_state(state, request);
+ if (status != 0)
+ goto out;
status = _nfs4_do_setlk(state, cmd, request, 0);
if (status != 0)
- goto out_unlock;
+ goto out;
/* Note: we always want to sleep here! */
- request->fl_flags = fl_flags | FL_SLEEP;
+ request->fl_flags |= FL_SLEEP;
if (do_vfs_lock(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
-out_unlock:
- up_read(&clp->cl_sem);
out:
- request->fl_flags = fl_flags;
+ up_read(&clp->cl_sem);
return status;
}
{
size_t len = strlen(XATTR_NAME_NFSV4_ACL) + 1;
- if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
- return 0;
if (buf && buflen < len)
return -ERANGE;
if (buf)
return len;
}
-int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry,
- struct nfs4_fs_locations *fs_locations, struct page *page)
-{
- struct nfs_server *server = NFS_SERVER(dir);
- u32 bitmask[2] = {
- [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
- [1] = FATTR4_WORD1_MOUNTED_ON_FILEID,
- };
- struct nfs4_fs_locations_arg args = {
- .dir_fh = NFS_FH(dir),
- .name = &dentry->d_name,
- .page = page,
- .bitmask = bitmask,
- };
- struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
- .rpc_argp = &args,
- .rpc_resp = fs_locations,
- };
- int status;
-
- dprintk("%s: start\n", __FUNCTION__);
- fs_locations->fattr.valid = 0;
- fs_locations->server = server;
- fs_locations->nlocations = 0;
- status = rpc_call_sync(server->client, &msg, 0);
- dprintk("%s: returned status = %d\n", __FUNCTION__, status);
- return status;
-}
-
struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
.recover_open = nfs4_open_reclaim,
.recover_lock = nfs4_lock_reclaim,
.listxattr = nfs4_listxattr,
};
-const struct nfs_rpc_ops nfs_v4_clientops = {
+struct nfs_rpc_ops nfs_v4_clientops = {
.version = 4, /* protocol version */
.dentry_ops = &nfs4_dentry_operations,
.dir_inode_ops = &nfs4_dir_inode_operations,
.getroot = nfs4_proc_get_root,
.getattr = nfs4_proc_getattr,
.setattr = nfs4_proc_setattr,
- .lookupfh = nfs4_proc_lookupfh,
.lookup = nfs4_proc_lookup,
.access = nfs4_proc_access,
.readlink = nfs4_proc_readlink,
.statfs = nfs4_proc_statfs,
.fsinfo = nfs4_proc_fsinfo,
.pathconf = nfs4_proc_pathconf,
- .set_capabilities = nfs4_server_capabilities,
.decode_dirent = nfs4_decode_dirent,
.read_setup = nfs4_proc_read_setup,
- .read_done = nfs4_read_done,
.write_setup = nfs4_proc_write_setup,
- .write_done = nfs4_write_done,
.commit_setup = nfs4_proc_commit_setup,
- .commit_done = nfs4_commit_done,
.file_open = nfs_open,
.file_release = nfs_release,
.lock = nfs4_proc_lock,