Commit ca486f40 authored by Trond Myklebust's avatar Trond Myklebust

Merge bk://nfsclient.bkbits.net/linux-2.6

into fys.uio.no:/home/linux/bitkeeper/nfsclient-2.6
parents e8323593 9e84df77
......@@ -783,6 +783,54 @@ void d_instantiate(struct dentry *entry, struct inode * inode)
security_d_instantiate(entry, inode);
}
/**
* d_instantiate_unique - instantiate a non-aliased dentry
* @entry: dentry to instantiate
* @inode: inode to attach to this dentry
*
* Fill in inode information in the entry. On success, it returns NULL.
* If an unhashed alias of "entry" already exists, then we return the
* aliased dentry instead.
*
* Note that in order to avoid conflicts with rename() etc, the caller
* had better be holding the parent directory semaphore.
*/
struct dentry *d_instantiate_unique(struct dentry *entry, struct inode *inode)
{
struct dentry *alias;
int len = entry->d_name.len;
const char *name = entry->d_name.name;
unsigned int hash = entry->d_name.hash;
BUG_ON(!list_empty(&entry->d_alias));
spin_lock(&dcache_lock);
if (!inode)
goto do_negative;
list_for_each_entry(alias, &inode->i_dentry, d_alias) {
struct qstr *qstr = &alias->d_name;
if (qstr->hash != hash)
continue;
if (alias->d_parent != entry->d_parent)
continue;
if (qstr->len != len)
continue;
if (memcmp(qstr->name, name, len))
continue;
dget_locked(alias);
spin_unlock(&dcache_lock);
BUG_ON(!d_unhashed(alias));
return alias;
}
list_add(&entry->d_alias, &inode->i_dentry);
do_negative:
entry->d_inode = inode;
spin_unlock(&dcache_lock);
security_d_instantiate(entry, inode);
return NULL;
}
EXPORT_SYMBOL(d_instantiate_unique);
/**
* d_alloc_root - allocate root dentry
* @root_inode: inode to allocate the root for
......
......@@ -1563,9 +1563,6 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
goto out;
else if (error == LOCK_USE_CLNT)
/* Bypass for NFS with no locking - 2.0.36 compat */
fl = posix_test_lock(filp, &file_lock);
else
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
......@@ -1708,9 +1705,6 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
goto out;
else if (error == LOCK_USE_CLNT)
/* Bypass for NFS with no locking - 2.0.36 compat */
fl = posix_test_lock(filp, &file_lock);
else
fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
......
This diff is collapsed.
This diff is collapsed.
......@@ -295,10 +295,19 @@ nfs_file_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t
static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
{
struct inode *inode = filp->f_mapping->host;
int status;
int status = 0;
lock_kernel();
/* Use local locking if mounted with "-onolock" */
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
else {
struct file_lock *cfl = posix_test_lock(filp, fl);
if (cfl != NULL) {
memcpy(fl, cfl, sizeof(*fl));
fl->fl_type = F_UNLCK;
}
}
unlock_kernel();
return status;
}
......@@ -325,7 +334,11 @@ static int do_unlk(struct file *filp, int cmd, struct file_lock *fl)
* still need to complete the unlock.
*/
lock_kernel();
/* Use local locking if mounted with "-onolock" */
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM))
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
else
status = posix_lock_file_wait(filp, fl);
rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
return status;
}
......@@ -351,6 +364,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
return status;
lock_kernel();
/* Use local locking if mounted with "-onolock" */
if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)) {
status = NFS_PROTO(inode)->lock(filp, cmd, fl);
/* If we were signalled we still need to ensure that
* we clean up any state on the server. We therefore
......@@ -360,6 +375,8 @@ static int do_setlk(struct file *filp, int cmd, struct file_lock *fl)
*/
if (status == -EINTR || status == -ERESTARTSYS)
posix_lock_file(filp, fl);
} else
status = posix_lock_file_wait(filp, fl);
unlock_kernel();
if (status < 0)
return status;
......@@ -396,15 +413,6 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
return -ENOLCK;
if (NFS_PROTO(inode)->version != 4) {
/* Fake OK code if mounted without NLM support */
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
if (IS_GETLK(cmd))
return LOCK_USE_CLNT;
return 0;
}
}
/*
* No BSD flocks over NFS allowed.
* Note: we could try to fake a POSIX lock request here by
......
......@@ -486,13 +486,27 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
if (error < 0)
goto out_err;
buf->f_frsize = server->wtmult;
/*
* Current versions of glibc do not correctly handle the
* case where f_frsize != f_bsize. Eventually we want to
* report the value of wtmult in this field.
*/
buf->f_frsize = sb->s_blocksize;
/*
* On most *nix systems, f_blocks, f_bfree, and f_bavail
* are reported in units of f_frsize. Linux hasn't had
* an f_frsize field in its statfs struct until recently,
* thus historically Linux's sys_statfs reports these
* fields in units of f_bsize.
*/
buf->f_bsize = sb->s_blocksize;
blockbits = sb->s_blocksize_bits;
blockres = (1 << blockbits) - 1;
buf->f_blocks = (res.tbytes + blockres) >> blockbits;
buf->f_bfree = (res.fbytes + blockres) >> blockbits;
buf->f_bavail = (res.abytes + blockres) >> blockbits;
buf->f_files = res.tfiles;
buf->f_ffree = res.afiles;
......@@ -565,9 +579,9 @@ nfs_zap_caches(struct inode *inode)
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
else
nfsi->flags |= NFS_INO_INVALID_ATTR;
nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
}
/*
......@@ -605,7 +619,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
return 0;
if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
if (is_bad_inode(inode))
if (is_bad_inode(inode) || NFS_STALE(inode))
return 0;
return 1;
}
......@@ -766,13 +780,8 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
vmtruncate(inode, attr->ia_size);
}
}
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
if (*cred) {
put_rpccred(*cred);
*cred = NULL;
}
}
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS;
nfs_end_data_update(inode);
unlock_kernel();
return error;
......@@ -949,14 +958,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
lock_kernel();
if (!inode || is_bad_inode(inode))
goto out_nowait;
if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
if (NFS_STALE(inode))
goto out_nowait;
while (NFS_REVALIDATING(inode)) {
status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
if (status < 0)
goto out_nowait;
if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOAC)
if (NFS_ATTRTIMEO(inode) == 0)
continue;
if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
continue;
......@@ -968,14 +977,14 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
/* Protect against RPC races by saving the change attribute */
verifier = nfs_save_change_attribute(inode);
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
if (status) {
if (status != 0) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
if (status == -ESTALE) {
nfs_zap_caches(inode);
if (!S_ISDIR(inode->i_mode))
NFS_FLAGS(inode) |= NFS_INO_STALE;
if (inode != inode->i_sb->s_root->d_inode)
remove_inode_hash(inode);
}
goto out;
}
......@@ -1014,7 +1023,6 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
NFS_FLAGS(inode) &= ~NFS_INO_STALE;
out:
NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
wake_up(&nfsi->nfs_i_wait);
......@@ -1161,7 +1169,7 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
|| inode->i_uid != fattr->uid
|| inode->i_gid != fattr->gid)
nfsi->flags |= NFS_INO_INVALID_ATTR;
nfsi->flags |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
/* Has the link count changed? */
if (inode->i_nlink != fattr->nlink)
......@@ -1270,7 +1278,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
#endif
nfsi->change_attr = fattr->change_attr;
if (!data_unstable)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS;
}
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
......@@ -1278,14 +1286,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) ||
inode->i_uid != fattr->uid ||
inode->i_gid != fattr->gid) {
struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
if (*cred) {
put_rpccred(*cred);
*cred = NULL;
}
invalid |= NFS_INO_INVALID_ATTR;
}
inode->i_gid != fattr->gid)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
......@@ -1335,7 +1337,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
*/
nfs_invalidate_inode(inode);
out_err:
return -EIO;
NFS_FLAGS(inode) |= NFS_INO_STALE;
return -ESTALE;
}
/*
......@@ -1449,8 +1452,6 @@ static void nfs_kill_super(struct super_block *s)
kill_anon_super(s);
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL && !IS_ERR(server->client))
rpc_shutdown_client(server->client);
if (server->client_sys != NULL && !IS_ERR(server->client_sys))
......@@ -1461,8 +1462,6 @@ static void nfs_kill_super(struct super_block *s)
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
if (server->hostname != NULL)
kfree(server->hostname);
kfree(server);
......@@ -1543,9 +1542,6 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
server->wsize = nfs_block_size(data->wsize, NULL);
server->flags = data->flags & NFS_MOUNT_FLAGMASK;
/* NFSv4 doesn't use NLM locking */
server->flags |= NFS_MOUNT_NONLM;
server->acregmin = data->acregmin*HZ;
server->acregmax = data->acregmax*HZ;
server->acdirmin = data->acdirmin*HZ;
......@@ -1790,8 +1786,22 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
static void nfs4_kill_super(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
nfs_return_all_delegations(sb);
nfs_kill_super(sb);
kill_anon_super(sb);
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL && !IS_ERR(server->client))
rpc_shutdown_client(server->client);
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
if (server->hostname != NULL)
kfree(server->hostname);
kfree(server);
}
static struct file_system_type nfs4_fs_type = {
......@@ -1821,9 +1831,13 @@ static struct file_system_type nfs4_fs_type = {
extern int nfs_init_nfspagecache(void);
extern void nfs_destroy_nfspagecache(void);
extern int nfs_init_readpagecache(void);
extern int nfs_destroy_readpagecache(void);
extern void nfs_destroy_readpagecache(void);
extern int nfs_init_writepagecache(void);
extern int nfs_destroy_writepagecache(void);
extern void nfs_destroy_writepagecache(void);
#ifdef CONFIG_NFS_DIRECTIO
extern int nfs_init_directcache(void);
extern void nfs_destroy_directcache(void);
#endif
static kmem_cache_t * nfs_inode_cachep;
......@@ -1904,6 +1918,12 @@ static int __init init_nfs_fs(void)
if (err)
goto out1;
#ifdef CONFIG_NFS_DIRECTIO
err = nfs_init_directcache();
if (err)
goto out0;
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_register(&nfs_rpcstat);
#endif
......@@ -1914,8 +1934,14 @@ static int __init init_nfs_fs(void)
goto out;
return 0;
out:
#ifdef CONFIG_PROC_FS
rpc_proc_unregister("nfs");
#endif
nfs_destroy_writepagecache();
#ifdef CONFIG_NFS_DIRECTIO
out0:
nfs_destroy_directcache();
#endif
out1:
nfs_destroy_readpagecache();
out2:
......@@ -1928,6 +1954,9 @@ static int __init init_nfs_fs(void)
static void __exit exit_nfs_fs(void)
{
#ifdef CONFIG_NFS_DIRECTIO
nfs_destroy_directcache();
#endif
nfs_destroy_writepagecache();
nfs_destroy_readpagecache();
nfs_destroy_inodecache();
......
......@@ -80,10 +80,10 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("%s: call fsinfo\n", __FUNCTION__);
info->fattr->valid = 0;
status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
dprintk("%s: reply fsinfo %d\n", __FUNCTION__, status);
dprintk("%s: reply fsinfo: %d\n", __FUNCTION__, status);
if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
status = rpc_call(server->client_sys, NFS3PROC_GETATTR, fhandle, info->fattr, 0);
dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
}
return status;
}
......@@ -101,7 +101,7 @@ nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
fattr->valid = 0;
status = rpc_call(server->client, NFS3PROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
dprintk("NFS reply getattr: %d\n", status);
return status;
}
......@@ -119,7 +119,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
dprintk("NFS call setattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
dprintk("NFS reply setattr\n");
dprintk("NFS reply setattr: %d\n", status);
return status;
}
......@@ -198,7 +198,7 @@ static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
dprintk("NFS reply access, status = %d\n", status);
dprintk("NFS reply access: %d\n", status);
return status;
}
......@@ -296,7 +296,7 @@ static int nfs3_proc_commit(struct nfs_write_data *cdata)
* For now, we don't implement O_EXCL.
*/
static struct inode *
nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
{
struct nfs_fh fhandle;
......@@ -304,8 +304,8 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
struct nfs_fattr dir_attr;
struct nfs3_createargs arg = {
.fh = NFS_FH(dir),
.name = name->name,
.len = name->len,
.name = dentry->d_name.name,
.len = dentry->d_name.len,
.sattr = sattr,
};
struct nfs3_diropres res = {
......@@ -315,7 +315,7 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
};
int status;
dprintk("NFS call create %s\n", name->name);
dprintk("NFS call create %s\n", dentry->d_name.name);
arg.createmode = NFS3_CREATE_UNCHECKED;
if (flags & O_EXCL) {
arg.createmode = NFS3_CREATE_EXCLUSIVE;
......@@ -353,7 +353,7 @@ nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
if (status != 0)
goto out;
if (fhandle.size == 0 || !(fattr.valid & NFS_ATTR_FATTR)) {
status = nfs3_proc_lookup(dir, name, &fhandle, &fattr);
status = nfs3_proc_lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (status != 0)
goto out;
}
......
......@@ -477,7 +477,7 @@ static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, st
/*
* Returns an nfs4_state + an referenced inode
*/
static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
......@@ -491,7 +491,7 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.open_flags = flags,
.name = name,
.name = &dentry->d_name,
.server = server,
.bitmask = server->attr_bitmask,
.claim = NFS4_OPEN_CLAIM_NULL,
......@@ -581,14 +581,14 @@ static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct
}
struct nfs4_state *nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int status;
do {
status = _nfs4_do_open(dir, name, flags, sattr, cred, &res);
status = _nfs4_do_open(dir, dentry, flags, sattr, cred, &res);
if (status == 0)
break;
/* NOTE: BAD_SEQID means the server and client disagree about the
......@@ -635,6 +635,8 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
fattr->valid = 0;
if (state != NULL)
msg.rpc_cred = state->owner->so_cred;
if (sattr->ia_valid & ATTR_SIZE)
nfs4_copy_stateid(&arg.stateid, state, NULL);
else
......@@ -658,113 +660,100 @@ int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
return err;
}
/*
* It is possible for data to be read/written from a mem-mapped file
* after the sys_close call (which hits the vfs layer as a flush).
* This means that we can't safely call nfsv4 close on a file until
* the inode is cleared. This in turn means that we are not good
* NFSv4 citizens - we do not indicate to the server to update the file's
* share state even when we are done with one of the three share
* stateid's in the inode.
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state)
{
struct nfs4_state_owner *sp = state->owner;
int status = 0;
struct nfs_closeargs arg = {
.fh = NFS_FH(inode),
};
struct nfs4_closedata {
struct inode *inode;
struct nfs4_state *state;
struct nfs_closeargs arg;
struct nfs_closeres res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
.rpc_argp = &arg,
.rpc_resp = &res,
};
};
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
return 0;
memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
/* Serialization for the sequence id */
arg.seqid = sp->so_seqid,
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
static void nfs4_close_done(struct rpc_task *task)
{
struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
struct nfs4_state *state = calldata->state;
struct nfs4_state_owner *sp = state->owner;
struct nfs_server *server = NFS_SERVER(calldata->inode);
/* hmm. we are done with the inode, and in the process of freeing
* the state_owner. we keep this around to process errors
*/
nfs4_increment_seqid(status, sp);
if (!status)
memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
return status;
}
int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_do_close(inode, state);
switch (err) {
nfs4_increment_seqid(task->tk_status, sp);
switch (task->tk_status) {
case 0:
state->state = calldata->arg.open_flags;
memcpy(&state->stateid, &calldata->res.stateid,
sizeof(state->stateid));
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
state->state = calldata->arg.open_flags;
nfs4_schedule_state_recovery(server->nfs4_state);
err = 0;
break;
default:
state->state = 0;
if (nfs4_async_handle_error(task, server) == -EAGAIN) {
rpc_restart_call(task);
return;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
nfs4_put_open_state(state);
up(&sp->so_sema);
nfs4_put_state_owner(sp);
up_read(&server->nfs4_state->cl_sem);
kfree(calldata);
}
static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata)
{
struct nfs4_state_owner *sp = state->owner;
int status = 0;
struct nfs_closeargs arg = {
.fh = NFS_FH(inode),
.seqid = sp->so_seqid,
.open_flags = mode,
};
struct nfs_closeres res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE],
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
.rpc_argp = &calldata->arg,
.rpc_resp = &calldata->res,
.rpc_cred = calldata->state->owner->so_cred,
};
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
return 0;
memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
nfs4_increment_seqid(status, sp);
if (!status)
memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
return status;
if (calldata->arg.open_flags != 0)
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
return rpc_call_async(clnt, &msg, 0, nfs4_close_done, calldata);
}
int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
/*
* It is possible for data to be read/written from a mem-mapped file
* after the sys_close call (which hits the vfs layer as a flush).
* This means that we can't safely call nfsv4 close on a file until
* the inode is cleared. This in turn means that we are not good
* NFSv4 citizens - we do not indicate to the server to update the file's
* share state even when we are done with one of the three share
* stateid's in the inode.
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_do_downgrade(inode, state, mode);
switch (err) {
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(server->nfs4_state);
err = 0;
default:
struct nfs4_closedata *calldata;
int status;
/* Tell caller we're done */
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
state->state = mode;
return 0;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
calldata = (struct nfs4_closedata *)kmalloc(sizeof(*calldata), GFP_KERNEL);
if (calldata == NULL)
return -ENOMEM;
calldata->inode = inode;
calldata->state = state;
calldata->arg.fh = NFS_FH(inode);
/* Serialization for the sequence id */
calldata->arg.seqid = state->owner->so_seqid;
calldata->arg.open_flags = mode;
memcpy(&calldata->arg.stateid, &state->stateid,
sizeof(calldata->arg.stateid));
status = nfs4_close_call(NFS_SERVER(inode)->client, calldata);
/*
* Return -EINPROGRESS on success in order to indicate to the
* caller that an asynchronous RPC call has been launched, and
* that it will release the semaphores on completion.
*/
return (status == 0) ? -EINPROGRESS : status;
}
struct inode *
......@@ -785,7 +774,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
}
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred);
state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
put_rpccred(cred);
if (IS_ERR(state))
return (struct inode *)state;
......@@ -802,7 +791,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
if (IS_ERR(state))
state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
put_rpccred(cred);
if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
return 1;
......@@ -1026,7 +1015,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
FMODE_WRITE, cred);
if (IS_ERR(state))
state = nfs4_do_open(dentry->d_parent->d_inode,
&dentry->d_name, FMODE_WRITE,
dentry, FMODE_WRITE,
NULL, cred);
need_iput = 1;
}
......@@ -1327,7 +1316,7 @@ static int nfs4_proc_commit(struct nfs_write_data *cdata)
*/
static struct inode *
nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
{
struct inode *inode;
......@@ -1335,7 +1324,7 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
struct rpc_cred *cred;
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
state = nfs4_do_open(dir, name, flags, sattr, cred);
state = nfs4_do_open(dir, dentry, flags, sattr, cred);
put_rpccred(cred);
if (!IS_ERR(state)) {
inode = state->inode;
......
......@@ -445,7 +445,7 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
state->owner = owner;
atomic_inc(&owner->so_count);
list_add(&state->inode_states, &nfsi->open_states);
state->inode = inode;
state->inode = igrab(inode);
spin_unlock(&inode->i_lock);
} else {
spin_unlock(&inode->i_lock);
......@@ -471,6 +471,7 @@ void nfs4_put_open_state(struct nfs4_state *state)
list_del(&state->inode_states);
spin_unlock(&inode->i_lock);
list_del(&state->open_states);
iput(inode);
BUG_ON (state->state != 0);
nfs4_free_open_state(state);
nfs4_put_state_owner(owner);
......@@ -486,7 +487,6 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
struct nfs4_state_owner *owner = state->owner;
struct nfs4_client *clp = owner->so_client;
int newstate;
int status = 0;
atomic_inc(&owner->so_count);
down_read(&clp->cl_sem);
......@@ -508,10 +508,8 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
newstate |= FMODE_WRITE;
if (state->state == newstate)
goto out;
if (newstate != 0)
status = nfs4_do_downgrade(inode, state, newstate);
else
status = nfs4_do_close(inode, state);
if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS)
return;
}
out:
nfs4_put_open_state(state);
......
......@@ -63,12 +63,12 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
dprintk("%s: call getattr\n", __FUNCTION__);
fattr->valid = 0;
status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0);
dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
if (status)
return status;
dprintk("%s: call statfs\n", __FUNCTION__);
status = rpc_call(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, 0);
dprintk("%s: reply statfs %d\n", __FUNCTION__, status);
dprintk("%s: reply statfs: %d\n", __FUNCTION__, status);
if (status)
return status;
info->rtmax = NFS_MAXDATA;
......@@ -96,7 +96,7 @@ nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
dprintk("NFS reply getattr: %d\n", status);
return status;
}
......@@ -114,7 +114,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
dprintk("NFS call setattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
dprintk("NFS reply setattr\n");
dprintk("NFS reply setattr: %d\n", status);
return status;
}
......@@ -213,15 +213,15 @@ static int nfs_proc_write(struct nfs_write_data *wdata)
}
static struct inode *
nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
int flags)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
struct nfs_createargs arg = {
.fh = NFS_FH(dir),
.name = name->name,
.len = name->len,
.name = dentry->d_name.name,
.len = dentry->d_name.len,
.sattr = sattr
};
struct nfs_diropok res = {
......@@ -231,7 +231,7 @@ nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
int status;
fattr.valid = 0;
dprintk("NFS call create %s\n", name->name);
dprintk("NFS call create %s\n", dentry->d_name.name);
status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
dprintk("NFS reply create: %d\n", status);
if (status == 0) {
......
......@@ -24,7 +24,6 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/mempool.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
......@@ -39,25 +38,11 @@ static void nfs_readpage_result_partial(struct nfs_read_data *, int);
static void nfs_readpage_result_full(struct nfs_read_data *, int);
static kmem_cache_t *nfs_rdata_cachep;
static mempool_t *nfs_rdata_mempool;
mempool_t *nfs_rdata_mempool;
#define MIN_POOL_READ (32)
static struct nfs_read_data *nfs_readdata_alloc(void)
{
struct nfs_read_data *p;
p = (struct nfs_read_data *)mempool_alloc(nfs_rdata_mempool, SLAB_NOFS);
if (p)
memset(p, 0, sizeof(*p));
return p;
}
static __inline__ void nfs_readdata_free(struct nfs_read_data *p)
{
mempool_free(p, nfs_rdata_mempool);
}
static void nfs_readdata_release(struct rpc_task *task)
void nfs_readdata_release(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata;
nfs_readdata_free(data);
......
......@@ -215,7 +215,6 @@ nfs_complete_unlink(struct dentry *dentry)
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
spin_unlock(&dentry->d_lock);
if (data->task.tk_rpcwait == &nfs_delete_queue)
rpc_wake_up_task(&data->task);
nfs_put_unlinkdata(data);
}
......@@ -61,7 +61,6 @@
#include <linux/nfs_page.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
#include <linux/mempool.h>
#include "delegation.h"
......@@ -83,49 +82,17 @@ static int nfs_wait_on_write_congestion(struct address_space *, int);
static int nfs_wait_on_requests(struct inode *, unsigned long, unsigned int);
static kmem_cache_t *nfs_wdata_cachep;
static mempool_t *nfs_wdata_mempool;
static mempool_t *nfs_commit_mempool;
mempool_t *nfs_wdata_mempool;
mempool_t *nfs_commit_mempool;
static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion);
static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
{
struct nfs_write_data *p;
p = (struct nfs_write_data *)mempool_alloc(nfs_wdata_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
}
return p;
}
static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
{
mempool_free(p, nfs_wdata_mempool);
}
static void nfs_writedata_release(struct rpc_task *task)
void nfs_writedata_release(struct rpc_task *task)
{
struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata;
nfs_writedata_free(wdata);
}
static __inline__ struct nfs_write_data *nfs_commit_alloc(void)
{
struct nfs_write_data *p;
p = (struct nfs_write_data *)mempool_alloc(nfs_commit_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
}
return p;
}
static __inline__ void nfs_commit_free(struct nfs_write_data *p)
{
mempool_free(p, nfs_commit_mempool);
}
/* Adjust the file length if we're writing beyond the end */
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
{
......@@ -184,11 +151,10 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
int result, written = 0;
struct nfs_write_data *wdata;
wdata = kmalloc(sizeof(*wdata), GFP_NOFS);
wdata = nfs_writedata_alloc();
if (!wdata)
return -ENOMEM;
memset(wdata, 0, sizeof(*wdata));
wdata->flags = how;
wdata->cred = ctx->cred;
wdata->inode = inode;
......@@ -238,8 +204,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
io_error:
nfs_end_data_update_defer(inode);
kfree(wdata);
nfs_writedata_free(wdata);
return written ? written : result;
}
......@@ -1199,7 +1164,8 @@ void nfs_writeback_done(struct rpc_task *task)
}
if (time_before(complain, jiffies)) {
printk(KERN_WARNING
"NFS: Server wrote less than requested.\n");
"NFS: Server wrote zero bytes, expected %u.\n",
argp->count);
complain = jiffies + 300 * HZ;
}
/* Can't do anything about it except throw an error. */
......
......@@ -199,6 +199,7 @@ static inline int dname_external(struct dentry *dentry)
* These are the low-level FS interfaces to the dcache..
*/
extern void d_instantiate(struct dentry *, struct inode *);
extern struct dentry * d_instantiate_unique(struct dentry *, struct inode *);
extern void d_delete(struct dentry *);
/* allocate/de-allocate */
......@@ -242,6 +243,23 @@ static inline void d_add(struct dentry *entry, struct inode *inode)
d_rehash(entry);
}
/**
* d_add_unique - add dentry to hash queues without aliasing
* @entry: dentry to add
* @inode: The inode to attach to this dentry
*
* This adds the entry to the hash queues and initializes @inode.
* The entry was actually filled in earlier during d_alloc().
*/
static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *inode)
{
struct dentry *res;
res = d_instantiate_unique(entry, inode);
d_rehash(res != NULL ? res : entry);
return res;
}
/* used for rename() and baskets */
extern void d_move(struct dentry *, struct dentry *);
......
......@@ -1189,11 +1189,6 @@ extern long do_mount(char *, char *, char *, unsigned long, void *);
extern int vfs_statfs(struct super_block *, struct kstatfs *);
/* Return value for VFS lock functions - tells locks.c to lock conventionally
* REALLY kosha for root NFS and nfs_lock
*/
#define LOCK_USE_CLNT 1
#define FLOCK_VERIFY_READ 1
#define FLOCK_VERIFY_WRITE 2
......
......@@ -30,6 +30,7 @@
#include <linux/nfs_xdr.h>
#include <linux/rwsem.h>
#include <linux/workqueue.h>
#include <linux/mempool.h>
/*
* Enable debugging support for nfs client.
......@@ -201,6 +202,7 @@ struct nfs_inode {
#define NFS_INO_INVALID_ATTR 0x0008 /* cached attrs are invalid */
#define NFS_INO_INVALID_DATA 0x0010 /* cached data is invalid */
#define NFS_INO_INVALID_ATIME 0x0020 /* cached atime is invalid */
#define NFS_INO_INVALID_ACCESS 0x0040 /* cached access cred invalid */
static inline struct nfs_inode *NFS_I(struct inode *inode)
{
......@@ -239,7 +241,7 @@ static inline int nfs_caches_unstable(struct inode *inode)
static inline void NFS_CACHEINV(struct inode *inode)
{
if (!nfs_caches_unstable(inode))
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATTR;
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
}
static inline int nfs_server_capable(struct inode *inode, int cap)
......@@ -424,6 +426,44 @@ static inline int nfs_wb_page(struct inode *inode, struct page* page)
return nfs_wb_page_priority(inode, page, 0);
}
/*
* Allocate and free nfs_write_data structures
*/
extern mempool_t *nfs_wdata_mempool;
extern mempool_t *nfs_commit_mempool;
static inline struct nfs_write_data *nfs_writedata_alloc(void)
{
struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
}
return p;
}
static inline void nfs_writedata_free(struct nfs_write_data *p)
{
mempool_free(p, nfs_wdata_mempool);
}
extern void nfs_writedata_release(struct rpc_task *task);
static inline struct nfs_write_data *nfs_commit_alloc(void)
{
struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS);
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->pages);
}
return p;
}
static inline void nfs_commit_free(struct nfs_write_data *p)
{
mempool_free(p, nfs_commit_mempool);
}
/* Hack for future NFS swap support */
#ifndef IS_SWAPFILE
# define IS_SWAPFILE(inode) (0)
......@@ -438,6 +478,26 @@ extern int nfs_readpages(struct file *, struct address_space *,
extern int nfs_pagein_list(struct list_head *, int);
extern void nfs_readpage_result(struct rpc_task *);
/*
* Allocate and free nfs_read_data structures
*/
extern mempool_t *nfs_rdata_mempool;
static inline struct nfs_read_data *nfs_readdata_alloc(void)
{
struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS);
if (p)
memset(p, 0, sizeof(*p));
return p;
}
static inline void nfs_readdata_free(struct nfs_read_data *p)
{
mempool_free(p, nfs_rdata_mempool);
}
extern void nfs_readdata_release(struct rpc_task *task);
/*
* linux/fs/mount_clnt.c
* (Used only by nfsroot module)
......@@ -651,8 +711,7 @@ extern int nfs4_proc_setclientid_confirm(struct nfs4_client *);
extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *);
extern int nfs4_proc_async_renew(struct nfs4_client *);
extern int nfs4_proc_renew(struct nfs4_client *);
extern int nfs4_do_close(struct inode *, struct nfs4_state *);
extern int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode);
extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode);
extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *);
extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
......
......@@ -681,7 +681,7 @@ struct nfs_rpc_ops {
int (*read) (struct nfs_read_data *);
int (*write) (struct nfs_write_data *);
int (*commit) (struct nfs_write_data *);
struct inode * (*create) (struct inode *, struct qstr *,
struct inode * (*create) (struct inode *, struct dentry *,
struct iattr *, int);
int (*remove) (struct inode *, struct qstr *);
int (*unlink_setup) (struct rpc_message *,
......
......@@ -51,7 +51,6 @@ struct rpc_cred {
};
#define RPCAUTH_CRED_LOCKED 0x0001
#define RPCAUTH_CRED_UPTODATE 0x0002
#define RPCAUTH_CRED_DEAD 0x0004
#define RPCAUTH_CRED_MAGIC 0x0f4aa4f0
......@@ -131,7 +130,6 @@ int rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
int rpcauth_refreshcred(struct rpc_task *);
void rpcauth_invalcred(struct rpc_task *);
int rpcauth_uptodatecred(struct rpc_task *);
int rpcauth_deadcred(struct rpc_task *);
void rpcauth_init_credcache(struct rpc_auth *);
void rpcauth_free_credcache(struct rpc_auth *);
......
......@@ -11,7 +11,9 @@
#include <linux/timer.h>
#include <linux/sunrpc/types.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/xdr.h>
/*
......@@ -25,11 +27,18 @@ struct rpc_message {
struct rpc_cred * rpc_cred; /* Credentials */
};
struct rpc_wait_queue;
struct rpc_wait {
struct list_head list; /* wait queue links */
struct list_head links; /* Links to related tasks */
wait_queue_head_t waitq; /* sync: sleep on this q */
struct rpc_wait_queue * rpc_waitq; /* RPC wait queue we're on */
};
/*
* This is the RPC task struct
*/
struct rpc_task {
struct list_head tk_list; /* wait queue links */
#ifdef RPC_DEBUG
unsigned long tk_magic; /* 0xf00baa */
#endif
......@@ -37,7 +46,6 @@ struct rpc_task {
struct rpc_clnt * tk_client; /* RPC client */
struct rpc_rqst * tk_rqstp; /* RPC request */
int tk_status; /* result of last operation */
struct rpc_wait_queue * tk_rpcwait; /* RPC wait queue we're on */
/*
* RPC call state
......@@ -70,13 +78,18 @@ struct rpc_task {
* you have a pathological interest in kernel oopses.
*/
struct timer_list tk_timer; /* kernel timer */
wait_queue_head_t tk_wait; /* sync: sleep on this q */
unsigned long tk_timeout; /* timeout for rpc_sleep() */
unsigned short tk_flags; /* misc flags */
unsigned char tk_active : 1;/* Task has been activated */
unsigned char tk_priority : 2;/* Task priority */
unsigned long tk_runstate; /* Task run status */
struct list_head tk_links; /* links to related tasks */
struct workqueue_struct *tk_workqueue; /* Normally rpciod, but could
* be any workqueue
*/
union {
struct work_struct tk_work; /* Async task work queue */
struct rpc_wait tk_wait; /* RPC wait */
} u;
#ifdef RPC_DEBUG
unsigned short tk_pid; /* debugging aid */
#endif
......@@ -87,11 +100,11 @@ struct rpc_task {
/* support walking a list of tasks on a wait queue */
#define task_for_each(task, pos, head) \
list_for_each(pos, head) \
if ((task=list_entry(pos, struct rpc_task, tk_list)),1)
if ((task=list_entry(pos, struct rpc_task, u.tk_wait.list)),1)
#define task_for_first(task, head) \
if (!list_empty(head) && \
((task=list_entry((head)->next, struct rpc_task, tk_list)),1))
((task=list_entry((head)->next, struct rpc_task, u.tk_wait.list)),1))
/* .. and walking list of all tasks */
#define alltask_for_each(task, pos, head) \
......@@ -126,22 +139,39 @@ typedef void (*rpc_action)(struct rpc_task *);
#define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT)
#define RPC_TASK_UNINTERRUPTIBLE(t) ((t)->tk_flags & RPC_TASK_NOINTR)
#define RPC_TASK_SLEEPING 0
#define RPC_TASK_RUNNING 1
#define RPC_IS_SLEEPING(t) (test_bit(RPC_TASK_SLEEPING, &(t)->tk_runstate))
#define RPC_IS_RUNNING(t) (test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate))
#define RPC_TASK_RUNNING 0
#define RPC_TASK_QUEUED 1
#define RPC_TASK_WAKEUP 2
#define RPC_TASK_HAS_TIMER 3
#define RPC_IS_RUNNING(t) (test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate))
#define rpc_set_running(t) (set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate))
#define rpc_clear_running(t) (clear_bit(RPC_TASK_RUNNING, &(t)->tk_runstate))
#define rpc_test_and_set_running(t) \
(test_and_set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate))
#define rpc_clear_running(t) \
do { \
smp_mb__before_clear_bit(); \
clear_bit(RPC_TASK_RUNNING, &(t)->tk_runstate); \
smp_mb__after_clear_bit(); \
} while (0)
#define rpc_set_sleeping(t) (set_bit(RPC_TASK_SLEEPING, &(t)->tk_runstate))
#define RPC_IS_QUEUED(t) (test_bit(RPC_TASK_QUEUED, &(t)->tk_runstate))
#define rpc_set_queued(t) (set_bit(RPC_TASK_QUEUED, &(t)->tk_runstate))
#define rpc_clear_queued(t) \
do { \
smp_mb__before_clear_bit(); \
clear_bit(RPC_TASK_QUEUED, &(t)->tk_runstate); \
smp_mb__after_clear_bit(); \
} while (0)
#define rpc_clear_sleeping(t) \
#define rpc_start_wakeup(t) \
(test_and_set_bit(RPC_TASK_WAKEUP, &(t)->tk_runstate) == 0)
#define rpc_finish_wakeup(t) \
do { \
smp_mb__before_clear_bit(); \
clear_bit(RPC_TASK_SLEEPING, &(t)->tk_runstate); \
clear_bit(RPC_TASK_WAKEUP, &(t)->tk_runstate); \
smp_mb__after_clear_bit(); \
} while(0)
} while (0)
/*
* Task priorities.
......@@ -157,6 +187,7 @@ typedef void (*rpc_action)(struct rpc_task *);
* RPC synchronization objects
*/
struct rpc_wait_queue {
spinlock_t lock;
struct list_head tasks[RPC_NR_PRIORITY]; /* task queue for each priority level */
unsigned long cookie; /* cookie of last task serviced */
unsigned char maxpriority; /* maximum priority (0 if queue is not a priority queue) */
......@@ -177,6 +208,7 @@ struct rpc_wait_queue {
#ifndef RPC_DEBUG
# define RPC_WAITQ_INIT(var,qname) { \
.lock = SPIN_LOCK_UNLOCKED, \
.tasks = { \
[0] = LIST_HEAD_INIT(var.tasks[0]), \
[1] = LIST_HEAD_INIT(var.tasks[1]), \
......@@ -185,6 +217,7 @@ struct rpc_wait_queue {
}
#else
# define RPC_WAITQ_INIT(var,qname) { \
.lock = SPIN_LOCK_UNLOCKED, \
.tasks = { \
[0] = LIST_HEAD_INIT(var.tasks[0]), \
[1] = LIST_HEAD_INIT(var.tasks[1]), \
......@@ -209,13 +242,10 @@ void rpc_killall_tasks(struct rpc_clnt *);
int rpc_execute(struct rpc_task *);
void rpc_run_child(struct rpc_task *parent, struct rpc_task *child,
rpc_action action);
int rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *);
void rpc_remove_wait_queue(struct rpc_task *);
void rpc_init_priority_wait_queue(struct rpc_wait_queue *, const char *);
void rpc_init_wait_queue(struct rpc_wait_queue *, const char *);
void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *,
rpc_action action, rpc_action timer);
void rpc_add_timer(struct rpc_task *, rpc_action);
void rpc_wake_up_task(struct rpc_task *);
void rpc_wake_up(struct rpc_wait_queue *);
struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
......
......@@ -214,8 +214,6 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
list_for_each_safe(pos, next, &auth->au_credcache[nr]) {
struct rpc_cred *entry;
entry = list_entry(pos, struct rpc_cred, cr_hash);
if (entry->cr_flags & RPCAUTH_CRED_DEAD)
continue;
if (rpcauth_prune_expired(entry, &free))
continue;
if (entry->cr_ops->crmatch(acred, entry, taskflags)) {
......@@ -307,9 +305,6 @@ put_rpccred(struct rpc_cred *cred)
if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
return;
if ((cred->cr_flags & RPCAUTH_CRED_DEAD) && !list_empty(&cred->cr_hash))
list_del_init(&cred->cr_hash);
if (list_empty(&cred->cr_hash)) {
spin_unlock(&rpc_credcache_lock);
rpcauth_crdestroy(cred);
......@@ -413,10 +408,3 @@ rpcauth_uptodatecred(struct rpc_task *task)
return !(task->tk_msg.rpc_cred) ||
(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
}
int
rpcauth_deadcred(struct rpc_task *task)
{
return !(task->tk_msg.rpc_cred) ||
(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_DEAD);
}
......@@ -480,12 +480,14 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
if (!cred)
goto err;
if (gss_err)
cred->cr_flags |= RPCAUTH_CRED_DEAD;
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
else
gss_cred_set_ctx(cred, ctx);
spin_lock(&gss_auth->lock);
gss_msg = __gss_find_upcall(gss_auth, acred.uid);
if (gss_msg) {
if (gss_err)
gss_msg->msg.errno = -EACCES;
__gss_unhash_msg(gss_msg);
spin_unlock(&gss_auth->lock);
gss_release_msg(gss_msg);
......@@ -740,7 +742,9 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid)
maj_stat = gss_get_mic(ctx->gc_gss_ctx,
GSS_C_QOP_DEFAULT,
&verf_buf, &mic);
if(maj_stat != 0){
if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
} else if (maj_stat != 0) {
printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat);
goto out_put_ctx;
}
......@@ -779,6 +783,7 @@ gss_validate(struct rpc_task *task, u32 *p)
struct xdr_netobj mic;
u32 flav,len;
u32 service;
u32 maj_stat;
dprintk("RPC: %4u gss_validate\n", task->tk_pid);
......@@ -794,7 +799,10 @@ gss_validate(struct rpc_task *task, u32 *p)
mic.data = (u8 *)p;
mic.len = len;
if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state))
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state);
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
if (maj_stat)
goto out_bad;
service = gss_pseudoflavor_to_service(ctx->gc_gss_ctx->mech_type,
gss_cred->gc_flavor);
......@@ -821,11 +829,10 @@ gss_validate(struct rpc_task *task, u32 *p)
}
static inline int
gss_wrap_req_integ(struct gss_cl_ctx *ctx,
kxdrproc_t encode, void *rqstp, u32 *p, void *obj)
gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
kxdrproc_t encode, struct rpc_rqst *rqstp, u32 *p, void *obj)
{
struct rpc_rqst *req = (struct rpc_rqst *)rqstp;
struct xdr_buf *snd_buf = &req->rq_snd_buf;
struct xdr_buf *snd_buf = &rqstp->rq_snd_buf;
struct xdr_buf integ_buf;
u32 *integ_len = NULL;
struct xdr_netobj mic;
......@@ -836,7 +843,7 @@ gss_wrap_req_integ(struct gss_cl_ctx *ctx,
integ_len = p++;
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(req->rq_seqno);
*p++ = htonl(rqstp->rq_seqno);
status = encode(rqstp, p, obj);
if (status)
......@@ -858,7 +865,9 @@ gss_wrap_req_integ(struct gss_cl_ctx *ctx,
maj_stat = gss_get_mic(ctx->gc_gss_ctx,
GSS_C_QOP_DEFAULT, &integ_buf, &mic);
status = -EIO; /* XXX? */
if (maj_stat)
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
else if (maj_stat)
return status;
q = xdr_encode_opaque(p, NULL, mic.len);
......@@ -894,7 +903,8 @@ gss_wrap_req(struct rpc_task *task,
status = encode(rqstp, p, obj);
goto out;
case RPC_GSS_SVC_INTEGRITY:
status = gss_wrap_req_integ(ctx, encode, rqstp, p, obj);
status = gss_wrap_req_integ(cred, ctx, encode,
rqstp, p, obj);
goto out;
case RPC_GSS_SVC_PRIVACY:
default:
......@@ -907,11 +917,10 @@ gss_wrap_req(struct rpc_task *task,
}
static inline int
gss_unwrap_resp_integ(struct gss_cl_ctx *ctx,
kxdrproc_t decode, void *rqstp, u32 **p, void *obj)
gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
struct rpc_rqst *rqstp, u32 **p)
{
struct rpc_rqst *req = (struct rpc_rqst *)rqstp;
struct xdr_buf *rcv_buf = &req->rq_rcv_buf;
struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf;
struct xdr_buf integ_buf;
struct xdr_netobj mic;
u32 data_offset, mic_offset;
......@@ -926,7 +935,7 @@ gss_unwrap_resp_integ(struct gss_cl_ctx *ctx,
mic_offset = integ_len + data_offset;
if (mic_offset > rcv_buf->len)
return status;
if (ntohl(*(*p)++) != req->rq_seqno)
if (ntohl(*(*p)++) != rqstp->rq_seqno)
return status;
if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset,
......@@ -938,6 +947,8 @@ gss_unwrap_resp_integ(struct gss_cl_ctx *ctx,
maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf,
&mic, NULL);
if (maj_stat == GSS_S_CONTEXT_EXPIRED)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
if (maj_stat != GSS_S_COMPLETE)
return status;
return 0;
......@@ -962,8 +973,7 @@ gss_unwrap_resp(struct rpc_task *task,
case RPC_GSS_SVC_NONE:
goto out_decode;
case RPC_GSS_SVC_INTEGRITY:
status = gss_unwrap_resp_integ(ctx, decode,
rqstp, &p, obj);
status = gss_unwrap_resp_integ(cred, ctx, rqstp, &p);
if (status)
goto out;
break;
......
......@@ -928,7 +928,7 @@ call_refreshresult(struct rpc_task *task)
task->tk_action = call_reserve;
if (status >= 0 && rpcauth_uptodatecred(task))
return;
if (rpcauth_deadcred(task)) {
if (status == -EACCES) {
rpc_exit(task, -EACCES);
return;
}
......@@ -970,23 +970,31 @@ call_verify(struct rpc_task *task)
struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
int len = task->tk_rqstp->rq_rcv_buf.len >> 2;
u32 *p = iov->iov_base, n;
int error = -EACCES;
if ((len -= 3) < 0)
goto garbage;
goto out_overflow;
p += 1; /* skip XID */
if ((n = ntohl(*p++)) != RPC_REPLY) {
printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n);
goto garbage;
goto out_retry;
}
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
int error = -EACCES;
if (--len < 0)
goto garbage;
if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
} else if (--len < 0)
goto out_overflow;
switch ((n = ntohl(*p++))) {
case RPC_AUTH_ERROR:
break;
case RPC_MISMATCH:
printk(KERN_WARNING "%s: RPC call version mismatch!\n", __FUNCTION__);
goto out_eio;
default:
printk(KERN_WARNING "%s: RPC call rejected, unknown error: %x\n", __FUNCTION__, n);
goto out_eio;
}
if (--len < 0)
goto out_overflow;
switch ((n = ntohl(*p++))) {
case RPC_AUTH_REJECTEDCRED:
case RPC_AUTH_REJECTEDVERF:
......@@ -1017,20 +1025,18 @@ call_verify(struct rpc_task *task)
default:
printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
error = -EIO;
} else
goto garbage;
}
dprintk("RPC: %4d call_verify: call rejected %d\n",
task->tk_pid, n);
rpc_exit(task, error);
return NULL;
goto out_err;
}
if (!(p = rpcauth_checkverf(task, p))) {
printk(KERN_WARNING "call_verify: auth check failed\n");
goto garbage; /* bad verifier, retry */
goto out_retry; /* bad verifier, retry */
}
len = p - (u32 *)iov->iov_base - 1;
if (len < 0)
goto garbage;
goto out_overflow;
switch ((n = ntohl(*p++))) {
case RPC_SUCCESS:
return p;
......@@ -1053,23 +1059,28 @@ call_verify(struct rpc_task *task)
task->tk_client->cl_server);
goto out_eio;
case RPC_GARBAGE_ARGS:
dprintk("RPC: %4d %s: server saw garbage\n", task->tk_pid, __FUNCTION__);
break; /* retry */
default:
printk(KERN_WARNING "call_verify: server accept status: %x\n", n);
/* Also retry */
}
garbage:
dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
out_retry:
task->tk_client->cl_stats->rpcgarbage++;
if (task->tk_garb_retry) {
task->tk_garb_retry--;
dprintk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
dprintk(KERN_WARNING "RPC %s: retrying %4d\n", __FUNCTION__, task->tk_pid);
task->tk_action = call_bind;
return NULL;
}
printk(KERN_WARNING "RPC: garbage, exit EIO\n");
printk(KERN_WARNING "RPC %s: retry failed, exit EIO\n", __FUNCTION__);
out_eio:
rpc_exit(task, -EIO);
error = -EIO;
out_err:
rpc_exit(task, error);
return NULL;
out_overflow:
printk(KERN_WARNING "RPC %s: server reply was truncated.\n", __FUNCTION__);
goto out_retry;
}
This diff is collapsed.
......@@ -371,6 +371,7 @@ _shift_data_right_pages(struct page **pages, size_t pgto_base,
do {
/* Are any pointers crossing a page boundary? */
if (pgto_base == 0) {
flush_dcache_page(*pgto);
pgto_base = PAGE_CACHE_SIZE;
pgto--;
}
......@@ -394,6 +395,7 @@ _shift_data_right_pages(struct page **pages, size_t pgto_base,
kunmap_atomic(vto, KM_USER0);
} while ((len -= copy) != 0);
flush_dcache_page(*pgto);
}
/*
......@@ -427,12 +429,14 @@ _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
pgbase += copy;
if (pgbase == PAGE_CACHE_SIZE) {
flush_dcache_page(*pgto);
pgbase = 0;
pgto++;
}
p += copy;
} while ((len -= copy) != 0);
flush_dcache_page(*pgto);
}
/*
......
......@@ -893,7 +893,8 @@ tcp_read_xid(struct rpc_xprt *xprt, skb_reader_t *desc)
xprt->tcp_flags &= ~XPRT_COPY_XID;
xprt->tcp_flags |= XPRT_COPY_DATA;
xprt->tcp_copied = 4;
dprintk("RPC: reading reply for XID %08x\n", xprt->tcp_xid);
dprintk("RPC: reading reply for XID %08x\n",
ntohl(xprt->tcp_xid));
tcp_check_recm(xprt);
}
......@@ -913,7 +914,7 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc)
if (!req) {
xprt->tcp_flags &= ~XPRT_COPY_DATA;
dprintk("RPC: XID %08x request not found!\n",
xprt->tcp_xid);
ntohl(xprt->tcp_xid));
spin_unlock(&xprt->sock_lock);
return;
}
......@@ -1103,7 +1104,7 @@ xprt_write_space(struct sock *sk)
goto out;
spin_lock_bh(&xprt->sock_lock);
if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->pending)
if (xprt->snd_task)
rpc_wake_up_task(xprt->snd_task);
spin_unlock_bh(&xprt->sock_lock);
out:
......@@ -1362,7 +1363,7 @@ xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
req->rq_xprt = xprt;
req->rq_xid = xprt_alloc_xid(xprt);
dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid,
req, req->rq_xid);
req, ntohl(req->rq_xid));
}
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment