Commit aeb08fc0 authored by Trond Myklebust's avatar Trond Myklebust

Merge http://nfsclient.bkbits.net/linux-2.5

into fys.uio.no:/home/linux/bitkeeper/nfsclient-2.5
parents fa9fcf5f b67cfdb8
...@@ -1305,15 +1305,18 @@ config NFS_V3 ...@@ -1305,15 +1305,18 @@ config NFS_V3
Say Y here if you want your NFS client to be able to speak the newer Say Y here if you want your NFS client to be able to speak the newer
version 3 of the NFS protocol. version 3 of the NFS protocol.
If unsure, say N. If unsure, say Y.
config NFS_V4 config NFS_V4
bool "Provide NFSv4 client support (EXPERIMENTAL)" bool "Provide NFSv4 client support (EXPERIMENTAL)"
depends on NFS_FS && EXPERIMENTAL depends on NFS_FS && EXPERIMENTAL
select RPCSEC_GSS_KRB5
help help
Say Y here if you want your NFS client to be able to speak the newer Say Y here if you want your NFS client to be able to speak the newer
version 4 of the NFS protocol. This feature is experimental, and version 4 of the NFS protocol.
should only be used if you are interested in helping to test NFSv4.
Note: Requires auxiliary userspace daemons which may be found on
http://www.citi.umich.edu/projects/nfsv4/
If unsure, say N. If unsure, say N.
...@@ -1422,28 +1425,24 @@ config SUNRPC ...@@ -1422,28 +1425,24 @@ config SUNRPC
tristate tristate
config SUNRPC_GSS config SUNRPC_GSS
tristate "Provide RPCSEC_GSS authentication (EXPERIMENTAL)" tristate
config RPCSEC_GSS_KRB5
tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL depends on SUNRPC && EXPERIMENTAL
default SUNRPC if NFS_V4=y select SUNRPC_GSS
select CRYPTO
select CRYPTO_MD5
select CRYPTO_DES
help help
Provides cryptographic authentication for NFS rpc requests. To Provides for secure RPC calls by means of a gss-api
make this useful, you must also select at least one rpcsec_gss mechanism based on Kerberos V5. This is required for
mechanism.
Note: You should always select this option if you wish to use
NFSv4. NFSv4.
config RPCSEC_GSS_KRB5 Note: Requires an auxiliary userspace daemon which may be found on
tristate "Kerberos V mechanism for RPCSEC_GSS (EXPERIMENTAL)" http://www.citi.umich.edu/projects/nfsv4/
depends on SUNRPC_GSS && CRYPTO_DES && CRYPTO_MD5
default SUNRPC_GSS if NFS_V4=y
help
Provides a gss-api mechanism based on Kerberos V5 (this is
mandatory for RFC3010-compliant NFSv4 implementations).
Requires a userspace daemon;
see http://www.citi.umich.edu/projects/nfsv4/.
Note: If you select this option, please ensure that you also If unsure, say N.
enable the MD5 and DES crypto ciphers.
config SMB_FS config SMB_FS
tristate "SMB file system support (to mount Windows shares etc.)" tristate "SMB file system support (to mount Windows shares etc.)"
......
...@@ -1178,6 +1178,8 @@ void inode_update_time(struct inode *inode, int ctime_too) ...@@ -1178,6 +1178,8 @@ void inode_update_time(struct inode *inode, int ctime_too)
struct timespec now; struct timespec now;
int sync_it = 0; int sync_it = 0;
if (IS_NOCMTIME(inode))
return;
if (IS_RDONLY(inode)) if (IS_RDONLY(inode))
return; return;
......
...@@ -443,7 +443,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) ...@@ -443,7 +443,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
} }
if (status < 0) if (status < 0)
return status; return status;
} while (resp->status == NLM_LCK_BLOCKED); } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block);
if (resp->status == NLM_LCK_GRANTED) { if (resp->status == NLM_LCK_GRANTED) {
fl->fl_u.nfs_fl.state = host->h_state; fl->fl_u.nfs_fl.state = host->h_state;
......
...@@ -188,14 +188,14 @@ nlm_bind_host(struct nlm_host *host) ...@@ -188,14 +188,14 @@ nlm_bind_host(struct nlm_host *host)
} }
} else { } else {
xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL); xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL);
if (xprt == NULL) if (IS_ERR(xprt))
goto forgetit; goto forgetit;
xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
clnt = rpc_create_client(xprt, host->h_name, &nlm_program, clnt = rpc_create_client(xprt, host->h_name, &nlm_program,
host->h_version, host->h_authflavor); host->h_version, host->h_authflavor);
if (clnt == NULL) { if (IS_ERR(clnt)) {
xprt_destroy(xprt); xprt_destroy(xprt);
goto forgetit; goto forgetit;
} }
......
...@@ -36,10 +36,11 @@ nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res) ...@@ -36,10 +36,11 @@ nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res)
int status; int status;
struct nsm_args args; struct nsm_args args;
status = -EACCES;
clnt = nsm_create(); clnt = nsm_create();
if (!clnt) if (IS_ERR(clnt)) {
status = PTR_ERR(clnt);
goto out; goto out;
}
args.addr = host->h_addr.sin_addr.s_addr; args.addr = host->h_addr.sin_addr.s_addr;
args.proto= (host->h_proto<<1) | host->h_server; args.proto= (host->h_proto<<1) | host->h_server;
...@@ -104,7 +105,7 @@ static struct rpc_clnt * ...@@ -104,7 +105,7 @@ static struct rpc_clnt *
nsm_create(void) nsm_create(void)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct rpc_clnt *clnt = NULL; struct rpc_clnt *clnt;
struct sockaddr_in sin; struct sockaddr_in sin;
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
...@@ -112,24 +113,23 @@ nsm_create(void) ...@@ -112,24 +113,23 @@ nsm_create(void)
sin.sin_port = 0; sin.sin_port = 0;
xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL); xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
if (!xprt) if (IS_ERR(xprt))
goto out; return (struct rpc_clnt *)xprt;
clnt = rpc_create_client(xprt, "localhost", clnt = rpc_create_client(xprt, "localhost",
&nsm_program, SM_VERSION, &nsm_program, SM_VERSION,
RPC_AUTH_NULL); RPC_AUTH_NULL);
if (!clnt) if (IS_ERR(clnt))
goto out_destroy; goto out_destroy;
clnt->cl_softrtry = 1; clnt->cl_softrtry = 1;
clnt->cl_chatty = 1; clnt->cl_chatty = 1;
clnt->cl_oneshot = 1; clnt->cl_oneshot = 1;
xprt->resvport = 1; /* NSM requires a reserved port */ xprt->resvport = 1; /* NSM requires a reserved port */
out:
return clnt; return clnt;
out_destroy: out_destroy:
xprt_destroy(xprt); xprt_destroy(xprt);
goto out; return clnt;
} }
/* /*
......
...@@ -452,6 +452,24 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, ...@@ -452,6 +452,24 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
return rpc_success; return rpc_success;
} }
/*
* client sent a GRANTED_RES, let's remove the associated block
*/
static int
nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp,
void *resp)
{
if (!nlmsvc_ops)
return rpc_success;
dprintk("lockd: GRANTED_RES called\n");
nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status);
return rpc_success;
}
/* /*
* This is the generic lockd callback for async RPC calls * This is the generic lockd callback for async RPC calls
*/ */
...@@ -515,7 +533,6 @@ nlm4svc_callback_exit(struct rpc_task *task) ...@@ -515,7 +533,6 @@ nlm4svc_callback_exit(struct rpc_task *task)
#define nlm4svc_proc_lock_res nlm4svc_proc_null #define nlm4svc_proc_lock_res nlm4svc_proc_null
#define nlm4svc_proc_cancel_res nlm4svc_proc_null #define nlm4svc_proc_cancel_res nlm4svc_proc_null
#define nlm4svc_proc_unlock_res nlm4svc_proc_null #define nlm4svc_proc_unlock_res nlm4svc_proc_null
#define nlm4svc_proc_granted_res nlm4svc_proc_null
struct nlm_void { int dummy; }; struct nlm_void { int dummy; };
...@@ -548,7 +565,7 @@ struct svc_procedure nlmsvc_procedures4[] = { ...@@ -548,7 +565,7 @@ struct svc_procedure nlmsvc_procedures4[] = {
PROC(lock_res, lockres, norep, res, void, 1), PROC(lock_res, lockres, norep, res, void, 1),
PROC(cancel_res, cancelres, norep, res, void, 1), PROC(cancel_res, cancelres, norep, res, void, 1),
PROC(unlock_res, unlockres, norep, res, void, 1), PROC(unlock_res, unlockres, norep, res, void, 1),
PROC(granted_res, grantedres, norep, res, void, 1), PROC(granted_res, res, norep, res, void, 1),
/* statd callback */ /* statd callback */
PROC(sm_notify, reboot, void, reboot, void, 1), PROC(sm_notify, reboot, void, reboot, void, 1),
PROC(none, void, void, void, void, 0), PROC(none, void, void, void, void, 0),
......
...@@ -64,7 +64,7 @@ nlmsvc_insert_block(struct nlm_block *block, unsigned long when) ...@@ -64,7 +64,7 @@ nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
if (when != NLM_NEVER) { if (when != NLM_NEVER) {
if ((when += jiffies) == NLM_NEVER) if ((when += jiffies) == NLM_NEVER)
when ++; when ++;
while ((b = *bp) && time_before_eq(b->b_when,when)) while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER)
bp = &b->b_next; bp = &b->b_next;
} else } else
while ((b = *bp)) while ((b = *bp))
...@@ -143,14 +143,15 @@ static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b) ...@@ -143,14 +143,15 @@ static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
* Find a block with a given NLM cookie. * Find a block with a given NLM cookie.
*/ */
static inline struct nlm_block * static inline struct nlm_block *
nlmsvc_find_block(struct nlm_cookie *cookie) nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin)
{ {
struct nlm_block *block; struct nlm_block *block;
for (block = nlm_blocked; block; block = block->b_next) { for (block = nlm_blocked; block; block = block->b_next) {
dprintk("cookie: head of blocked queue %p, block %p\n", dprintk("cookie: head of blocked queue %p, block %p\n",
nlm_blocked, block); nlm_blocked, block);
if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)) if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)
&& nlm_cmp_addr(sin, &block->b_host->h_addr))
break; break;
} }
...@@ -566,12 +567,16 @@ nlmsvc_grant_callback(struct rpc_task *task) ...@@ -566,12 +567,16 @@ nlmsvc_grant_callback(struct rpc_task *task)
struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata;
struct nlm_block *block; struct nlm_block *block;
unsigned long timeout; unsigned long timeout;
struct sockaddr_in *peer_addr = RPC_PEERADDR(task->tk_client);
dprintk("lockd: GRANT_MSG RPC callback\n"); dprintk("lockd: GRANT_MSG RPC callback\n");
dprintk("callback: looking for cookie %x \n", dprintk("callback: looking for cookie %x, host (%08x)\n",
*(unsigned int *)(call->a_args.cookie.data)); *(unsigned int *)(call->a_args.cookie.data),
if (!(block = nlmsvc_find_block(&call->a_args.cookie))) { ntohl(peer_addr->sin_addr.s_addr));
dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data)); if (!(block = nlmsvc_find_block(&call->a_args.cookie, peer_addr))) {
dprintk("lockd: no block for cookie %x, host (%08x)\n",
*(u32 *)(call->a_args.cookie.data),
ntohl(peer_addr->sin_addr.s_addr));
return; return;
} }
...@@ -600,18 +605,21 @@ nlmsvc_grant_callback(struct rpc_task *task) ...@@ -600,18 +605,21 @@ nlmsvc_grant_callback(struct rpc_task *task)
* block. * block.
*/ */
void void
nlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status) nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status)
{ {
struct nlm_block *block; struct nlm_block *block;
struct nlm_file *file; struct nlm_file *file;
if (!(block = nlmsvc_find_block(cookie))) dprintk("grant_reply: looking for cookie %x, host (%08x), s=%d \n",
*(unsigned int *)(cookie->data),
ntohl(rqstp->rq_addr.sin_addr.s_addr), status);
if (!(block = nlmsvc_find_block(cookie, &rqstp->rq_addr)))
return; return;
file = block->b_file; file = block->b_file;
file->f_count++; file->f_count++;
down(&file->f_sema); down(&file->f_sema);
if ((block = nlmsvc_find_block(cookie)) != NULL) { if ((block = nlmsvc_find_block(cookie,&rqstp->rq_addr)) != NULL) {
if (status == NLM_LCK_DENIED_GRACE_PERIOD) { if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
/* Try again in a couple of seconds */ /* Try again in a couple of seconds */
nlmsvc_insert_block(block, 10 * HZ); nlmsvc_insert_block(block, 10 * HZ);
......
...@@ -478,6 +478,22 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, ...@@ -478,6 +478,22 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
return rpc_success; return rpc_success;
} }
/*
* client sent a GRANTED_RES, let's remove the associated block
*/
static int
nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp,
void *resp)
{
if (!nlmsvc_ops)
return rpc_success;
dprintk("lockd: GRANTED_RES called\n");
nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status);
return rpc_success;
}
/* /*
* This is the generic lockd callback for async RPC calls * This is the generic lockd callback for async RPC calls
*/ */
...@@ -541,7 +557,6 @@ nlmsvc_callback_exit(struct rpc_task *task) ...@@ -541,7 +557,6 @@ nlmsvc_callback_exit(struct rpc_task *task)
#define nlmsvc_proc_lock_res nlmsvc_proc_null #define nlmsvc_proc_lock_res nlmsvc_proc_null
#define nlmsvc_proc_cancel_res nlmsvc_proc_null #define nlmsvc_proc_cancel_res nlmsvc_proc_null
#define nlmsvc_proc_unlock_res nlmsvc_proc_null #define nlmsvc_proc_unlock_res nlmsvc_proc_null
#define nlmsvc_proc_granted_res nlmsvc_proc_null
struct nlm_void { int dummy; }; struct nlm_void { int dummy; };
...@@ -576,7 +591,7 @@ struct svc_procedure nlmsvc_procedures[] = { ...@@ -576,7 +591,7 @@ struct svc_procedure nlmsvc_procedures[] = {
PROC(lock_res, lockres, norep, res, void, 1), PROC(lock_res, lockres, norep, res, void, 1),
PROC(cancel_res, cancelres, norep, res, void, 1), PROC(cancel_res, cancelres, norep, res, void, 1),
PROC(unlock_res, unlockres, norep, res, void, 1), PROC(unlock_res, unlockres, norep, res, void, 1),
PROC(granted_res, grantedres, norep, res, void, 1), PROC(granted_res, res, norep, res, void, 1),
/* statd callback */ /* statd callback */
PROC(sm_notify, reboot, void, reboot, void, 1), PROC(sm_notify, reboot, void, reboot, void, 1),
PROC(none, void, void, void, void, 1), PROC(none, void, void, void, void, 1),
......
...@@ -139,11 +139,13 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) ...@@ -139,11 +139,13 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
struct file *file = desc->file; struct file *file = desc->file;
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
struct rpc_cred *cred = nfs_file_cred(file); struct rpc_cred *cred = nfs_file_cred(file);
unsigned long timestamp;
int error; int error;
dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index);
again: again:
timestamp = jiffies;
error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page, error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page,
NFS_SERVER(inode)->dtsize, desc->plus); NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) { if (error < 0) {
...@@ -157,18 +159,21 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) ...@@ -157,18 +159,21 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
goto error; goto error;
} }
SetPageUptodate(page); SetPageUptodate(page);
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
/* Ensure consistent page alignment of the data. /* Ensure consistent page alignment of the data.
* Note: assumes we have exclusive access to this mapping either * Note: assumes we have exclusive access to this mapping either
* throught inode->i_sem or some other mechanism. * throught inode->i_sem or some other mechanism.
*/ */
if (page->index == 0) if (page->index == 0) {
invalidate_inode_pages(inode->i_mapping); invalidate_inode_pages(inode->i_mapping);
NFS_I(inode)->readdir_timestamp = timestamp;
}
unlock_page(page); unlock_page(page);
return 0; return 0;
error: error:
SetPageError(page); SetPageError(page);
unlock_page(page); unlock_page(page);
invalidate_inode_pages(inode->i_mapping); nfs_zap_caches(inode);
desc->error = error; desc->error = error;
return -EIO; return -EIO;
} }
...@@ -381,6 +386,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, ...@@ -381,6 +386,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
page, page,
NFS_SERVER(inode)->dtsize, NFS_SERVER(inode)->dtsize,
desc->plus); desc->plus);
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
desc->page = page; desc->page = page;
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */
if (desc->error >= 0) { if (desc->error >= 0) {
...@@ -459,7 +465,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -459,7 +465,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
} }
res = 0; res = 0;
break; break;
} else if (res < 0) }
if (res == -ETOOSMALL && desc->plus) {
NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
nfs_zap_caches(inode);
desc->plus = 0;
desc->entry->eof = 0;
continue;
}
if (res < 0)
break; break;
res = nfs_do_filldir(desc, dirent, filldir); res = nfs_do_filldir(desc, dirent, filldir);
...@@ -481,14 +495,19 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -481,14 +495,19 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
* In the case it has, we assume that the dentries are untrustworthy * In the case it has, we assume that the dentries are untrustworthy
* and may need to be looked up again. * and may need to be looked up again.
*/ */
static inline static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
{ {
if (IS_ROOT(dentry)) if (IS_ROOT(dentry))
return 1; return 1;
if (nfs_revalidate_inode(NFS_SERVER(dir), dir)) if ((NFS_FLAGS(dir) & NFS_INO_INVALID_ATTR) != 0
|| nfs_attribute_timeout(dir))
return 0; return 0;
return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir)); return nfs_verify_change_attribute(dir, (unsigned long)dentry->d_fsdata);
}
static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
{
dentry->d_fsdata = (void *)verf;
} }
/* /*
...@@ -528,9 +547,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, ...@@ -528,9 +547,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
/* Don't revalidate a negative dentry if we're creating a new file */ /* Don't revalidate a negative dentry if we're creating a new file */
if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE)) if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE))
return 0; return 0;
if (!nfs_check_verifier(dir, dentry)) return !nfs_check_verifier(dir, dentry);
return 1;
return time_after(jiffies, dentry->d_time + NFS_ATTRTIMEO(dir));
} }
/* /*
...@@ -552,6 +569,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -552,6 +569,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
int error; int error;
struct nfs_fh fhandle; struct nfs_fh fhandle;
struct nfs_fattr fattr; struct nfs_fattr fattr;
unsigned long verifier;
int isopen = 0; int isopen = 0;
parent = dget_parent(dentry); parent = dget_parent(dentry);
...@@ -574,6 +592,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -574,6 +592,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_bad; goto out_bad;
} }
/* Revalidate parent directory attribute cache */
nfs_revalidate_inode(NFS_SERVER(dir), dir);
/* Force a full look up iff the parent directory has changed */ /* Force a full look up iff the parent directory has changed */
if (nfs_check_verifier(dir, dentry)) { if (nfs_check_verifier(dir, dentry)) {
if (nfs_lookup_verify_inode(inode, isopen)) if (nfs_lookup_verify_inode(inode, isopen))
...@@ -581,6 +602,12 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -581,6 +602,12 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_valid; goto out_valid;
} }
/*
* Note: we're not holding inode->i_sem and so may be racing with
* operations that change the directory. We therefore save the
* change attribute *before* we do the RPC call.
*/
verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) { if (!error) {
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
...@@ -603,6 +630,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -603,6 +630,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
out_valid_renew: out_valid_renew:
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, verifier);
out_valid: out_valid:
unlock_kernel(); unlock_kernel();
dput(parent); dput(parent);
...@@ -638,6 +666,11 @@ static int nfs_dentry_delete(struct dentry *dentry) ...@@ -638,6 +666,11 @@ static int nfs_dentry_delete(struct dentry *dentry)
/* Unhash it, so that ->d_iput() would be called */ /* Unhash it, so that ->d_iput() would be called */
return 1; return 1;
} }
if (!(dentry->d_sb->s_flags & MS_ACTIVE)) {
/* Unhash it, so that ancestors of killed async unlink
* files will be cleaned up during umount */
return 1;
}
return 0; return 0;
} }
...@@ -693,6 +726,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -693,6 +726,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
dentry->d_op = NFS_PROTO(dir)->dentry_ops; dentry->d_op = NFS_PROTO(dir)->dentry_ops;
lock_kernel(); lock_kernel();
/* Revalidate parent directory attribute cache */
nfs_revalidate_inode(NFS_SERVER(dir), dir);
/* If we're doing an exclusive create, optimize away the lookup */ /* If we're doing an exclusive create, optimize away the lookup */
if (nfs_is_exclusive_create(dir, nd)) if (nfs_is_exclusive_create(dir, nd))
...@@ -715,6 +750,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -715,6 +750,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
error = 0; error = 0;
d_add(dentry, inode); d_add(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unlock: out_unlock:
unlock_kernel(); unlock_kernel();
out: out:
...@@ -768,7 +804,15 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -768,7 +804,15 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
/* Open the file on the server */ /* Open the file on the server */
lock_kernel(); lock_kernel();
inode = nfs4_atomic_open(dir, dentry, nd); /* Revalidate parent directory attribute cache */
nfs_revalidate_inode(NFS_SERVER(dir), dir);
if (nd->intent.open.flags & O_CREAT) {
nfs_begin_data_update(dir);
inode = nfs4_atomic_open(dir, dentry, nd);
nfs_end_data_update(dir);
} else
inode = nfs4_atomic_open(dir, dentry, nd);
unlock_kernel(); unlock_kernel();
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
error = PTR_ERR(inode); error = PTR_ERR(inode);
...@@ -790,6 +834,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -790,6 +834,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
no_entry: no_entry:
d_add(dentry, inode); d_add(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out: out:
BUG_ON(error > 0); BUG_ON(error > 0);
return ERR_PTR(error); return ERR_PTR(error);
...@@ -801,13 +846,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -801,13 +846,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
{ {
struct dentry *parent = NULL; struct dentry *parent = NULL;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct inode *dir;
unsigned long verifier;
int openflags, ret = 0; int openflags, ret = 0;
/* NFS only supports OPEN for regular files */ /* NFS only supports OPEN for regular files */
if (inode && !S_ISREG(inode->i_mode)) if (inode && !S_ISREG(inode->i_mode))
goto no_open; goto no_open;
parent = dget_parent(dentry); parent = dget_parent(dentry);
if (!is_atomic_open(parent->d_inode, nd)) dir = parent->d_inode;
if (!is_atomic_open(dir, nd))
goto no_open; goto no_open;
openflags = nd->intent.open.flags; openflags = nd->intent.open.flags;
if (openflags & O_CREAT) { if (openflags & O_CREAT) {
...@@ -821,8 +869,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -821,8 +869,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
/* We can't create new files, or truncate existing ones here */ /* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC); openflags &= ~(O_CREAT|O_TRUNC);
/*
* Note: we're not holding inode->i_sem and so may be racing with
* operations that change the directory. We therefore save the
* change attribute *before* we do the RPC call.
*/
lock_kernel(); lock_kernel();
ret = nfs4_open_revalidate(parent->d_inode, dentry, openflags); verifier = nfs_save_change_attribute(dir);
ret = nfs4_open_revalidate(dir, dentry, openflags);
if (!ret)
nfs_set_verifier(dentry, verifier);
unlock_kernel(); unlock_kernel();
out: out:
dput(parent); dput(parent);
...@@ -869,15 +925,20 @@ int nfs_cached_lookup(struct inode *dir, struct dentry *dentry, ...@@ -869,15 +925,20 @@ int nfs_cached_lookup(struct inode *dir, struct dentry *dentry,
struct nfs_server *server; struct nfs_server *server;
struct nfs_entry entry; struct nfs_entry entry;
struct page *page; struct page *page;
unsigned long timestamp = NFS_MTIME_UPDATE(dir); unsigned long timestamp;
int res; int res;
if (!NFS_USE_READDIRPLUS(dir)) if (!NFS_USE_READDIRPLUS(dir))
return -ENOENT; return -ENOENT;
server = NFS_SERVER(dir); server = NFS_SERVER(dir);
if (server->flags & NFS_MOUNT_NOAC) /* Don't use readdirplus unless the cache is stable */
if ((server->flags & NFS_MOUNT_NOAC) != 0
|| nfs_caches_unstable(dir)
|| nfs_attribute_timeout(dir))
return -ENOENT;
if ((NFS_FLAGS(dir) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0)
return -ENOENT; return -ENOENT;
nfs_revalidate_inode(server, dir); timestamp = NFS_I(dir)->readdir_timestamp;
entry.fh = fh; entry.fh = fh;
entry.fattr = fattr; entry.fattr = fattr;
...@@ -931,9 +992,10 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, ...@@ -931,9 +992,10 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
if (inode) { if (inode) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
error = 0; nfs_set_verifier(dentry, nfs_save_change_attribute(dentry->d_parent->d_inode));
return 0;
} }
return error; error = -ENOMEM;
out_err: out_err:
d_drop(dentry); d_drop(dentry);
return error; return error;
...@@ -969,11 +1031,13 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -969,11 +1031,13 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
* does not pass the create flags. * does not pass the create flags.
*/ */
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags); inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags);
nfs_end_data_update(dir);
if (!IS_ERR(inode)) { if (!IS_ERR(inode)) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
error = 0; error = 0;
} else { } else {
error = PTR_ERR(inode); error = PTR_ERR(inode);
...@@ -1004,9 +1068,10 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) ...@@ -1004,9 +1068,10 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev, error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev,
&fhandle, &fattr); &fhandle, &fattr);
nfs_end_data_update(dir);
if (!error) if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr); error = nfs_instantiate(dentry, &fhandle, &fattr);
else else
...@@ -1041,9 +1106,10 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -1041,9 +1106,10 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
*/ */
d_drop(dentry); d_drop(dentry);
#endif #endif
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle, error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
&fattr); &fattr);
nfs_end_data_update(dir);
if (!error) if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr); error = nfs_instantiate(dentry, &fhandle, &fattr);
else else
...@@ -1060,10 +1126,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1060,10 +1126,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
dir->i_ino, dentry->d_name.name); dir->i_ino, dentry->d_name.name);
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
if (!error) /* Ensure the VFS deletes this inode */
if (error == 0 && dentry->d_inode != NULL)
dentry->d_inode->i_nlink = 0; dentry->d_inode->i_nlink = 0;
nfs_end_data_update(dir);
unlock_kernel(); unlock_kernel();
return error; return error;
...@@ -1119,12 +1187,21 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1119,12 +1187,21 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out; goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */ } while(sdentry->d_inode != NULL); /* need negative lookup */
nfs_zap_caches(dir);
qsilly.name = silly; qsilly.name = silly;
qsilly.len = strlen(silly); qsilly.len = strlen(silly);
error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); nfs_begin_data_update(dir);
if (dentry->d_inode) {
nfs_begin_data_update(dentry->d_inode);
error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
dir, &qsilly);
nfs_end_data_update(dentry->d_inode);
} else
error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
dir, &qsilly);
nfs_end_data_update(dir);
if (!error) { if (!error) {
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
d_move(dentry, sdentry); d_move(dentry, sdentry);
error = nfs_async_unlink(dentry); error = nfs_async_unlink(dentry);
/* If we return 0 we don't unlink */ /* If we return 0 we don't unlink */
...@@ -1156,14 +1233,17 @@ static int nfs_safe_remove(struct dentry *dentry) ...@@ -1156,14 +1233,17 @@ static int nfs_safe_remove(struct dentry *dentry)
goto out; goto out;
} }
nfs_zap_caches(dir); nfs_begin_data_update(dir);
if (inode) if (inode != NULL) {
NFS_CACHEINV(inode); nfs_begin_data_update(inode);
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
if (error < 0) /* The VFS may want to delete this inode */
goto out; if (error == 0)
if (inode) inode->i_nlink--;
inode->i_nlink--; nfs_end_data_update(inode);
} else
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
nfs_end_data_update(dir);
out: out:
return error; return error;
} }
...@@ -1198,9 +1278,10 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1198,9 +1278,10 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
error = nfs_safe_remove(dentry); error = nfs_safe_remove(dentry);
if (!error) if (!error) {
nfs_renew_times(dentry); nfs_renew_times(dentry);
else if (need_rehash) nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
} else if (need_rehash)
d_rehash(dentry); d_rehash(dentry);
unlock_kernel(); unlock_kernel();
return error; return error;
...@@ -1247,9 +1328,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1247,9 +1328,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
qsymname.len = strlen(symname); qsymname.len = strlen(symname);
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname,
&attr, &sym_fh, &sym_attr); &attr, &sym_fh, &sym_attr);
nfs_end_data_update(dir);
if (!error) { if (!error) {
error = nfs_instantiate(dentry, &sym_fh, &sym_attr); error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
} else { } else {
...@@ -1281,9 +1363,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) ...@@ -1281,9 +1363,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
*/ */
lock_kernel(); lock_kernel();
d_drop(dentry); d_drop(dentry);
nfs_zap_caches(dir);
NFS_CACHEINV(inode); nfs_begin_data_update(dir);
nfs_begin_data_update(inode);
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
nfs_end_data_update(inode);
nfs_end_data_update(dir);
unlock_kernel(); unlock_kernel();
return error; return error;
} }
...@@ -1388,16 +1473,23 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1388,16 +1473,23 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_inode) if (new_inode)
d_delete(new_dentry); d_delete(new_dentry);
nfs_zap_caches(new_dir); nfs_begin_data_update(old_dir);
nfs_zap_caches(old_dir); nfs_begin_data_update(new_dir);
nfs_begin_data_update(old_inode);
error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
new_dir, &new_dentry->d_name); new_dir, &new_dentry->d_name);
nfs_end_data_update(old_inode);
nfs_end_data_update(new_dir);
nfs_end_data_update(old_dir);
out: out:
if (rehash) if (rehash)
d_rehash(rehash); d_rehash(rehash);
if (!error && !S_ISDIR(old_inode->i_mode)) if (!error) {
d_move(old_dentry, new_dentry); if (!S_ISDIR(old_inode->i_mode))
nfs_renew_times(new_dentry); d_move(old_dentry, new_dentry);
nfs_renew_times(new_dentry);
nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir));
}
/* new dentry created? */ /* new dentry created? */
if (dentry) if (dentry)
...@@ -1451,7 +1543,8 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1451,7 +1543,8 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (cache->cred == cred if (cache->cred == cred
&& time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) { && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
&& !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
if (!(res = cache->err)) { if (!(res = cache->err)) {
/* Is the mask a subset of an accepted mask? */ /* Is the mask a subset of an accepted mask? */
if ((cache->mask & mask) == mask) if ((cache->mask & mask) == mask)
......
...@@ -269,6 +269,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -269,6 +269,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize) if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize)
wdata.args.stable = NFS_FILE_SYNC; wdata.args.stable = NFS_FILE_SYNC;
nfs_begin_data_update(inode);
retry: retry:
need_commit = 0; need_commit = 0;
tot_bytes = 0; tot_bytes = 0;
...@@ -334,6 +335,8 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -334,6 +335,8 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
VERF_SIZE) != 0) VERF_SIZE) != 0)
goto sync_retry; goto sync_retry;
} }
nfs_end_data_update(inode);
NFS_FLAGS(inode) |= NFS_INO_INVALID_DATA;
return tot_bytes; return tot_bytes;
......
...@@ -104,11 +104,16 @@ nfs_file_flush(struct file *file) ...@@ -104,11 +104,16 @@ nfs_file_flush(struct file *file)
dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
if ((file->f_mode & FMODE_WRITE) == 0)
return 0;
lock_kernel(); lock_kernel();
status = nfs_wb_file(inode, file); /* Ensure that data+attribute caches are up to date after close() */
status = nfs_wb_all(inode);
if (!status) { if (!status) {
status = file->f_error; status = file->f_error;
file->f_error = 0; file->f_error = 0;
if (!status)
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
} }
unlock_kernel(); unlock_kernel();
return status; return status;
...@@ -179,7 +184,7 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync) ...@@ -179,7 +184,7 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
lock_kernel(); lock_kernel();
status = nfs_wb_file(inode, file); status = nfs_wb_all(inode);
if (!status) { if (!status) {
status = file->f_error; status = file->f_error;
file->f_error = 0; file->f_error = 0;
......
...@@ -47,14 +47,11 @@ ...@@ -47,14 +47,11 @@
* their needs. People that do NFS over a slow network, might for * their needs. People that do NFS over a slow network, might for
* instance want to reduce it to something closer to 1 for improved * instance want to reduce it to something closer to 1 for improved
* interactive response. * interactive response.
*
* For the moment, though, we instead set it to RPC_MAXREQS, which
* is the maximum number of simultaneous RPC requests on the wire.
*/ */
#define NFS_MAX_READAHEAD RPC_MAXREQS #define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1)
void nfs_zap_caches(struct inode *);
static void nfs_invalidate_inode(struct inode *); static void nfs_invalidate_inode(struct inode *);
static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long);
static struct inode *nfs_alloc_inode(struct super_block *sb); static struct inode *nfs_alloc_inode(struct super_block *sb);
static void nfs_destroy_inode(struct inode *); static void nfs_destroy_inode(struct inode *);
...@@ -118,7 +115,7 @@ nfs_write_inode(struct inode *inode, int sync) ...@@ -118,7 +115,7 @@ nfs_write_inode(struct inode *inode, int sync)
{ {
int flags = sync ? FLUSH_WAIT : 0; int flags = sync ? FLUSH_WAIT : 0;
nfs_commit_file(inode, NULL, 0, 0, flags); nfs_commit_inode(inode, 0, 0, flags);
} }
static void static void
...@@ -151,6 +148,7 @@ nfs_clear_inode(struct inode *inode) ...@@ -151,6 +148,7 @@ nfs_clear_inode(struct inode *inode)
cred = nfsi->cache_access.cred; cred = nfsi->cache_access.cred;
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
BUG_ON(atomic_read(&nfsi->data_updates) != 0);
} }
void void
...@@ -230,50 +228,23 @@ nfs_block_size(unsigned long bsize, unsigned char *nrbitsp) ...@@ -230,50 +228,23 @@ nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
/* /*
* Obtain the root inode of the file system. * Obtain the root inode of the file system.
*/ */
static int static struct inode *
nfs_get_root(struct inode **rooti, rpc_authflavor_t authflavor, struct super_block *sb, struct nfs_fh *rootfh) nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo)
{ {
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
struct nfs_fattr fattr = { }; struct inode *rooti;
int error; int error;
error = server->rpc_ops->getroot(server, rootfh, &fattr); error = server->rpc_ops->getroot(server, rootfh, fsinfo);
if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) { if (error < 0) {
/*
* Some authentication types (gss/krb5, most notably)
* are such that root won't be able to present a
* credential for GETATTR (ie, getroot()).
*
* We still want the mount to succeed.
*
* So we fake the attr values and mark the inode as such.
* On the first succesful traversal, we fix everything.
* The auth type test isn't quite correct, but whatever.
*/
dfprintk(VFS, "NFS: faking root inode\n");
fattr.fileid = 1;
fattr.nlink = 2; /* minimum for a dir */
fattr.type = NFDIR;
fattr.mode = S_IFDIR|S_IRUGO|S_IXUGO;
fattr.size = 4096;
fattr.du.nfs3.used = 1;
fattr.valid = NFS_ATTR_FATTR|NFS_ATTR_FATTR_V3;
} else if (error < 0) {
printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error); printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
*rooti = NULL; /* superfluous ... but safe */ return ERR_PTR(error);
return error;
} }
*rooti = nfs_fhget(sb, rootfh, &fattr); rooti = nfs_fhget(sb, rootfh, fsinfo->fattr);
if (error == -EACCES && authflavor > RPC_AUTH_MAXFLAVOR) { if (!rooti)
if (*rooti) { return ERR_PTR(-ENOMEM);
NFS_FLAGS(*rooti) |= NFS_INO_FAKE_ROOT; return rooti;
NFS_CACHEINV((*rooti));
error = 0;
}
}
return error;
} }
/* /*
...@@ -283,7 +254,7 @@ static int ...@@ -283,7 +254,7 @@ static int
nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
{ {
struct nfs_server *server; struct nfs_server *server;
struct inode *root_inode = NULL; struct inode *root_inode;
struct nfs_fattr fattr; struct nfs_fattr fattr;
struct nfs_fsinfo fsinfo = { struct nfs_fsinfo fsinfo = {
.fattr = &fattr, .fattr = &fattr,
...@@ -299,8 +270,9 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) ...@@ -299,8 +270,9 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
sb->s_magic = NFS_SUPER_MAGIC; sb->s_magic = NFS_SUPER_MAGIC;
root_inode = nfs_get_root(sb, &server->fh, &fsinfo);
/* Did getting the root inode fail? */ /* Did getting the root inode fail? */
if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0) if (IS_ERR(root_inode))
goto out_no_root; goto out_no_root;
sb->s_root = d_alloc_root(root_inode); sb->s_root = d_alloc_root(root_inode);
if (!sb->s_root) if (!sb->s_root)
...@@ -309,10 +281,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) ...@@ -309,10 +281,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
sb->s_root->d_op = server->rpc_ops->dentry_ops; sb->s_root->d_op = server->rpc_ops->dentry_ops;
/* Get some general file system info */ /* Get some general file system info */
if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) {
printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n");
goto out_no_root;
}
if (server->namelen == 0 && if (server->namelen == 0 &&
server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0)
server->namelen = pathinfo.max_namelen; server->namelen = pathinfo.max_namelen;
...@@ -368,13 +336,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) ...@@ -368,13 +336,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
return 0; return 0;
/* Yargs. It didn't work out. */ /* Yargs. It didn't work out. */
out_free_all:
if (root_inode)
iput(root_inode);
return -EINVAL;
out_no_root: out_no_root:
printk("nfs_read_super: get root inode failed\n"); printk("nfs_read_super: get root inode failed\n");
goto out_free_all; if (!IS_ERR(root_inode))
iput(root_inode);
return -EINVAL;
} }
/* /*
...@@ -402,13 +368,13 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) ...@@ -402,13 +368,13 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
/* create transport and client */ /* create transport and client */
xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP, xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP,
&server->addr, &timeparms); &server->addr, &timeparms);
if (xprt == NULL) { if (IS_ERR(xprt)) {
printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
goto out_fail; return (struct rpc_clnt *)xprt;
} }
clnt = rpc_create_client(xprt, server->hostname, &nfs_program, clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
server->rpc_ops->version, data->pseudoflavor); server->rpc_ops->version, data->pseudoflavor);
if (clnt == NULL) { if (IS_ERR(clnt)) {
printk(KERN_WARNING "NFS: cannot create RPC client.\n"); printk(KERN_WARNING "NFS: cannot create RPC client.\n");
goto out_fail; goto out_fail;
} }
...@@ -421,9 +387,8 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) ...@@ -421,9 +387,8 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
return clnt; return clnt;
out_fail: out_fail:
if (xprt) xprt_destroy(xprt);
xprt_destroy(xprt); return clnt;
return NULL;
} }
/* /*
...@@ -627,13 +592,17 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) ...@@ -627,13 +592,17 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
void void
nfs_zap_caches(struct inode *inode) nfs_zap_caches(struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
int mode = inode->i_mode;
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies; NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
invalidate_remote_inode(inode);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
NFS_CACHEINV(inode); if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
else
nfsi->flags |= NFS_INO_INVALID_ATTR;
} }
/* /*
...@@ -673,9 +642,6 @@ nfs_find_actor(struct inode *inode, void *opaque) ...@@ -673,9 +642,6 @@ nfs_find_actor(struct inode *inode, void *opaque)
return 0; return 0;
if (is_bad_inode(inode)) if (is_bad_inode(inode))
return 0; return 0;
/* Force an attribute cache update if inode->i_count == 0 */
if (!atomic_read(&inode->i_count))
NFS_CACHEINV(inode);
return 1; return 1;
} }
...@@ -729,7 +695,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -729,7 +695,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_ino = hash; inode->i_ino = hash;
/* We can't support update_atime(), since the server will reset it */ /* We can't support update_atime(), since the server will reset it */
inode->i_flags |= S_NOATIME; inode->i_flags |= S_NOATIME|S_NOCMTIME;
inode->i_mode = fattr->mode; inode->i_mode = fattr->mode;
/* Why so? Because we want revalidate for devices/FIFOs, and /* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations. * that's precisely what we have in nfs_file_inode_operations.
...@@ -754,10 +720,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -754,10 +720,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_atime = fattr->atime; inode->i_atime = fattr->atime;
inode->i_mtime = fattr->mtime; inode->i_mtime = fattr->mtime;
inode->i_ctime = fattr->ctime; inode->i_ctime = fattr->ctime;
nfsi->read_cache_ctime = fattr->ctime;
nfsi->read_cache_mtime = fattr->mtime;
nfsi->cache_mtime_jiffies = fattr->timestamp;
nfsi->read_cache_isize = fattr->size;
if (fattr->valid & NFS_ATTR_FATTR_V4) if (fattr->valid & NFS_ATTR_FATTR_V4)
nfsi->change_attr = fattr->change_attr; nfsi->change_attr = fattr->change_attr;
inode->i_size = nfs_size_to_loff_t(fattr->size); inode->i_size = nfs_size_to_loff_t(fattr->size);
...@@ -804,70 +766,50 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -804,70 +766,50 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
struct nfs_fattr fattr; struct nfs_fattr fattr;
int error; int error;
if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
attr->ia_valid &= ~ATTR_SIZE;
}
/* Optimization: if the end result is no change, don't RPC */ /* Optimization: if the end result is no change, don't RPC */
attr->ia_valid &= NFS_VALID_ATTRS; attr->ia_valid &= NFS_VALID_ATTRS;
if (attr->ia_valid == 0) if (attr->ia_valid == 0)
return 0; return 0;
lock_kernel(); lock_kernel();
nfs_begin_data_update(inode);
/* /* Write all dirty data if we're changing file permissions or size */
* Make sure the inode is up-to-date. if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) {
*/ if (filemap_fdatawrite(inode->i_mapping) == 0)
error = nfs_revalidate_inode(NFS_SERVER(inode),inode); filemap_fdatawait(inode->i_mapping);
if (error) { nfs_wb_all(inode);
#ifdef NFS_PARANOIA
printk("nfs_setattr: revalidate failed, error=%d\n", error);
#endif
goto out;
}
if (!S_ISREG(inode->i_mode)) {
attr->ia_valid &= ~ATTR_SIZE;
if (attr->ia_valid == 0)
goto out;
} else {
filemap_fdatawrite(inode->i_mapping);
error = nfs_wb_all(inode);
filemap_fdatawait(inode->i_mapping);
if (error)
goto out;
/* Optimize away unnecessary truncates */
if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size)
attr->ia_valid &= ~ATTR_SIZE;
} }
if (!attr->ia_valid)
goto out;
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
if (error) if (error == 0) {
goto out; nfs_refresh_inode(inode, &fattr);
/* if ((attr->ia_valid & ATTR_MODE) != 0) {
* If we changed the size or mtime, update the inode int mode;
* now to avoid invalidating the page cache. mode = inode->i_mode & ~S_IALLUGO;
*/ mode |= attr->ia_mode & S_IALLUGO;
if (attr->ia_valid & ATTR_SIZE) { inode->i_mode = mode;
if (attr->ia_size != fattr.size) }
printk("nfs_setattr: attr=%Ld, fattr=%Ld??\n", if ((attr->ia_valid & ATTR_UID) != 0)
(long long) attr->ia_size, (long long)fattr.size); inode->i_uid = attr->ia_uid;
vmtruncate(inode, attr->ia_size); if ((attr->ia_valid & ATTR_GID) != 0)
inode->i_gid = attr->ia_gid;
if ((attr->ia_valid & ATTR_SIZE) != 0) {
inode->i_size = attr->ia_size;
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 we changed the size or mtime, update the inode if (*cred) {
* now to avoid invalidating the page cache. put_rpccred(*cred);
*/ *cred = NULL;
if (!(fattr.valid & NFS_ATTR_WCC)) { }
struct nfs_inode *nfsi = NFS_I(inode);
fattr.pre_size = nfsi->read_cache_isize;
fattr.pre_mtime = nfsi->read_cache_mtime;
fattr.pre_ctime = nfsi->read_cache_ctime;
fattr.valid |= NFS_ATTR_WCC;
} }
/* Force an attribute cache update */ nfs_end_data_update(inode);
NFS_CACHEINV(inode);
error = nfs_refresh_inode(inode, &fattr);
out:
unlock_kernel(); unlock_kernel();
return error; return error;
} }
...@@ -895,7 +837,19 @@ nfs_wait_on_inode(struct inode *inode, int flag) ...@@ -895,7 +837,19 @@ nfs_wait_on_inode(struct inode *inode, int flag)
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int err = nfs_revalidate_inode(NFS_SERVER(inode), inode); struct nfs_inode *nfsi = NFS_I(inode);
int need_atime = nfsi->flags & NFS_INO_INVALID_ATIME;
int err;
if (__IS_FLG(inode, MS_NOATIME))
need_atime = 0;
else if (__IS_FLG(inode, MS_NODIRATIME) && S_ISDIR(inode->i_mode))
need_atime = 0;
/* We may force a getattr if the user cares about atime */
if (need_atime)
err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
else
err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (!err) if (!err)
generic_fillattr(inode, stat); generic_fillattr(inode, stat);
return err; return err;
...@@ -930,8 +884,10 @@ int nfs_open(struct inode *inode, struct file *filp) ...@@ -930,8 +884,10 @@ int nfs_open(struct inode *inode, struct file *filp)
auth = NFS_CLIENT(inode)->cl_auth; auth = NFS_CLIENT(inode)->cl_auth;
cred = rpcauth_lookupcred(auth, 0); cred = rpcauth_lookupcred(auth, 0);
filp->private_data = cred; filp->private_data = cred;
if (filp->f_mode & FMODE_WRITE) if ((filp->f_mode & FMODE_WRITE) != 0) {
nfs_set_mmcred(inode, cred); nfs_set_mmcred(inode, cred);
nfs_begin_data_update(inode);
}
return 0; return 0;
} }
...@@ -940,6 +896,8 @@ int nfs_release(struct inode *inode, struct file *filp) ...@@ -940,6 +896,8 @@ int nfs_release(struct inode *inode, struct file *filp)
struct rpc_cred *cred; struct rpc_cred *cred;
lock_kernel(); lock_kernel();
if ((filp->f_mode & FMODE_WRITE) != 0)
nfs_end_data_update(inode);
cred = nfs_file_cred(filp); cred = nfs_file_cred(filp);
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
...@@ -956,6 +914,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -956,6 +914,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{ {
int status = -ESTALE; int status = -ESTALE;
struct nfs_fattr fattr; struct nfs_fattr fattr;
struct nfs_inode *nfsi = NFS_I(inode);
unsigned long verifier;
unsigned int flags;
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode)); inode->i_sb->s_id, (long long)NFS_FILEID(inode));
...@@ -965,23 +926,22 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -965,23 +926,22 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out_nowait; goto out_nowait;
if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode) if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
goto out_nowait; goto out_nowait;
if (NFS_FAKE_ROOT(inode)) {
dfprintk(VFS, "NFS: not revalidating fake root\n");
status = 0;
goto out_nowait;
}
while (NFS_REVALIDATING(inode)) { while (NFS_REVALIDATING(inode)) {
status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING); status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
if (status < 0) if (status < 0)
goto out_nowait; goto out_nowait;
if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) { if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOAC)
status = NFS_STALE(inode) ? -ESTALE : 0; continue;
goto out_nowait; if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
} continue;
status = NFS_STALE(inode) ? -ESTALE : 0;
goto out_nowait;
} }
NFS_FLAGS(inode) |= NFS_INO_REVALIDATING; NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
/* Protect against RPC races by saving the change attribute */
verifier = nfs_save_change_attribute(inode);
status = NFS_PROTO(inode)->getattr(inode, &fattr); status = NFS_PROTO(inode)->getattr(inode, &fattr);
if (status) { if (status) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
...@@ -995,13 +955,36 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -995,13 +955,36 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out; goto out;
} }
status = nfs_refresh_inode(inode, &fattr); status = nfs_update_inode(inode, &fattr, verifier);
if (status) { if (status) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status); (long long)NFS_FILEID(inode), status);
goto out; goto out;
} }
flags = nfsi->flags;
/*
* We may need to keep the attributes marked as invalid if
* we raced with nfs_end_attr_update().
*/
if (verifier == nfsi->cache_change_attribute)
nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
/* Do the page cache invalidation */
if (flags & NFS_INO_INVALID_DATA) {
if (S_ISREG(inode->i_mode)) {
if (filemap_fdatawrite(inode->i_mapping) == 0)
filemap_fdatawait(inode->i_mapping);
nfs_wb_all(inode);
}
nfsi->flags &= ~NFS_INO_INVALID_DATA;
invalidate_inode_pages2(inode->i_mapping);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
/* This ensures we revalidate dentries */
nfsi->cache_change_attribute++;
}
dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n", dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode)); (long long)NFS_FILEID(inode));
...@@ -1009,41 +992,107 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -1009,41 +992,107 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
NFS_FLAGS(inode) &= ~NFS_INO_STALE; NFS_FLAGS(inode) &= ~NFS_INO_STALE;
out: out:
NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING; NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
wake_up(&NFS_I(inode)->nfs_i_wait); wake_up(&nfsi->nfs_i_wait);
out_nowait: out_nowait:
unlock_kernel(); unlock_kernel();
return status; return status;
} }
/* /**
* nfs_fattr_obsolete - Test if attribute data is newer than cached data * nfs_begin_data_update
* @inode: inode * @inode - pointer to inode
* @fattr: attributes to test * Declare that a set of operations will update file data on the server
*/
void nfs_begin_data_update(struct inode *inode)
{
atomic_inc(&NFS_I(inode)->data_updates);
}
/**
* nfs_end_data_update
* @inode - pointer to inode
* Declare end of the operations that will update file data
*/
void nfs_end_data_update(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
/* Mark the attribute cache for revalidation */
nfsi->flags |= NFS_INO_INVALID_ATTR;
/* Directories and symlinks: invalidate page cache too */
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
nfsi->flags |= NFS_INO_INVALID_DATA;
nfsi->cache_change_attribute ++;
atomic_dec(&nfsi->data_updates);
}
/**
* nfs_refresh_inode - verify consistency of the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
* *
* Avoid stuffing the attribute cache with obsolete information. * Verifies the attribute cache. If we have just changed the attributes,
* We always accept updates if the attribute cache timed out, or if * so that fattr carries weak cache consistency data, then it may
* fattr->ctime is newer than our cached value. * also update the ctime/mtime/change_attribute.
* If fattr->ctime matches the cached value, we still accept the update
* if it increases the file size.
*/ */
static inline int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
long cdif; loff_t cur_size, new_isize;
int data_unstable;
if (time_after(jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo))
goto out_valid; /* Are we in the process of updating data on the server? */
cdif = fattr->ctime.tv_sec - nfsi->read_cache_ctime.tv_sec; data_unstable = nfs_caches_unstable(inode);
if (cdif == 0)
cdif = fattr->ctime.tv_nsec - nfsi->read_cache_ctime.tv_nsec; if (fattr->valid & NFS_ATTR_FATTR_V4) {
if (cdif > 0) if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0
goto out_valid; && nfsi->change_attr == fattr->pre_change_attr)
/* Ugh... */ nfsi->change_attr = fattr->change_attr;
if (cdif == 0 && fattr->size > nfsi->read_cache_isize) if (!data_unstable && nfsi->change_attr != fattr->change_attr)
goto out_valid; nfsi->flags |= NFS_INO_INVALID_ATTR;
return -1; }
out_valid:
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
return 0;
/* Has the inode gone and changed behind our back? */
if (nfsi->fileid != fattr->fileid
|| (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
return -EIO;
cur_size = i_size_read(inode);
new_isize = nfs_size_to_loff_t(fattr->size);
/* If we have atomic WCC data, we may update some attributes */
if ((fattr->valid & NFS_ATTR_WCC) != 0) {
if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime))
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime))
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
}
/* Verify a few of the more important attributes */
if (!data_unstable) {
if (!timespec_equal(&inode->i_mtime, &fattr->mtime)
|| cur_size != new_isize)
nfsi->flags |= NFS_INO_INVALID_ATTR;
} else if (S_ISREG(inode->i_mode) && new_isize > cur_size)
nfsi->flags |= NFS_INO_INVALID_ATTR;
/* Have any file permissions changed? */
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;
/* Has the link count changed? */
if (inode->i_nlink != fattr->nlink)
nfsi->flags |= NFS_INO_INVALID_ATTR;
if (!timespec_equal(&inode->i_atime, &fattr->atime))
nfsi->flags |= NFS_INO_INVALID_ATIME;
nfsi->read_cache_jiffies = fattr->timestamp;
return 0; return 0;
} }
...@@ -1059,65 +1108,66 @@ int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1059,65 +1108,66 @@ int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
* *
* A very similar scenario holds for the dir cache. * A very similar scenario holds for the dir cache.
*/ */
int static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier)
__nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
__u64 new_size; __u64 new_size;
loff_t new_isize; loff_t new_isize;
int invalid = 0; unsigned int invalid = 0;
int mtime_update = 0;
loff_t cur_isize; loff_t cur_isize;
int data_unstable;
dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n", dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
inode->i_sb->s_id, inode->i_ino, __FUNCTION__, inode->i_sb->s_id, inode->i_ino,
atomic_read(&inode->i_count), fattr->valid); atomic_read(&inode->i_count), fattr->valid);
/* First successful call after mount, fill real data. */ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
if (NFS_FAKE_ROOT(inode)) { return 0;
dfprintk(VFS, "NFS: updating fake root\n");
nfsi->fileid = fattr->fileid;
NFS_FLAGS(inode) &= ~NFS_INO_FAKE_ROOT;
}
if (nfsi->fileid != fattr->fileid) { if (nfsi->fileid != fattr->fileid) {
printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n" printk(KERN_ERR "%s: inode number mismatch\n"
"expected (%s/0x%Lx), got (%s/0x%Lx)\n", "expected (%s/0x%Lx), got (%s/0x%Lx)\n",
__FUNCTION__,
inode->i_sb->s_id, (long long)nfsi->fileid, inode->i_sb->s_id, (long long)nfsi->fileid,
inode->i_sb->s_id, (long long)fattr->fileid); inode->i_sb->s_id, (long long)fattr->fileid);
goto out_err; goto out_err;
} }
/* Throw out obsolete READDIRPLUS attributes */
if (time_before(fattr->timestamp, NFS_READTIME(inode)))
return 0;
/* /*
* Make sure the inode's type hasn't changed. * Make sure the inode's type hasn't changed.
*/ */
if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
goto out_changed; goto out_changed;
new_size = fattr->size;
new_isize = nfs_size_to_loff_t(fattr->size);
/* Avoid races */
if (nfs_fattr_obsolete(inode, fattr))
goto out_nochange;
/* /*
* Update the read time so we don't revalidate too often. * Update the read time so we don't revalidate too often.
*/ */
nfsi->read_cache_jiffies = fattr->timestamp; nfsi->read_cache_jiffies = fattr->timestamp;
/* /* Are we racing with known updates of the metadata on the server? */
* Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache. data_unstable = ! nfs_verify_change_attribute(inode, verifier);
* NOT inode->i_size!!!
*/ /* Check if the file size agrees */
if (nfsi->read_cache_isize != new_size) { new_size = fattr->size;
new_isize = nfs_size_to_loff_t(fattr->size);
cur_isize = i_size_read(inode);
if (cur_isize != new_size) {
#ifdef NFS_DEBUG_VERBOSE #ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
#endif #endif
invalid = 1; /*
* If we have pending writebacks, things can get
* messy.
*/
if (S_ISREG(inode->i_mode) && data_unstable) {
if (new_isize > cur_isize) {
inode->i_size = new_isize;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
} else {
inode->i_size = new_isize;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
} }
/* /*
...@@ -1125,12 +1175,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1125,12 +1175,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
* can change this value in VFS without requiring a * can change this value in VFS without requiring a
* cache revalidation. * cache revalidation.
*/ */
if (!timespec_equal(&nfsi->read_cache_mtime, &fattr->mtime)) { if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
#ifdef NFS_DEBUG_VERBOSE #ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
#endif #endif
invalid = 1; if (!data_unstable)
mtime_update = 1; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
} }
if ((fattr->valid & NFS_ATTR_FATTR_V4) if ((fattr->valid & NFS_ATTR_FATTR_V4)
...@@ -1139,47 +1190,15 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1139,47 +1190,15 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n", printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n",
inode->i_sb->s_id, inode->i_ino); inode->i_sb->s_id, inode->i_ino);
#endif #endif
invalid = 1; nfsi->change_attr = fattr->change_attr;
} if (!data_unstable)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
/* Check Weak Cache Consistency data.
* If size and mtime match the pre-operation values, we can
* assume that any attribute changes were caused by our NFS
* operation, so there's no need to invalidate the caches.
*/
if ((fattr->valid & NFS_ATTR_PRE_CHANGE)
&& nfsi->change_attr == fattr->pre_change_attr) {
invalid = 0;
}
else if ((fattr->valid & NFS_ATTR_WCC)
&& nfsi->read_cache_isize == fattr->pre_size
&& timespec_equal(&nfsi->read_cache_mtime, &fattr->pre_mtime)) {
invalid = 0;
}
/*
* If we have pending writebacks, things can get
* messy.
*/
cur_isize = i_size_read(inode);
if (nfs_have_writebacks(inode) && new_isize < cur_isize)
new_isize = cur_isize;
nfsi->read_cache_ctime = fattr->ctime;
inode->i_ctime = fattr->ctime;
inode->i_atime = fattr->atime;
if (mtime_update) {
if (invalid)
nfsi->cache_mtime_jiffies = fattr->timestamp;
nfsi->read_cache_mtime = fattr->mtime;
inode->i_mtime = fattr->mtime;
} }
nfsi->read_cache_isize = new_size; memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
i_size_write(inode, new_isize); memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
if (inode->i_mode != fattr->mode || if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) ||
inode->i_uid != fattr->uid || inode->i_uid != fattr->uid ||
inode->i_gid != fattr->gid) { inode->i_gid != fattr->gid) {
struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
...@@ -1187,11 +1206,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1187,11 +1206,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
put_rpccred(*cred); put_rpccred(*cred);
*cred = NULL; *cred = NULL;
} }
invalid |= NFS_INO_INVALID_ATTR;
} }
if (fattr->valid & NFS_ATTR_FATTR_V4)
nfsi->change_attr = fattr->change_attr;
inode->i_mode = fattr->mode; inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink; inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid; inode->i_uid = fattr->uid;
...@@ -1207,31 +1224,30 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1207,31 +1224,30 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_blocks = fattr->du.nfs2.blocks; inode->i_blocks = fattr->du.nfs2.blocks;
inode->i_blksize = fattr->du.nfs2.blocksize; inode->i_blksize = fattr->du.nfs2.blocksize;
} }
/* Update attrtimeo value */ /* Update attrtimeo value if we're out of the unstable period */
if (invalid) { if (invalid & NFS_INO_INVALID_ATTR) {
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
invalidate_remote_inode(inode);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
} else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) { } else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
} }
/* Don't invalidate the data if we were to blame */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode)))
invalid &= ~NFS_INO_INVALID_DATA;
nfsi->flags |= invalid;
return 0; return 0;
out_nochange:
if (!timespec_equal(&fattr->atime, &inode->i_atime))
inode->i_atime = fattr->atime;
return 0;
out_changed: out_changed:
/* /*
* Big trouble! The inode has become a different object. * Big trouble! The inode has become a different object.
*/ */
#ifdef NFS_PARANOIA #ifdef NFS_PARANOIA
printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n", printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n",
inode->i_ino, inode->i_mode, fattr->mode); __FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
#endif #endif
/* /*
* No need to worry about unhashing the dentry, as the * No need to worry about unhashing the dentry, as the
...@@ -1391,8 +1407,8 @@ static void nfs4_clear_inode(struct inode *inode) ...@@ -1391,8 +1407,8 @@ static void nfs4_clear_inode(struct inode *inode)
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode), (long long)NFS_FILEID(inode),
state); state);
list_del(&state->inode_states); BUG_ON(atomic_read(&state->count) != 1);
nfs4_put_open_state(state); nfs4_close_state(state, state->state);
} }
/* Now call standard NFS clear_inode() code */ /* Now call standard NFS clear_inode() code */
nfs_clear_inode(inode); nfs_clear_inode(inode);
...@@ -1472,17 +1488,19 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, ...@@ -1472,17 +1488,19 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
down_write(&clp->cl_sem); down_write(&clp->cl_sem);
if (clp->cl_rpcclient == NULL) { if (clp->cl_rpcclient == NULL) {
xprt = xprt_create_proto(proto, &server->addr, &timeparms); xprt = xprt_create_proto(proto, &server->addr, &timeparms);
if (xprt == NULL) { if (IS_ERR(xprt)) {
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
err = PTR_ERR(xprt);
goto out_fail; goto out_fail;
} }
clnt = rpc_create_client(xprt, server->hostname, &nfs_program, clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
server->rpc_ops->version, authflavour); server->rpc_ops->version, authflavour);
if (clnt == NULL) { if (IS_ERR(clnt)) {
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
printk(KERN_WARNING "NFS: cannot create RPC client.\n"); printk(KERN_WARNING "NFS: cannot create RPC client.\n");
xprt_destroy(xprt); xprt_destroy(xprt);
err = PTR_ERR(clnt);
goto out_fail; goto out_fail;
} }
clnt->cl_chatty = 1; clnt->cl_chatty = 1;
...@@ -1495,14 +1513,17 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, ...@@ -1495,14 +1513,17 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clear_bit(NFS4CLNT_OK, &clp->cl_state); clear_bit(NFS4CLNT_OK, &clp->cl_state);
list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
clnt = rpc_clone_client(clp->cl_rpcclient); clnt = rpc_clone_client(clp->cl_rpcclient);
server->nfs4_state = clp; if (!IS_ERR(clnt))
server->nfs4_state = clp;
up_write(&clp->cl_sem); up_write(&clp->cl_sem);
clp = NULL; clp = NULL;
if (clnt == NULL) { if (IS_ERR(clnt)) {
printk(KERN_WARNING "NFS: cannot create RPC client.\n"); printk(KERN_WARNING "NFS: cannot create RPC client.\n");
err = PTR_ERR(clnt);
goto out_remove_list; goto out_remove_list;
} }
err = -ENOMEM;
if (server->nfs4_state->cl_idmap == NULL) { if (server->nfs4_state->cl_idmap == NULL) {
printk(KERN_WARNING "NFS: failed to create idmapper.\n"); printk(KERN_WARNING "NFS: failed to create idmapper.\n");
goto out_shutdown; goto out_shutdown;
...@@ -1601,7 +1622,7 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, ...@@ -1601,7 +1622,7 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
if (data->version != NFS4_MOUNT_VERSION) { if (data->version != NFS4_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n", printk("nfs warning: mount version %s than kernel\n",
data->version < NFS_MOUNT_VERSION ? "older" : "newer"); data->version < NFS4_MOUNT_VERSION ? "older" : "newer");
} }
p = nfs_copy_user_string(NULL, &data->hostname, 256); p = nfs_copy_user_string(NULL, &data->hostname, 256);
...@@ -1718,6 +1739,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ...@@ -1718,6 +1739,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit); INIT_LIST_HEAD(&nfsi->commit);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
atomic_set(&nfsi->data_updates, 0);
nfsi->ndirty = 0; nfsi->ndirty = 0;
nfsi->ncommit = 0; nfsi->ncommit = 0;
nfsi->npages = 0; nfsi->npages = 0;
......
...@@ -57,8 +57,9 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh, ...@@ -57,8 +57,9 @@ nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh,
(unsigned)ntohl(addr->sin_addr.s_addr), path); (unsigned)ntohl(addr->sin_addr.s_addr), path);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr->sin_addr.s_addr)); sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr->sin_addr.s_addr));
if (!(mnt_clnt = mnt_create(hostname, addr, version, protocol))) mnt_clnt = mnt_create(hostname, addr, version, protocol);
return -EACCES; if (IS_ERR(mnt_clnt))
return PTR_ERR(mnt_clnt);
call = (version == NFS_MNT3_VERSION) ? MOUNTPROC3_MNT : MNTPROC_MNT; call = (version == NFS_MNT3_VERSION) ? MOUNTPROC3_MNT : MNTPROC_MNT;
status = rpc_call(mnt_clnt, call, path, &result, 0); status = rpc_call(mnt_clnt, call, path, &result, 0);
...@@ -72,13 +73,14 @@ mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version, ...@@ -72,13 +73,14 @@ mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version,
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
if (!(xprt = xprt_create_proto(protocol, srvaddr, NULL))) xprt = xprt_create_proto(protocol, srvaddr, NULL);
return NULL; if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
clnt = rpc_create_client(xprt, hostname, clnt = rpc_create_client(xprt, hostname,
&mnt_program, version, &mnt_program, version,
RPC_AUTH_NULL); RPC_AUTH_UNIX);
if (!clnt) { if (IS_ERR(clnt)) {
xprt_destroy(xprt); xprt_destroy(xprt);
} else { } else {
clnt->cl_softrtry = 1; clnt->cl_softrtry = 1;
......
...@@ -36,33 +36,33 @@ extern int nfs_stat_to_errno(int stat); ...@@ -36,33 +36,33 @@ extern int nfs_stat_to_errno(int stat);
* Declare the space requirements for NFS arguments and replies as * Declare the space requirements for NFS arguments and replies as
* number of 32bit-words * number of 32bit-words
*/ */
#define NFS_fhandle_sz 8 #define NFS_fhandle_sz (8)
#define NFS_sattr_sz 8 #define NFS_sattr_sz (8)
#define NFS_filename_sz 1+(NFS2_MAXNAMLEN>>2) #define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2))
#define NFS_path_sz 1+(NFS2_MAXPATHLEN>>2) #define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2))
#define NFS_fattr_sz 17 #define NFS_fattr_sz (17)
#define NFS_info_sz 5 #define NFS_info_sz (5)
#define NFS_entry_sz NFS_filename_sz+3 #define NFS_entry_sz (NFS_filename_sz+3)
#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz #define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz)
#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz #define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz)
#define NFS_readlinkargs_sz NFS_fhandle_sz #define NFS_readlinkargs_sz (NFS_fhandle_sz)
#define NFS_readargs_sz NFS_fhandle_sz+3 #define NFS_readargs_sz (NFS_fhandle_sz+3)
#define NFS_writeargs_sz NFS_fhandle_sz+4 #define NFS_writeargs_sz (NFS_fhandle_sz+4)
#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz #define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz)
#define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz #define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz)
#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz #define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz)
#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz #define NFS_symlinkargs_sz (NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz)
#define NFS_readdirargs_sz NFS_fhandle_sz+2 #define NFS_readdirargs_sz (NFS_fhandle_sz+2)
#define NFS_attrstat_sz 1+NFS_fattr_sz #define NFS_attrstat_sz (1+NFS_fattr_sz)
#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz #define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz)
#define NFS_readlinkres_sz 1 #define NFS_readlinkres_sz (1)
#define NFS_readres_sz 1+NFS_fattr_sz+1 #define NFS_readres_sz (1+NFS_fattr_sz+1)
#define NFS_writeres_sz NFS_attrstat_sz #define NFS_writeres_sz (NFS_attrstat_sz)
#define NFS_stat_sz 1 #define NFS_stat_sz (1)
#define NFS_readdirres_sz 1 #define NFS_readdirres_sz (1)
#define NFS_statfsres_sz 1+NFS_info_sz #define NFS_statfsres_sz (1+NFS_info_sz)
/* /*
* Common NFS XDR functions as inlines * Common NFS XDR functions as inlines
......
...@@ -68,20 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task) ...@@ -68,20 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task)
return 1; return 1;
} }
static void
nfs3_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
if (fattr->valid & NFS_ATTR_FATTR) {
if (!(fattr->valid & NFS_ATTR_WCC)) {
fattr->pre_size = NFS_CACHE_ISIZE(inode);
fattr->pre_mtime = NFS_CACHE_MTIME(inode);
fattr->pre_ctime = NFS_CACHE_CTIME(inode);
fattr->valid |= NFS_ATTR_WCC;
}
nfs_refresh_inode(inode, fattr);
}
}
static struct rpc_cred * static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp) nfs_cred(struct inode *inode, struct file *filp)
{ {
...@@ -99,14 +85,18 @@ nfs_cred(struct inode *inode, struct file *filp) ...@@ -99,14 +85,18 @@ nfs_cred(struct inode *inode, struct file *filp)
*/ */
static int static int
nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr) struct nfs_fsinfo *info)
{ {
int status; int status;
dprintk("NFS call getroot\n"); dprintk("%s: call fsinfo\n", __FUNCTION__);
fattr->valid = 0; info->fattr->valid = 0;
status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0); status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
dprintk("NFS reply getroot\n"); 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);
}
return status; return status;
} }
...@@ -280,7 +270,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -280,7 +270,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0) if (status >= 0)
nfs3_write_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
dprintk("NFS reply write: %d\n", status); dprintk("NFS reply write: %d\n", status);
return status < 0? status : wdata->res.count; return status < 0? status : wdata->res.count;
} }
...@@ -303,7 +293,7 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp) ...@@ -303,7 +293,7 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status >= 0) if (status >= 0)
nfs3_write_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
dprintk("NFS reply commit: %d\n", status); dprintk("NFS reply commit: %d\n", status);
return status; return status;
} }
...@@ -777,12 +767,13 @@ nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count) ...@@ -777,12 +767,13 @@ nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count)
static void static void
nfs3_write_done(struct rpc_task *task) nfs3_write_done(struct rpc_task *task)
{ {
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_write_data *data;
if (nfs3_async_handle_jukebox(task)) if (nfs3_async_handle_jukebox(task))
return; return;
data = (struct nfs_write_data *)task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0)
nfs3_write_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
nfs_writeback_done(task); nfs_writeback_done(task);
} }
...@@ -835,12 +826,13 @@ nfs3_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) ...@@ -835,12 +826,13 @@ nfs3_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
static void static void
nfs3_commit_done(struct rpc_task *task) nfs3_commit_done(struct rpc_task *task)
{ {
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_write_data *data;
if (nfs3_async_handle_jukebox(task)) if (nfs3_async_handle_jukebox(task))
return; return;
data = (struct nfs_write_data *)task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0)
nfs3_write_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
nfs_commit_done(task); nfs_commit_done(task);
} }
......
...@@ -33,51 +33,51 @@ extern int nfs_stat_to_errno(int); ...@@ -33,51 +33,51 @@ extern int nfs_stat_to_errno(int);
* Declare the space requirements for NFS arguments and replies as * Declare the space requirements for NFS arguments and replies as
* number of 32bit-words * number of 32bit-words
*/ */
#define NFS3_fhandle_sz 1+16 #define NFS3_fhandle_sz (1+16)
#define NFS3_fh_sz NFS3_fhandle_sz /* shorthand */ #define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */
#define NFS3_sattr_sz 15 #define NFS3_sattr_sz (15)
#define NFS3_filename_sz 1+(NFS3_MAXNAMLEN>>2) #define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2))
#define NFS3_path_sz 1+(NFS3_MAXPATHLEN>>2) #define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2))
#define NFS3_fattr_sz 21 #define NFS3_fattr_sz (21)
#define NFS3_wcc_attr_sz 6 #define NFS3_wcc_attr_sz (6)
#define NFS3_pre_op_attr_sz 1+NFS3_wcc_attr_sz #define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz)
#define NFS3_post_op_attr_sz 1+NFS3_fattr_sz #define NFS3_post_op_attr_sz (1+NFS3_fattr_sz)
#define NFS3_wcc_data_sz NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz #define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
#define NFS3_fsstat_sz #define NFS3_fsstat_sz
#define NFS3_fsinfo_sz #define NFS3_fsinfo_sz
#define NFS3_pathconf_sz #define NFS3_pathconf_sz
#define NFS3_entry_sz NFS3_filename_sz+3 #define NFS3_entry_sz (NFS3_filename_sz+3)
#define NFS3_sattrargs_sz NFS3_fh_sz+NFS3_sattr_sz+3 #define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3)
#define NFS3_diropargs_sz NFS3_fh_sz+NFS3_filename_sz #define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz)
#define NFS3_accessargs_sz NFS3_fh_sz+1 #define NFS3_accessargs_sz (NFS3_fh_sz+1)
#define NFS3_readlinkargs_sz NFS3_fh_sz #define NFS3_readlinkargs_sz (NFS3_fh_sz)
#define NFS3_readargs_sz NFS3_fh_sz+3 #define NFS3_readargs_sz (NFS3_fh_sz+3)
#define NFS3_writeargs_sz NFS3_fh_sz+5 #define NFS3_writeargs_sz (NFS3_fh_sz+5)
#define NFS3_createargs_sz NFS3_diropargs_sz+NFS3_sattr_sz #define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
#define NFS3_mkdirargs_sz NFS3_diropargs_sz+NFS3_sattr_sz #define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz)
#define NFS3_symlinkargs_sz NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz #define NFS3_symlinkargs_sz (NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz)
#define NFS3_mknodargs_sz NFS3_diropargs_sz+2+NFS3_sattr_sz #define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz)
#define NFS3_renameargs_sz NFS3_diropargs_sz+NFS3_diropargs_sz #define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz)
#define NFS3_linkargs_sz NFS3_fh_sz+NFS3_diropargs_sz #define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz)
#define NFS3_readdirargs_sz NFS3_fh_sz+2 #define NFS3_readdirargs_sz (NFS3_fh_sz+2)
#define NFS3_commitargs_sz NFS3_fh_sz+3 #define NFS3_commitargs_sz (NFS3_fh_sz+3)
#define NFS3_attrstat_sz 1+NFS3_fattr_sz #define NFS3_attrstat_sz (1+NFS3_fattr_sz)
#define NFS3_wccstat_sz 1+NFS3_wcc_data_sz #define NFS3_wccstat_sz (1+NFS3_wcc_data_sz)
#define NFS3_lookupres_sz 1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz) #define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
#define NFS3_accessres_sz 1+NFS3_post_op_attr_sz+1 #define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1)
#define NFS3_readlinkres_sz 1+NFS3_post_op_attr_sz #define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz)
#define NFS3_readres_sz 1+NFS3_post_op_attr_sz+3 #define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3)
#define NFS3_writeres_sz 1+NFS3_wcc_data_sz+4 #define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4)
#define NFS3_createres_sz 1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz #define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
#define NFS3_renameres_sz 1+(2 * NFS3_wcc_data_sz) #define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz))
#define NFS3_linkres_sz 1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz #define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz)
#define NFS3_readdirres_sz 1+NFS3_post_op_attr_sz+2 #define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2)
#define NFS3_fsstatres_sz 1+NFS3_post_op_attr_sz+13 #define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13)
#define NFS3_fsinfores_sz 1+NFS3_post_op_attr_sz+12 #define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12)
#define NFS3_pathconfres_sz 1+NFS3_post_op_attr_sz+6 #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6)
#define NFS3_commitres_sz 1+NFS3_wcc_data_sz+2 #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2)
/* /*
* Map file type to S_IFMT bits * Map file type to S_IFMT bits
...@@ -103,9 +103,7 @@ static struct { ...@@ -103,9 +103,7 @@ static struct {
static inline u32 * static inline u32 *
xdr_encode_fhandle(u32 *p, struct nfs_fh *fh) xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
{ {
*p++ = htonl(fh->size); return xdr_encode_array(p, fh->data, fh->size);
memcpy(p, fh->data, fh->size);
return p + XDR_QUADLEN(fh->size);
} }
static inline u32 * static inline u32 *
......
...@@ -54,12 +54,24 @@ ...@@ -54,12 +54,24 @@
#define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name #define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name
#define OPNUM(cp) cp->ops[cp->req_nops].opnum #define OPNUM(cp) cp->ops[cp->req_nops].opnum
static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *); static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *);
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[]; extern struct rpc_procinfo nfs4_procedures[];
extern nfs4_stateid zero_stateid; extern nfs4_stateid zero_stateid;
/* Prevent leaks of NFSv4 errors into userland */
static inline int nfs4_map_errors(int err)
{
if (err < -1000) {
printk(KERN_WARNING "%s could not handle NFSv4 error %d\n",
__FUNCTION__, -err);
return -EIO;
}
return err;
}
static void static void
nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops,
struct nfs_server *server, char *tag) struct nfs_server *server, char *tag)
...@@ -505,6 +517,8 @@ nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) ...@@ -505,6 +517,8 @@ nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
status = rpc_call_sync(server->client, &msg, 0); status = rpc_call_sync(server->client, &msg, 0);
nfs4_increment_seqid(status, sp); nfs4_increment_seqid(status, sp);
if (status == 0)
memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid));
/* Update the inode attributes */ /* Update the inode attributes */
nfs_refresh_inode(inode, &fattr); nfs_refresh_inode(inode, &fattr);
return status; return status;
...@@ -689,12 +703,12 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, ...@@ -689,12 +703,12 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
retry: retry:
fattr->valid = 0; fattr->valid = 0;
if (state) if (sattr->ia_valid & ATTR_SIZE)
nfs4_copy_stateid(&arg.stateid, state, 0); nfs4_copy_stateid(&arg.stateid, state, 0);
else else
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
status = rpc_call_sync(server->client, &msg, 0); status = rpc_call_sync(server->client, &msg, 0);
if (status) { if (status) {
status = nfs4_handle_error(server, status); status = nfs4_handle_error(server, status);
if (!status) if (!status)
...@@ -822,10 +836,11 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) ...@@ -822,10 +836,11 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
static int static int
nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr) struct nfs_fsinfo *info)
{ {
struct nfs4_compound compound; struct nfs4_compound compound;
struct nfs4_op ops[4]; struct nfs4_op ops[4];
struct nfs_fattr * fattr = info->fattr;
unsigned char * p; unsigned char * p;
struct qstr q; struct qstr q;
int status; int status;
...@@ -869,7 +884,9 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -869,7 +884,9 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
break; break;
} }
out: out:
return status; if (status)
return nfs4_map_errors(status);
return nfs4_proc_fsinfo(server, fhandle, info);
} }
static int static int
...@@ -883,7 +900,7 @@ nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) ...@@ -883,7 +900,7 @@ nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "getattr"); nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "getattr");
nfs4_setup_putfh(&compound, NFS_FH(inode)); nfs4_setup_putfh(&compound, NFS_FH(inode));
nfs4_setup_getattr(&compound, fattr); nfs4_setup_getattr(&compound, fattr);
return nfs4_call_compound(&compound, NULL, 0); return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0));
} }
/* /*
...@@ -969,7 +986,7 @@ nfs4_proc_lookup(struct inode *dir, struct qstr *name, ...@@ -969,7 +986,7 @@ nfs4_proc_lookup(struct inode *dir, struct qstr *name,
if (status >= 0) if (status >= 0)
status = nfs_refresh_inode(dir, &dir_attr); status = nfs_refresh_inode(dir, &dir_attr);
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1016,7 +1033,7 @@ nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) ...@@ -1016,7 +1033,7 @@ nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
else if (req_access != resp_access) else if (req_access != resp_access)
status = -EACCES; status = -EACCES;
} }
return status; return nfs4_map_errors(status);
} }
/* /*
...@@ -1052,7 +1069,7 @@ nfs4_proc_readlink(struct inode *inode, struct page *page) ...@@ -1052,7 +1069,7 @@ nfs4_proc_readlink(struct inode *inode, struct page *page)
nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "readlink"); nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "readlink");
nfs4_setup_putfh(&compound, NFS_FH(inode)); nfs4_setup_putfh(&compound, NFS_FH(inode));
nfs4_setup_readlink(&compound, PAGE_CACHE_SIZE, &page); nfs4_setup_readlink(&compound, PAGE_CACHE_SIZE, &page);
return nfs4_call_compound(&compound, NULL, 0); return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0));
} }
static int static int
...@@ -1088,14 +1105,10 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) ...@@ -1088,14 +1105,10 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp)
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, flags); status = rpc_call_sync(server->client, &msg, flags);
if (!status) { if (!status)
renew_lease(server, timestamp); renew_lease(server, timestamp);
/* Check cache consistency */
if (fattr->change_attr != NFS_CHANGE_ATTR(inode))
nfs_zap_caches(inode);
}
dprintk("NFS reply read: %d\n", status); dprintk("NFS reply read: %d\n", status);
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1130,9 +1143,8 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -1130,9 +1143,8 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp)
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, rpcflags); status = rpc_call_sync(server->client, &msg, rpcflags);
NFS_CACHEINV(inode);
dprintk("NFS reply write: %d\n", status); dprintk("NFS reply write: %d\n", status);
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1167,7 +1179,7 @@ nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp) ...@@ -1167,7 +1179,7 @@ nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp)
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, 0); status = rpc_call_sync(server->client, &msg, 0);
dprintk("NFS reply commit: %d\n", status); dprintk("NFS reply commit: %d\n", status);
return status; return nfs4_map_errors(status);
} }
/* /*
...@@ -1234,7 +1246,7 @@ nfs4_proc_remove(struct inode *dir, struct qstr *name) ...@@ -1234,7 +1246,7 @@ nfs4_proc_remove(struct inode *dir, struct qstr *name)
process_cinfo(&dir_cinfo, &dir_attr); process_cinfo(&dir_cinfo, &dir_attr);
nfs_refresh_inode(dir, &dir_attr); nfs_refresh_inode(dir, &dir_attr);
} }
return status; return nfs4_map_errors(status);
} }
struct unlink_desc { struct unlink_desc {
...@@ -1312,7 +1324,7 @@ nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, ...@@ -1312,7 +1324,7 @@ nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
nfs_refresh_inode(old_dir, &old_dir_attr); nfs_refresh_inode(old_dir, &old_dir_attr);
nfs_refresh_inode(new_dir, &new_dir_attr); nfs_refresh_inode(new_dir, &new_dir_attr);
} }
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1342,7 +1354,7 @@ nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) ...@@ -1342,7 +1354,7 @@ nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
nfs_refresh_inode(dir, &dir_attr); nfs_refresh_inode(dir, &dir_attr);
nfs_refresh_inode(inode, &fattr); nfs_refresh_inode(inode, &fattr);
} }
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1373,7 +1385,7 @@ nfs4_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, ...@@ -1373,7 +1385,7 @@ nfs4_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
process_cinfo(&dir_cinfo, &dir_attr); process_cinfo(&dir_cinfo, &dir_attr);
nfs_refresh_inode(dir, &dir_attr); nfs_refresh_inode(dir, &dir_attr);
} }
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1403,7 +1415,7 @@ nfs4_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -1403,7 +1415,7 @@ nfs4_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr,
process_cinfo(&dir_cinfo, &dir_attr); process_cinfo(&dir_cinfo, &dir_attr);
nfs_refresh_inode(dir, &dir_attr); nfs_refresh_inode(dir, &dir_attr);
} }
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1421,9 +1433,11 @@ nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, ...@@ -1421,9 +1433,11 @@ nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
nfs4_setup_putfh(&compound, NFS_FH(dir)); nfs4_setup_putfh(&compound, NFS_FH(dir));
nfs4_setup_readdir(&compound, cookie, NFS_COOKIEVERF(dir), &page, count, dentry); nfs4_setup_readdir(&compound, cookie, NFS_COOKIEVERF(dir), &page, count, dentry);
status = nfs4_call_compound(&compound, cred, 0); status = nfs4_call_compound(&compound, cred, 0);
if (status == 0)
memcpy(NFS_COOKIEVERF(dir), ops[1].u.readdir.rd_resp_verifier.data, NFS4_VERIFIER_SIZE);
unlock_kernel(); unlock_kernel();
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1453,7 +1467,7 @@ nfs4_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr, ...@@ -1453,7 +1467,7 @@ nfs4_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
process_cinfo(&dir_cinfo, &dir_attr); process_cinfo(&dir_cinfo, &dir_attr);
nfs_refresh_inode(dir, &dir_attr); nfs_refresh_inode(dir, &dir_attr);
} }
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -1463,11 +1477,10 @@ nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -1463,11 +1477,10 @@ nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs4_compound compound; struct nfs4_compound compound;
struct nfs4_op ops[2]; struct nfs4_op ops[2];
memset(fsstat, 0, sizeof(*fsstat));
nfs4_setup_compound(&compound, ops, server, "statfs"); nfs4_setup_compound(&compound, ops, server, "statfs");
nfs4_setup_putfh(&compound, fhandle); nfs4_setup_putfh(&compound, fhandle);
nfs4_setup_statfs(&compound, fsstat); nfs4_setup_statfs(&compound, fsstat);
return nfs4_call_compound(&compound, NULL, 0); return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0));
} }
static int static int
...@@ -1480,8 +1493,7 @@ nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -1480,8 +1493,7 @@ nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = fsinfo, .rpc_resp = fsinfo,
}; };
memset(fsinfo, 0, sizeof(*fsinfo)); return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0));
return rpc_call_sync(server->client, &msg, 0);
} }
static int static int
...@@ -1491,11 +1503,10 @@ nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -1491,11 +1503,10 @@ nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs4_compound compound; struct nfs4_compound compound;
struct nfs4_op ops[2]; struct nfs4_op ops[2];
memset(pathconf, 0, sizeof(*pathconf));
nfs4_setup_compound(&compound, ops, server, "statfs"); nfs4_setup_compound(&compound, ops, server, "statfs");
nfs4_setup_putfh(&compound, fhandle); nfs4_setup_putfh(&compound, fhandle);
nfs4_setup_pathconf(&compound, pathconf); nfs4_setup_pathconf(&compound, pathconf);
return nfs4_call_compound(&compound, NULL, 0); return nfs4_map_errors(nfs4_call_compound(&compound, NULL, 0));
} }
static void static void
...@@ -1517,7 +1528,6 @@ nfs4_read_done(struct rpc_task *task) ...@@ -1517,7 +1528,6 @@ nfs4_read_done(struct rpc_task *task)
{ {
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
struct inode *inode = data->inode; struct inode *inode = data->inode;
struct nfs_fattr *fattr = data->res.fattr;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
task->tk_action = nfs4_restart_read; task->tk_action = nfs4_restart_read;
...@@ -1525,11 +1535,6 @@ nfs4_read_done(struct rpc_task *task) ...@@ -1525,11 +1535,6 @@ nfs4_read_done(struct rpc_task *task)
} }
if (task->tk_status > 0) if (task->tk_status > 0)
renew_lease(NFS_SERVER(inode), data->timestamp); renew_lease(NFS_SERVER(inode), data->timestamp);
/* Check cache consistency */
if (fattr->change_attr != NFS_CHANGE_ATTR(inode))
nfs_zap_caches(inode);
if (fattr->bitmap[1] & FATTR4_WORD1_TIME_ACCESS)
inode->i_atime = fattr->atime;
/* Call back common NFS readpage processing */ /* Call back common NFS readpage processing */
nfs_readpage_result(task); nfs_readpage_result(task);
} }
...@@ -1576,21 +1581,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) ...@@ -1576,21 +1581,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
rpc_call_setup(task, &msg, 0); rpc_call_setup(task, &msg, 0);
} }
static void
nfs4_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
/* Check cache consistency */
if (fattr->pre_change_attr != NFS_CHANGE_ATTR(inode))
nfs_zap_caches(inode);
NFS_CHANGE_ATTR(inode) = fattr->change_attr;
if (fattr->bitmap[1] & FATTR4_WORD1_SPACE_USED)
inode->i_blocks = (fattr->du.nfs3.used + 511) >> 9;
if (fattr->bitmap[1] & FATTR4_WORD1_TIME_METADATA)
inode->i_ctime = fattr->ctime;
if (fattr->bitmap[1] & FATTR4_WORD1_TIME_MODIFY)
inode->i_mtime = fattr->mtime;
}
static void static void
nfs4_restart_write(struct rpc_task *task) nfs4_restart_write(struct rpc_task *task)
{ {
...@@ -1617,7 +1607,6 @@ nfs4_write_done(struct rpc_task *task) ...@@ -1617,7 +1607,6 @@ nfs4_write_done(struct rpc_task *task)
} }
if (task->tk_status >= 0) if (task->tk_status >= 0)
renew_lease(NFS_SERVER(inode), data->timestamp); renew_lease(NFS_SERVER(inode), data->timestamp);
nfs4_write_refresh_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */ /* Call back common NFS writeback processing */
nfs_writeback_done(task); nfs_writeback_done(task);
} }
...@@ -1684,7 +1673,6 @@ nfs4_commit_done(struct rpc_task *task) ...@@ -1684,7 +1673,6 @@ nfs4_commit_done(struct rpc_task *task)
task->tk_action = nfs4_restart_write; task->tk_action = nfs4_restart_write;
return; return;
} }
nfs4_write_refresh_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */ /* Call back common NFS writeback processing */
nfs_commit_done(task); nfs_commit_done(task);
} }
...@@ -1807,6 +1795,7 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) ...@@ -1807,6 +1795,7 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp)
if (filp->f_mode & FMODE_WRITE) { if (filp->f_mode & FMODE_WRITE) {
lock_kernel(); lock_kernel();
nfs_set_mmcred(inode, state->owner->so_cred); nfs_set_mmcred(inode, state->owner->so_cred);
nfs_begin_data_update(inode);
unlock_kernel(); unlock_kernel();
} }
filp->private_data = state; filp->private_data = state;
...@@ -1823,6 +1812,11 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp) ...@@ -1823,6 +1812,11 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp)
if (state) if (state)
nfs4_close_state(state, filp->f_mode); nfs4_close_state(state, filp->f_mode);
if (filp->f_mode & FMODE_WRITE) {
lock_kernel();
nfs_end_data_update(inode);
unlock_kernel();
}
return 0; return 0;
} }
...@@ -1850,7 +1844,7 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) ...@@ -1850,7 +1844,7 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
{ {
struct nfs4_client *clp = server->nfs4_state; struct nfs4_client *clp = server->nfs4_state;
if (!clp) if (!clp || task->tk_status >= 0)
return 0; return 0;
switch(task->tk_status) { switch(task->tk_status) {
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
...@@ -1869,6 +1863,7 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) ...@@ -1869,6 +1863,7 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
task->tk_status = 0; task->tk_status = 0;
return -EAGAIN; return -EAGAIN;
} }
task->tk_status = nfs4_map_errors(task->tk_status);
return 0; return 0;
} }
...@@ -1946,16 +1941,9 @@ nfs4_handle_error(struct nfs_server *server, int errorcode) ...@@ -1946,16 +1941,9 @@ nfs4_handle_error(struct nfs_server *server, int errorcode)
break; break;
case -NFS4ERR_OLD_STATEID: case -NFS4ERR_OLD_STATEID:
ret = 0; ret = 0;
break;
default:
if (errorcode <= -1000) {
printk(KERN_WARNING "%s could not handle NFSv4 error %d\n",
__FUNCTION__, -errorcode);
ret = -EIO;
}
} }
/* We failed to handle the error */ /* We failed to handle the error */
return ret; return nfs4_map_errors(ret);
} }
...@@ -2130,7 +2118,7 @@ nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request) ...@@ -2130,7 +2118,7 @@ nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
if (lsp) if (lsp)
nfs4_put_lock_state(lsp); nfs4_put_lock_state(lsp);
up(&state->lock_sema); up(&state->lock_sema);
return status; return nfs4_map_errors(status);
} }
int int
...@@ -2175,7 +2163,7 @@ nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) ...@@ -2175,7 +2163,7 @@ nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
nfs4_put_lock_state(lsp); nfs4_put_lock_state(lsp);
out: out:
up(&state->lock_sema); up(&state->lock_sema);
return status; return nfs4_map_errors(status);
} }
static int static int
...@@ -2251,7 +2239,7 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) ...@@ -2251,7 +2239,7 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
nfs4_put_lock_state(lsp); nfs4_put_lock_state(lsp);
out: out:
up(&state->lock_sema); up(&state->lock_sema);
return status; return nfs4_map_errors(status);
} }
static int static int
......
...@@ -411,18 +411,20 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) ...@@ -411,18 +411,20 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
return state; return state;
} }
void static void
nfs4_put_open_state(struct nfs4_state *state) __nfs4_put_open_state(struct nfs4_state *state)
{ {
struct inode *inode = state->inode; struct inode *inode = state->inode;
struct nfs4_state_owner *owner = state->owner; struct nfs4_state_owner *owner = state->owner;
int status = 0; int status = 0;
if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) {
up(&owner->so_sema);
return; return;
list_del(&state->inode_states); }
if (!list_empty(&state->inode_states))
list_del(&state->inode_states);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
down(&owner->so_sema);
list_del(&state->open_states); list_del(&state->open_states);
if (state->state != 0) { if (state->state != 0) {
do { do {
...@@ -439,6 +441,13 @@ nfs4_put_open_state(struct nfs4_state *state) ...@@ -439,6 +441,13 @@ nfs4_put_open_state(struct nfs4_state *state)
nfs4_put_state_owner(owner); nfs4_put_state_owner(owner);
} }
void
nfs4_put_open_state(struct nfs4_state *state)
{
down(&state->owner->so_sema);
__nfs4_put_open_state(state);
}
void void
nfs4_close_state(struct nfs4_state *state, mode_t mode) nfs4_close_state(struct nfs4_state *state, mode_t mode)
{ {
...@@ -479,8 +488,7 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode) ...@@ -479,8 +488,7 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode)
status = nfs4_handle_error(NFS_SERVER(inode), status); status = nfs4_handle_error(NFS_SERVER(inode), status);
down(&owner->so_sema); down(&owner->so_sema);
} while (!status); } while (!status);
up(&owner->so_sema); __nfs4_put_open_state(state);
nfs4_put_open_state(state);
} }
/* /*
...@@ -790,7 +798,7 @@ reclaimer(void *ptr) ...@@ -790,7 +798,7 @@ reclaimer(void *ptr)
restart_loop: restart_loop:
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
list_for_each_entry(sp, &clp->cl_state_owners, so_list) { list_for_each_entry(sp, &clp->cl_state_owners, so_list) {
if (sp->so_generation - generation <= 0) if (sp->so_generation - generation >= 0)
continue; continue;
atomic_inc(&sp->so_count); atomic_inc(&sp->so_count);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
......
...@@ -69,84 +69,84 @@ static int nfs_stat_to_errno(int); ...@@ -69,84 +69,84 @@ static int nfs_stat_to_errno(int);
/* lock,open owner id: /* lock,open owner id:
* we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2)
*/ */
#define owner_id_maxsz 1 + 1 #define owner_id_maxsz (1 + 1)
#define compound_encode_hdr_maxsz 3 + (NFS4_MAXTAGLEN >> 2) #define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2))
#define compound_decode_hdr_maxsz 2 + (NFS4_MAXTAGLEN >> 2) #define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2))
#define op_encode_hdr_maxsz 1 #define op_encode_hdr_maxsz (1)
#define op_decode_hdr_maxsz 2 #define op_decode_hdr_maxsz (2)
#define encode_putfh_maxsz op_encode_hdr_maxsz + 1 + \ #define encode_putfh_maxsz (op_encode_hdr_maxsz + 1 + \
(NFS4_FHSIZE >> 2) (NFS4_FHSIZE >> 2))
#define decode_putfh_maxsz op_decode_hdr_maxsz #define decode_putfh_maxsz (op_decode_hdr_maxsz)
#define encode_putrootfh_maxsz op_encode_hdr_maxsz #define encode_putrootfh_maxsz (op_encode_hdr_maxsz)
#define decode_putrootfh_maxsz op_decode_hdr_maxsz #define decode_putrootfh_maxsz (op_decode_hdr_maxsz)
#define encode_getfh_maxsz op_encode_hdr_maxsz #define encode_getfh_maxsz (op_encode_hdr_maxsz)
#define decode_getfh_maxsz op_decode_hdr_maxsz + 1 + \ #define decode_getfh_maxsz (op_decode_hdr_maxsz + 1 + \
(NFS4_FHSIZE >> 2) (NFS4_FHSIZE >> 2))
#define encode_getattr_maxsz op_encode_hdr_maxsz + 3 #define encode_getattr_maxsz (op_encode_hdr_maxsz + 3)
#define nfs4_fattr_bitmap_maxsz 26 + 2 * ((NFS4_MAXNAMLEN +1) >> 2) #define nfs4_fattr_bitmap_maxsz (26 + 2 * ((NFS4_MAXNAMLEN +1) >> 2))
#define decode_getattr_maxsz op_decode_hdr_maxsz + 3 + \ #define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \
nfs4_fattr_bitmap_maxsz nfs4_fattr_bitmap_maxsz)
#define encode_savefh_maxsz op_encode_hdr_maxsz #define encode_savefh_maxsz (op_encode_hdr_maxsz)
#define decode_savefh_maxsz op_decode_hdr_maxsz #define decode_savefh_maxsz (op_decode_hdr_maxsz)
#define encode_restorefh_maxsz op_encode_hdr_maxsz #define encode_restorefh_maxsz (op_encode_hdr_maxsz)
#define decode_restorefh_maxsz op_decode_hdr_maxsz #define decode_restorefh_maxsz (op_decode_hdr_maxsz)
#define encode_read_getattr_maxsz op_encode_hdr_maxsz + 2 #define encode_read_getattr_maxsz (op_encode_hdr_maxsz + 2)
#define decode_read_getattr_maxsz op_decode_hdr_maxsz + 8 #define decode_read_getattr_maxsz (op_decode_hdr_maxsz + 8)
#define encode_pre_write_getattr_maxsz op_encode_hdr_maxsz + 2 #define encode_pre_write_getattr_maxsz (op_encode_hdr_maxsz + 2)
#define decode_pre_write_getattr_maxsz op_decode_hdr_maxsz + 5 #define decode_pre_write_getattr_maxsz (op_decode_hdr_maxsz + 5)
#define encode_post_write_getattr_maxsz op_encode_hdr_maxsz + 2 #define encode_post_write_getattr_maxsz (op_encode_hdr_maxsz + 2)
#define decode_post_write_getattr_maxsz op_decode_hdr_maxsz + 13 #define decode_post_write_getattr_maxsz (op_decode_hdr_maxsz + 13)
#define encode_fsinfo_maxsz op_encode_hdr_maxsz + 2 #define encode_fsinfo_maxsz (op_encode_hdr_maxsz + 2)
#define decode_fsinfo_maxsz op_decode_hdr_maxsz + 11 #define decode_fsinfo_maxsz (op_decode_hdr_maxsz + 11)
#define encode_renew_maxsz op_encode_hdr_maxsz + 3 #define encode_renew_maxsz (op_encode_hdr_maxsz + 3)
#define decode_renew_maxsz op_decode_hdr_maxsz #define decode_renew_maxsz (op_decode_hdr_maxsz)
#define encode_setclientid_maxsz \ #define encode_setclientid_maxsz \
op_encode_hdr_maxsz + \ (op_encode_hdr_maxsz + \
4 /*server->ip_addr*/ + \ 4 /*server->ip_addr*/ + \
1 /*Netid*/ + \ 1 /*Netid*/ + \
6 /*uaddr*/ + \ 6 /*uaddr*/ + \
6 + (NFS4_VERIFIER_SIZE >> 2) 6 + (NFS4_VERIFIER_SIZE >> 2))
#define decode_setclientid_maxsz \ #define decode_setclientid_maxsz \
op_decode_hdr_maxsz + \ (op_decode_hdr_maxsz + \
2 + \ 2 + \
1024 /* large value for CLID_INUSE */ 1024) /* large value for CLID_INUSE */
#define encode_setclientid_confirm_maxsz \ #define encode_setclientid_confirm_maxsz \
op_encode_hdr_maxsz + \ (op_encode_hdr_maxsz + \
3 + (NFS4_VERIFIER_SIZE >> 2) 3 + (NFS4_VERIFIER_SIZE >> 2))
#define decode_setclientid_confirm_maxsz \ #define decode_setclientid_confirm_maxsz \
op_decode_hdr_maxsz (op_decode_hdr_maxsz)
#define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */
#define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */
#define NFS4_enc_read_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_read_getattr_maxsz + \ encode_read_getattr_maxsz + \
op_encode_hdr_maxsz + 7 op_encode_hdr_maxsz + 7)
#define NFS4_dec_read_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_read_getattr_maxsz + \ decode_read_getattr_maxsz + \
op_decode_hdr_maxsz + 2 op_decode_hdr_maxsz + 2)
#define NFS4_enc_write_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_pre_write_getattr_maxsz + \ encode_pre_write_getattr_maxsz + \
op_encode_hdr_maxsz + 8 + \ op_encode_hdr_maxsz + 8 + \
encode_post_write_getattr_maxsz encode_post_write_getattr_maxsz)
#define NFS4_dec_write_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_pre_write_getattr_maxsz + \ decode_pre_write_getattr_maxsz + \
op_decode_hdr_maxsz + 4 + \ op_decode_hdr_maxsz + 4 + \
decode_post_write_getattr_maxsz decode_post_write_getattr_maxsz)
#define NFS4_enc_commit_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_pre_write_getattr_maxsz + \ encode_pre_write_getattr_maxsz + \
op_encode_hdr_maxsz + 3 + \ op_encode_hdr_maxsz + 3 + \
encode_post_write_getattr_maxsz encode_post_write_getattr_maxsz)
#define NFS4_dec_commit_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_pre_write_getattr_maxsz + \ decode_pre_write_getattr_maxsz + \
op_decode_hdr_maxsz + 2 + \ op_decode_hdr_maxsz + 2 + \
decode_post_write_getattr_maxsz decode_post_write_getattr_maxsz)
#define NFS4_enc_open_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_savefh_maxsz + \ encode_savefh_maxsz + \
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
...@@ -154,107 +154,107 @@ static int nfs_stat_to_errno(int); ...@@ -154,107 +154,107 @@ static int nfs_stat_to_errno(int);
encode_getattr_maxsz + \ encode_getattr_maxsz + \
encode_getfh_maxsz + \ encode_getfh_maxsz + \
encode_restorefh_maxsz + \ encode_restorefh_maxsz + \
encode_getattr_maxsz encode_getattr_maxsz)
#define NFS4_dec_open_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_savefh_maxsz + \ decode_savefh_maxsz + \
op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \ op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \
decode_getattr_maxsz + \ decode_getattr_maxsz + \
decode_getfh_maxsz + \ decode_getfh_maxsz + \
decode_restorefh_maxsz + \ decode_restorefh_maxsz + \
decode_getattr_maxsz decode_getattr_maxsz)
#define NFS4_enc_open_confirm_sz \ #define NFS4_enc_open_confirm_sz \
compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + 5 op_encode_hdr_maxsz + 5)
#define NFS4_dec_open_confirm_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_open_confirm_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4 op_decode_hdr_maxsz + 4)
#define NFS4_enc_open_reclaim_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_open_reclaim_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
11 + \ 11 + \
encode_getattr_maxsz encode_getattr_maxsz)
#define NFS4_dec_open_reclaim_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_open_reclaim_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + \ op_decode_hdr_maxsz + \
4 + 5 + 2 + 3 + \ 4 + 5 + 2 + 3 + \
decode_getattr_maxsz decode_getattr_maxsz)
#define NFS4_enc_open_downgrade_sz \ #define NFS4_enc_open_downgrade_sz \
compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + 7 op_encode_hdr_maxsz + 7)
#define NFS4_dec_open_downgrade_sz \ #define NFS4_dec_open_downgrade_sz \
compound_decode_hdr_maxsz + \ (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4 op_decode_hdr_maxsz + 4)
#define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + 5 op_encode_hdr_maxsz + 5)
#define NFS4_dec_close_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4 op_decode_hdr_maxsz + 4)
#define NFS4_enc_setattr_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
op_encode_hdr_maxsz + 4 + \ op_encode_hdr_maxsz + 4 + \
nfs4_fattr_bitmap_maxsz + \ nfs4_fattr_bitmap_maxsz + \
encode_getattr_maxsz encode_getattr_maxsz)
#define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
op_decode_hdr_maxsz + 3 op_decode_hdr_maxsz + 3)
#define NFS4_enc_fsinfo_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_fsinfo_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_fsinfo_maxsz encode_fsinfo_maxsz)
#define NFS4_dec_fsinfo_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_fsinfo_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_fsinfo_maxsz decode_fsinfo_maxsz)
#define NFS4_enc_renew_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_renew_sz (compound_encode_hdr_maxsz + \
encode_renew_maxsz encode_renew_maxsz)
#define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_renew_sz (compound_decode_hdr_maxsz + \
decode_renew_maxsz decode_renew_maxsz)
#define NFS4_enc_setclientid_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_setclientid_sz (compound_encode_hdr_maxsz + \
encode_setclientid_maxsz encode_setclientid_maxsz)
#define NFS4_dec_setclientid_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_setclientid_sz (compound_decode_hdr_maxsz + \
decode_setclientid_maxsz decode_setclientid_maxsz)
#define NFS4_enc_setclientid_confirm_sz \ #define NFS4_enc_setclientid_confirm_sz \
compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_setclientid_confirm_maxsz + \ encode_setclientid_confirm_maxsz + \
encode_putrootfh_maxsz + \ encode_putrootfh_maxsz + \
encode_fsinfo_maxsz encode_fsinfo_maxsz)
#define NFS4_dec_setclientid_confirm_sz \ #define NFS4_dec_setclientid_confirm_sz \
compound_decode_hdr_maxsz + \ (compound_decode_hdr_maxsz + \
decode_setclientid_confirm_maxsz + \ decode_setclientid_confirm_maxsz + \
decode_putrootfh_maxsz + \ decode_putrootfh_maxsz + \
decode_fsinfo_maxsz decode_fsinfo_maxsz)
#define NFS4_enc_lock_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_getattr_maxsz + \ encode_getattr_maxsz + \
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
1 + 1 + 2 + 2 + \ 1 + 1 + 2 + 2 + \
1 + 4 + 1 + 2 + \ 1 + 4 + 1 + 2 + \
owner_id_maxsz owner_id_maxsz)
#define NFS4_dec_lock_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_getattr_maxsz + \ decode_getattr_maxsz + \
op_decode_hdr_maxsz + \ op_decode_hdr_maxsz + \
2 + 2 + 1 + 2 + \ 2 + 2 + 1 + 2 + \
owner_id_maxsz owner_id_maxsz)
#define NFS4_enc_lockt_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_getattr_maxsz + \ encode_getattr_maxsz + \
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
1 + 2 + 2 + 2 + \ 1 + 2 + 2 + 2 + \
owner_id_maxsz owner_id_maxsz)
#define NFS4_dec_lockt_sz NFS4_dec_lock_sz #define NFS4_dec_lockt_sz (NFS4_dec_lock_sz)
#define NFS4_enc_locku_sz compound_encode_hdr_maxsz + \ #define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
encode_getattr_maxsz + \ encode_getattr_maxsz + \
op_encode_hdr_maxsz + \ op_encode_hdr_maxsz + \
1 + 1 + 4 + 2 + 2 1 + 1 + 4 + 2 + 2)
#define NFS4_dec_locku_sz compound_decode_hdr_maxsz + \ #define NFS4_dec_locku_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \ decode_putfh_maxsz + \
decode_getattr_maxsz + \ decode_getattr_maxsz + \
op_decode_hdr_maxsz + 4 op_decode_hdr_maxsz + 4)
...@@ -324,7 +324,7 @@ encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) ...@@ -324,7 +324,7 @@ encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag); dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag);
BUG_ON(hdr->taglen > NFS4_MAXTAGLEN); BUG_ON(hdr->taglen > NFS4_MAXTAGLEN);
RESERVE_SPACE(12+XDR_QUADLEN(hdr->taglen)); RESERVE_SPACE(12+(XDR_QUADLEN(hdr->taglen)<<2));
WRITE32(hdr->taglen); WRITE32(hdr->taglen);
WRITEMEM(hdr->tag, hdr->taglen); WRITEMEM(hdr->tag, hdr->taglen);
WRITE32(NFS4_MINOR_VERSION); WRITE32(NFS4_MINOR_VERSION);
...@@ -3165,6 +3165,10 @@ static struct { ...@@ -3165,6 +3165,10 @@ static struct {
{ NFS4ERR_SYMLINK, ELOOP }, { NFS4ERR_SYMLINK, ELOOP },
{ NFS4ERR_OP_ILLEGAL, EOPNOTSUPP }, { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP },
{ NFS4ERR_DEADLOCK, EDEADLK }, { NFS4ERR_DEADLOCK, EDEADLK },
{ NFS4ERR_WRONGSEC, EPERM }, /* FIXME: this needs
* to be handled by a
* middle-layer.
*/
{ -1, EIO } { -1, EIO }
}; };
...@@ -3180,6 +3184,10 @@ nfs_stat_to_errno(int stat) ...@@ -3180,6 +3184,10 @@ nfs_stat_to_errno(int stat)
if (nfs_errtbl[i].stat == stat) if (nfs_errtbl[i].stat == stat)
return nfs_errtbl[i].errno; return nfs_errtbl[i].errno;
} }
if (stat < 0) {
/* The server is looney tunes. */
return ESERVERFAULT;
}
/* If we cannot translate the error, the recovery routines should /* If we cannot translate the error, the recovery routines should
* handle it. * handle it.
* Note: remaining NFSv4 error codes have values > 10000, so should * Note: remaining NFSv4 error codes have values > 10000, so should
......
...@@ -165,37 +165,6 @@ static struct nfs_bool_opts { ...@@ -165,37 +165,6 @@ static struct nfs_bool_opts {
}; };
/*
* Extract IP address from the parameter string if needed. Note that we
* need to have root_server_addr set _before_ IPConfig gets called as it
* can override it.
*/
static void __init root_nfs_parse_addr(char *name)
{
int octets = 0;
char *cp, *cq;
cp = cq = name;
while (octets < 4) {
while (*cp >= '0' && *cp <= '9')
cp++;
if (cp == cq || cp - cq > 3)
break;
if (*cp == '.' || octets == 3)
octets++;
if (octets < 4)
cp++;
cq = cp;
}
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
if (*cp == ':')
*cp++ = '\0';
root_server_addr = in_aton(name);
strcpy(name, cp);
}
}
/* /*
* Parse option string. * Parse option string.
*/ */
...@@ -345,7 +314,7 @@ int __init nfs_root_setup(char *line) ...@@ -345,7 +314,7 @@ int __init nfs_root_setup(char *line)
line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0'; line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
sprintf(nfs_root_name, NFS_ROOT, line); sprintf(nfs_root_name, NFS_ROOT, line);
} }
root_nfs_parse_addr(nfs_root_name); root_server_addr = root_nfs_parse_addr(nfs_root_name);
return 1; return 1;
} }
......
...@@ -246,7 +246,6 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, ...@@ -246,7 +246,6 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
* nfs_scan_list - Scan a list for matching requests * nfs_scan_list - Scan a list for matching requests
* @head: One of the NFS inode request lists * @head: One of the NFS inode request lists
* @dst: Destination list * @dst: Destination list
* @file: if set, ensure we match requests from this file
* @idx_start: lower bound of page->index to scan * @idx_start: lower bound of page->index to scan
* @npages: idx_start + npages sets the upper bound to scan. * @npages: idx_start + npages sets the upper bound to scan.
* *
...@@ -258,7 +257,6 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst, ...@@ -258,7 +257,6 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
*/ */
int int
nfs_scan_list(struct list_head *head, struct list_head *dst, nfs_scan_list(struct list_head *head, struct list_head *dst,
struct file *file,
unsigned long idx_start, unsigned int npages) unsigned long idx_start, unsigned int npages)
{ {
struct list_head *pos, *tmp; struct list_head *pos, *tmp;
...@@ -276,9 +274,6 @@ nfs_scan_list(struct list_head *head, struct list_head *dst, ...@@ -276,9 +274,6 @@ nfs_scan_list(struct list_head *head, struct list_head *dst,
req = nfs_list_entry(pos); req = nfs_list_entry(pos);
if (file && req->wb_file != file)
continue;
if (req->wb_index < idx_start) if (req->wb_index < idx_start)
continue; continue;
if (req->wb_index > idx_end) if (req->wb_index > idx_end)
......
...@@ -49,18 +49,6 @@ ...@@ -49,18 +49,6 @@
extern struct rpc_procinfo nfs_procedures[]; extern struct rpc_procinfo nfs_procedures[];
static void
nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
if (!(fattr->valid & NFS_ATTR_WCC)) {
fattr->pre_size = NFS_CACHE_ISIZE(inode);
fattr->pre_mtime = NFS_CACHE_MTIME(inode);
fattr->pre_ctime = NFS_CACHE_CTIME(inode);
fattr->valid |= NFS_ATTR_WCC;
}
nfs_refresh_inode(inode, fattr);
}
static struct rpc_cred * static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp) nfs_cred(struct inode *inode, struct file *filp)
{ {
...@@ -78,15 +66,33 @@ nfs_cred(struct inode *inode, struct file *filp) ...@@ -78,15 +66,33 @@ nfs_cred(struct inode *inode, struct file *filp)
*/ */
static int static int
nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr) struct nfs_fsinfo *info)
{ {
int status; struct nfs_fattr *fattr = info->fattr;
struct nfs2_fsstat fsinfo;
int status;
dprintk("NFS call getroot\n"); dprintk("%s: call getattr\n", __FUNCTION__);
fattr->valid = 0; fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0); status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0);
dprintk("NFS reply getroot\n"); dprintk("%s: reply getattr %d\n", __FUNCTION__, status);
return 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);
if (status)
return status;
info->rtmax = NFS_MAXDATA;
info->rtpref = fsinfo.tsize;
info->rtmult = fsinfo.bsize;
info->wtmax = NFS_MAXDATA;
info->wtpref = fsinfo.tsize;
info->wtmult = fsinfo.bsize;
info->dtpref = fsinfo.tsize;
info->maxfilesize = 0x7FFFFFFF;
info->lease_time = 0;
return 0;
} }
/* /*
...@@ -180,8 +186,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp) ...@@ -180,8 +186,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) if (status >= 0) {
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
/* Emulate the eof flag, which isn't normally needed in NFSv2
* as it is guaranteed to always return the file attributes
*/
if (rdata->args.offset + rdata->args.count >= fattr->size)
rdata->res.eof = 1;
}
dprintk("NFS reply read: %d\n", status); dprintk("NFS reply read: %d\n", status);
return status; return status;
} }
...@@ -205,7 +217,7 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -205,7 +217,7 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) { if (status >= 0) {
nfs_write_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
wdata->res.count = wdata->args.count; wdata->res.count = wdata->args.count;
wdata->verf.committed = NFS_FILE_SYNC; wdata->verf.committed = NFS_FILE_SYNC;
} }
...@@ -331,10 +343,8 @@ nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task) ...@@ -331,10 +343,8 @@ nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
{ {
struct rpc_message *msg = &task->tk_msg; struct rpc_message *msg = &task->tk_msg;
if (msg->rpc_argp) { if (msg->rpc_argp)
NFS_CACHEINV(dir->d_inode);
kfree(msg->rpc_argp); kfree(msg->rpc_argp);
}
return 0; return 0;
} }
...@@ -537,8 +547,14 @@ nfs_read_done(struct rpc_task *task) ...@@ -537,8 +547,14 @@ nfs_read_done(struct rpc_task *task)
{ {
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0) {
nfs_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
/* Emulate the eof flag, which isn't normally needed in NFSv2
* as it is guaranteed to always return the file attributes
*/
if (data->args.offset + data->args.count >= data->res.fattr->size)
data->res.eof = 1;
}
nfs_readpage_result(task); nfs_readpage_result(task);
} }
...@@ -584,7 +600,7 @@ nfs_write_done(struct rpc_task *task) ...@@ -584,7 +600,7 @@ nfs_write_done(struct rpc_task *task)
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0)
nfs_write_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
nfs_writeback_done(task); nfs_writeback_done(task);
} }
......
...@@ -121,9 +121,13 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) ...@@ -121,9 +121,13 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
} }
count -= result; count -= result;
rdata.args.pgbase += result; rdata.args.pgbase += result;
if (result < rdata.args.count) /* NFSv2ism */ /* Note: result == 0 should only happen if we're caching
* a write that extends the file and punches a hole.
*/
if (rdata.res.eof != 0 || result == 0)
break; break;
} while (count); } while (count);
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
if (count) if (count)
memclear_highpage_flush(page, rdata.args.pgbase, count); memclear_highpage_flush(page, rdata.args.pgbase, count);
...@@ -266,6 +270,7 @@ nfs_readpage_result(struct rpc_task *task) ...@@ -266,6 +270,7 @@ nfs_readpage_result(struct rpc_task *task)
dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
NFS_FLAGS(data->inode) |= NFS_INO_INVALID_ATIME;
while (!list_empty(&data->pages)) { while (!list_empty(&data->pages)) {
struct nfs_page *req = nfs_list_entry(data->pages.next); struct nfs_page *req = nfs_list_entry(data->pages.next);
struct page *page = req->wb_page; struct page *page = req->wb_page;
......
...@@ -104,6 +104,7 @@ nfs_async_unlink_init(struct rpc_task *task) ...@@ -104,6 +104,7 @@ nfs_async_unlink_init(struct rpc_task *task)
status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name);
if (status < 0) if (status < 0)
goto out_err; goto out_err;
nfs_begin_data_update(dir->d_inode);
rpc_call_setup(task, &msg, 0); rpc_call_setup(task, &msg, 0);
return; return;
out_err: out_err:
...@@ -126,7 +127,7 @@ nfs_async_unlink_done(struct rpc_task *task) ...@@ -126,7 +127,7 @@ nfs_async_unlink_done(struct rpc_task *task)
if (!dir) if (!dir)
return; return;
dir_i = dir->d_inode; dir_i = dir->d_inode;
nfs_zap_caches(dir_i); nfs_end_data_update(dir_i);
if (NFS_PROTO(dir_i)->unlink_done(dir, task)) if (NFS_PROTO(dir_i)->unlink_done(dir, task))
return; return;
put_rpccred(data->cred); put_rpccred(data->cred);
......
...@@ -74,7 +74,6 @@ ...@@ -74,7 +74,6 @@
static struct nfs_page * nfs_update_request(struct file*, struct inode *, static struct nfs_page * nfs_update_request(struct file*, struct inode *,
struct page *, struct page *,
unsigned int, unsigned int); unsigned int, unsigned int);
static void nfs_strategy(struct inode *inode);
static kmem_cache_t *nfs_wdata_cachep; static kmem_cache_t *nfs_wdata_cachep;
static mempool_t *nfs_wdata_mempool; static mempool_t *nfs_wdata_mempool;
...@@ -124,6 +123,52 @@ void nfs_commit_release(struct rpc_task *task) ...@@ -124,6 +123,52 @@ void nfs_commit_release(struct rpc_task *task)
nfs_commit_free(wdata); nfs_commit_free(wdata);
} }
/* 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)
{
struct inode *inode = page->mapping->host;
loff_t end, i_size = i_size_read(inode);
unsigned long end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
if (i_size > 0 && page->index < end_index)
return;
end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
if (i_size >= end)
return;
i_size_write(inode, end);
}
/* We can set the PG_uptodate flag if we see that a write request
* covers the full page.
*/
static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int count)
{
loff_t end_offs;
if (PageUptodate(page))
return;
if (base != 0)
return;
if (count == PAGE_CACHE_SIZE) {
SetPageUptodate(page);
return;
}
end_offs = i_size_read(page->mapping->host) - 1;
if (end_offs < 0)
return;
/* Is this the last page? */
if (page->index != (unsigned long)(end_offs >> PAGE_CACHE_SHIFT))
return;
/* This is the last page: set PG_uptodate if we cover the entire
* extent of the data, then zero the rest of the page.
*/
if (count == (unsigned int)(end_offs & (PAGE_CACHE_SIZE - 1)) + 1) {
memclear_highpage_flush(page, count, PAGE_CACHE_SIZE - count);
SetPageUptodate(page);
}
}
/* /*
* Write a page synchronously. * Write a page synchronously.
* Offset is the data offset within the page. * Offset is the data offset within the page.
...@@ -157,6 +202,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, ...@@ -157,6 +202,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
(long long)NFS_FILEID(inode), (long long)NFS_FILEID(inode),
count, (long long)(page_offset(page) + offset)); count, (long long)(page_offset(page) + offset));
nfs_begin_data_update(inode);
do { do {
if (count < wsize && !swapfile) if (count < wsize && !swapfile)
wdata.args.count = count; wdata.args.count = count;
...@@ -177,43 +223,38 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, ...@@ -177,43 +223,38 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
wdata.args.pgbase += result; wdata.args.pgbase += result;
written += result; written += result;
count -= result; count -= result;
/*
* If we've extended the file, update the inode
* now so we don't invalidate the cache.
*/
if (wdata.args.offset > i_size_read(inode))
i_size_write(inode, wdata.args.offset);
} while (count); } while (count);
/* Update file length */
nfs_grow_file(page, offset, written);
/* Set the PG_uptodate flag? */
nfs_mark_uptodate(page, offset, written);
if (PageError(page)) if (PageError(page))
ClearPageError(page); ClearPageError(page);
io_error: io_error:
nfs_end_data_update(inode);
if (wdata.cred) if (wdata.cred)
put_rpccred(wdata.cred); put_rpccred(wdata.cred);
return written ? written : result; return written ? written : result;
} }
static int static int nfs_writepage_async(struct file *file, struct inode *inode,
nfs_writepage_async(struct file *file, struct inode *inode, struct page *page, struct page *page, unsigned int offset, unsigned int count)
unsigned int offset, unsigned int count)
{ {
struct nfs_page *req; struct nfs_page *req;
loff_t end;
int status; int status;
req = nfs_update_request(file, inode, page, offset, count); req = nfs_update_request(file, inode, page, offset, count);
status = (IS_ERR(req)) ? PTR_ERR(req) : 0; status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
if (status < 0) if (status < 0)
goto out; goto out;
/* Update file length */
nfs_grow_file(page, offset, count);
/* Set the PG_uptodate flag? */
nfs_mark_uptodate(page, offset, count);
nfs_unlock_request(req); nfs_unlock_request(req);
nfs_strategy(inode);
end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
if (i_size_read(inode) < end)
i_size_write(inode, end);
out: out:
return status; return status;
} }
...@@ -286,7 +327,7 @@ nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) ...@@ -286,7 +327,7 @@ nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
err = generic_writepages(mapping, wbc); err = generic_writepages(mapping, wbc);
if (err) if (err)
goto out; goto out;
err = nfs_flush_file(inode, NULL, 0, 0, 0); err = nfs_flush_inode(inode, 0, 0, 0);
if (err < 0) if (err < 0)
goto out; goto out;
if (wbc->sync_mode == WB_SYNC_HOLD) if (wbc->sync_mode == WB_SYNC_HOLD)
...@@ -294,7 +335,7 @@ nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) ...@@ -294,7 +335,7 @@ nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
if (is_sync && wbc->sync_mode == WB_SYNC_ALL) { if (is_sync && wbc->sync_mode == WB_SYNC_ALL) {
err = nfs_wb_all(inode); err = nfs_wb_all(inode);
} else } else
nfs_commit_file(inode, NULL, 0, 0, 0); nfs_commit_inode(inode, 0, 0, 0);
out: out:
return err; return err;
} }
...@@ -312,8 +353,10 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req) ...@@ -312,8 +353,10 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
BUG_ON(error == -EEXIST); BUG_ON(error == -EEXIST);
if (error) if (error)
return error; return error;
if (!nfsi->npages) if (!nfsi->npages) {
igrab(inode); igrab(inode);
nfs_begin_data_update(inode);
}
nfsi->npages++; nfsi->npages++;
req->wb_count++; req->wb_count++;
return 0; return 0;
...@@ -336,6 +379,7 @@ nfs_inode_remove_request(struct nfs_page *req) ...@@ -336,6 +379,7 @@ nfs_inode_remove_request(struct nfs_page *req)
nfsi->npages--; nfsi->npages--;
if (!nfsi->npages) { if (!nfsi->npages) {
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
nfs_end_data_update(inode);
iput(inode); iput(inode);
} else } else
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
...@@ -421,7 +465,7 @@ nfs_mark_request_commit(struct nfs_page *req) ...@@ -421,7 +465,7 @@ nfs_mark_request_commit(struct nfs_page *req)
* Interruptible by signals only if mounted with intr flag. * Interruptible by signals only if mounted with intr flag.
*/ */
static int static int
nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages) nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req; struct nfs_page *req;
...@@ -441,8 +485,6 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s ...@@ -441,8 +485,6 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s
break; break;
next = req->wb_index + 1; next = req->wb_index + 1;
if (file && req->wb_file != file)
continue;
if (!NFS_WBACK_BUSY(req)) if (!NFS_WBACK_BUSY(req))
continue; continue;
...@@ -453,7 +495,6 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s ...@@ -453,7 +495,6 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s
if (error < 0) if (error < 0)
return error; return error;
spin_lock(&nfs_wreq_lock); spin_lock(&nfs_wreq_lock);
next = idx_start;
res++; res++;
} }
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
...@@ -464,7 +505,6 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s ...@@ -464,7 +505,6 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s
* nfs_scan_dirty - Scan an inode for dirty requests * nfs_scan_dirty - Scan an inode for dirty requests
* @inode: NFS inode to scan * @inode: NFS inode to scan
* @dst: destination list * @dst: destination list
* @file: if set, ensure we match requests from this file
* @idx_start: lower bound of page->index to scan. * @idx_start: lower bound of page->index to scan.
* @npages: idx_start + npages sets the upper bound to scan. * @npages: idx_start + npages sets the upper bound to scan.
* *
...@@ -472,11 +512,11 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s ...@@ -472,11 +512,11 @@ nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_s
* The requests are *not* checked to ensure that they form a contiguous set. * The requests are *not* checked to ensure that they form a contiguous set.
*/ */
static int static int
nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
int res; int res;
res = nfs_scan_list(&nfsi->dirty, dst, file, idx_start, npages); res = nfs_scan_list(&nfsi->dirty, dst, idx_start, npages);
nfsi->ndirty -= res; nfsi->ndirty -= res;
sub_page_state(nr_dirty,res); sub_page_state(nr_dirty,res);
if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty))
...@@ -489,7 +529,6 @@ nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, un ...@@ -489,7 +529,6 @@ nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, un
* nfs_scan_commit - Scan an inode for commit requests * nfs_scan_commit - Scan an inode for commit requests
* @inode: NFS inode to scan * @inode: NFS inode to scan
* @dst: destination list * @dst: destination list
* @file: if set, ensure we collect requests from this file only.
* @idx_start: lower bound of page->index to scan. * @idx_start: lower bound of page->index to scan.
* @npages: idx_start + npages sets the upper bound to scan. * @npages: idx_start + npages sets the upper bound to scan.
* *
...@@ -497,11 +536,11 @@ nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, un ...@@ -497,11 +536,11 @@ nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, un
* The requests are *not* checked to ensure that they form a contiguous set. * The requests are *not* checked to ensure that they form a contiguous set.
*/ */
static int static int
nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages) nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
int res; int res;
res = nfs_scan_list(&nfsi->commit, dst, file, idx_start, npages); res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages);
nfsi->ncommit -= res; nfsi->ncommit -= res;
if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
...@@ -600,46 +639,6 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page, ...@@ -600,46 +639,6 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
return req; return req;
} }
/*
* This is the strategy routine for NFS.
* It is called by nfs_updatepage whenever the user wrote up to the end
* of a page.
*
* We always try to submit a set of requests in parallel so that the
* server's write code can gather writes. This is mainly for the benefit
* of NFSv2.
*
* We never submit more requests than we think the remote can handle.
* For UDP sockets, we make sure we don't exceed the congestion window;
* for TCP, we limit the number of requests to 8.
*
* NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that
* should be sent out in one go. This is for the benefit of NFSv2 servers
* that perform write gathering.
*
* FIXME: Different servers may have different sweet spots.
* Record the average congestion window in server struct?
*/
#define NFS_STRATEGY_PAGES 8
static void
nfs_strategy(struct inode *inode)
{
unsigned int dirty, wpages;
dirty = NFS_I(inode)->ndirty;
wpages = NFS_SERVER(inode)->wpages;
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
if (NFS_PROTO(inode)->version == 2) {
if (dirty >= NFS_STRATEGY_PAGES * wpages)
nfs_flush_file(inode, NULL, 0, 0, 0);
} else if (dirty >= wpages)
nfs_flush_file(inode, NULL, 0, 0, 0);
#else
if (dirty >= NFS_STRATEGY_PAGES * wpages)
nfs_flush_file(inode, NULL, 0, 0, 0);
#endif
}
int int
nfs_flush_incompatible(struct file *file, struct page *page) nfs_flush_incompatible(struct file *file, struct page *page)
{ {
...@@ -675,7 +674,6 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign ...@@ -675,7 +674,6 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign
struct dentry *dentry = file->f_dentry; struct dentry *dentry = file->f_dentry;
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct nfs_page *req; struct nfs_page *req;
loff_t end;
int status = 0; int status = 0;
dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n", dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n",
...@@ -696,6 +694,27 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign ...@@ -696,6 +694,27 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign
return status; return status;
} }
/* If we're not using byte range locks, and we know the page
* is entirely in cache, it may be more efficient to avoid
* fragmenting write requests.
*/
if (PageUptodate(page) && inode->i_flock == NULL) {
loff_t end_offs = i_size_read(inode) - 1;
unsigned long end_index = end_offs >> PAGE_CACHE_SHIFT;
count += offset;
offset = 0;
if (unlikely(end_offs < 0)) {
/* Do nothing */
} else if (page->index == end_index) {
unsigned int pglen;
pglen = (unsigned int)(end_offs & (PAGE_CACHE_SIZE-1)) + 1;
if (count < pglen)
count = pglen;
} else if (page->index < end_index)
count = PAGE_CACHE_SIZE;
}
/* /*
* Try to find an NFS request corresponding to this page * Try to find an NFS request corresponding to this page
* and update it. * and update it.
...@@ -714,20 +733,12 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign ...@@ -714,20 +733,12 @@ nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsign
goto done; goto done;
status = 0; status = 0;
end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
if (i_size_read(inode) < end)
i_size_write(inode, end);
/* If we wrote past the end of the page. /* Update file length */
* Call the strategy routine so it can send out a bunch nfs_grow_file(page, offset, count);
* of requests. /* Set the PG_uptodate flag? */
*/ nfs_mark_uptodate(page, req->wb_pgbase, req->wb_bytes);
if (req->wb_pgbase == 0 && req->wb_bytes == PAGE_CACHE_SIZE) { nfs_unlock_request(req);
SetPageUptodate(page);
nfs_unlock_request(req);
nfs_strategy(inode);
} else
nfs_unlock_request(req);
done: done:
dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n", dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n",
status, (long long)i_size_read(inode)); status, (long long)i_size_read(inode));
...@@ -891,10 +902,7 @@ nfs_writeback_done(struct rpc_task *task) ...@@ -891,10 +902,7 @@ nfs_writeback_done(struct rpc_task *task)
#endif #endif
/* /*
* Update attributes as result of writeback. * Process the nfs_page list
* FIXME: There is an inherent race with invalidate_inode_pages and
* writebacks since the page->count is kept > 1 for as long
* as the page has a write request pending.
*/ */
while (!list_empty(&data->pages)) { while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next); req = nfs_list_entry(data->pages.next);
...@@ -1061,7 +1069,7 @@ nfs_commit_done(struct rpc_task *task) ...@@ -1061,7 +1069,7 @@ nfs_commit_done(struct rpc_task *task)
} }
#endif #endif
int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start, int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how) unsigned int npages, int how)
{ {
LIST_HEAD(head); LIST_HEAD(head);
...@@ -1069,7 +1077,7 @@ int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_sta ...@@ -1069,7 +1077,7 @@ int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_sta
error = 0; error = 0;
spin_lock(&nfs_wreq_lock); spin_lock(&nfs_wreq_lock);
res = nfs_scan_dirty(inode, &head, file, idx_start, npages); res = nfs_scan_dirty(inode, &head, idx_start, npages);
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
if (res) if (res)
error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how); error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
...@@ -1079,7 +1087,7 @@ int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_sta ...@@ -1079,7 +1087,7 @@ int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_sta
} }
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start, int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how) unsigned int npages, int how)
{ {
LIST_HEAD(head); LIST_HEAD(head);
...@@ -1087,9 +1095,9 @@ int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_st ...@@ -1087,9 +1095,9 @@ int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_st
error = 0; error = 0;
spin_lock(&nfs_wreq_lock); spin_lock(&nfs_wreq_lock);
res = nfs_scan_commit(inode, &head, file, idx_start, npages); res = nfs_scan_commit(inode, &head, idx_start, npages);
if (res) { if (res) {
res += nfs_scan_commit(inode, &head, NULL, 0, 0); res += nfs_scan_commit(inode, &head, 0, 0);
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
error = nfs_commit_list(&head, how); error = nfs_commit_list(&head, how);
} else } else
...@@ -1100,7 +1108,7 @@ int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_st ...@@ -1100,7 +1108,7 @@ int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_st
} }
#endif #endif
int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start, int nfs_sync_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how) unsigned int npages, int how)
{ {
int error, int error,
...@@ -1109,18 +1117,15 @@ int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_star ...@@ -1109,18 +1117,15 @@ int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_star
wait = how & FLUSH_WAIT; wait = how & FLUSH_WAIT;
how &= ~FLUSH_WAIT; how &= ~FLUSH_WAIT;
if (!inode && file)
inode = file->f_dentry->d_inode;
do { do {
error = 0; error = 0;
if (wait) if (wait)
error = nfs_wait_on_requests(inode, file, idx_start, npages); error = nfs_wait_on_requests(inode, idx_start, npages);
if (error == 0) if (error == 0)
error = nfs_flush_file(inode, file, idx_start, npages, how); error = nfs_flush_inode(inode, idx_start, npages, how);
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
if (error == 0) if (error == 0)
error = nfs_commit_file(inode, file, idx_start, npages, how); error = nfs_commit_inode(inode, idx_start, npages, how);
#endif #endif
} while (error > 0); } while (error > 0);
return error; return error;
......
...@@ -138,6 +138,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time; ...@@ -138,6 +138,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time;
#define S_DEAD 32 /* removed, but still open directory */ #define S_DEAD 32 /* removed, but still open directory */
#define S_NOQUOTA 64 /* Inode is not counted to quota */ #define S_NOQUOTA 64 /* Inode is not counted to quota */
#define S_DIRSYNC 128 /* Directory modifications are synchronous */ #define S_DIRSYNC 128 /* Directory modifications are synchronous */
#define S_NOCMTIME 256 /* Do not update file c/mtime */
/* /*
* Note that nosuid etc flags are inode-specific: setting some file-system * Note that nosuid etc flags are inode-specific: setting some file-system
...@@ -171,6 +172,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time; ...@@ -171,6 +172,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time;
#define IS_ONE_SECOND(inode) __IS_FLG(inode, MS_ONE_SECOND) #define IS_ONE_SECOND(inode) __IS_FLG(inode, MS_ONE_SECOND)
#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
/* the read-only stuff doesn't really belong here, but any other place is /* the read-only stuff doesn't really belong here, but any other place is
probably as bad and I don't want to create yet another include file. */ probably as bad and I don't want to create yet another include file. */
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#undef ifdebug #undef ifdebug
#if defined(RPC_DEBUG) && defined(LOCKD_DEBUG) #if defined(RPC_DEBUG) && defined(LOCKD_DEBUG)
# define ifdebug(flag) if (nlm_debug & NLMDBG_##flag) # define ifdebug(flag) if (unlikely(nlm_debug & NLMDBG_##flag))
#else #else
# define ifdebug(flag) if (0) # define ifdebug(flag) if (0)
#endif #endif
......
...@@ -165,6 +165,7 @@ u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); ...@@ -165,6 +165,7 @@ u32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *);
unsigned long nlmsvc_retry_blocked(void); unsigned long nlmsvc_retry_blocked(void);
int nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, int nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
int action); int action);
void nlmsvc_grant_reply(struct svc_rqst *, struct nlm_cookie *, u32);
/* /*
* File handling for the server personality * File handling for the server personality
......
...@@ -99,7 +99,7 @@ struct nfs_inode { ...@@ -99,7 +99,7 @@ struct nfs_inode {
/* /*
* Various flags * Various flags
*/ */
unsigned short flags; unsigned int flags;
/* /*
* read_cache_jiffies is when we started read-caching this inode, * read_cache_jiffies is when we started read-caching this inode,
...@@ -118,19 +118,22 @@ struct nfs_inode { ...@@ -118,19 +118,22 @@ struct nfs_inode {
* *
* mtime != read_cache_mtime * mtime != read_cache_mtime
*/ */
unsigned long readdir_timestamp;
unsigned long read_cache_jiffies; unsigned long read_cache_jiffies;
struct timespec read_cache_ctime;
struct timespec read_cache_mtime;
__u64 read_cache_isize;
unsigned long attrtimeo; unsigned long attrtimeo;
unsigned long attrtimeo_timestamp; unsigned long attrtimeo_timestamp;
__u64 change_attr; /* v4 only */ __u64 change_attr; /* v4 only */
/* "Generation counter" for the attribute cache. This is
* bumped whenever we update the metadata on the
* server.
*/
unsigned long cache_change_attribute;
/* /*
* Timestamp that dates the change made to read_cache_mtime. * Counter indicating the number of outstanding requests that
* This is of use for dentry revalidation * will cause a file data update.
*/ */
unsigned long cache_mtime_jiffies; atomic_t data_updates;
struct nfs_access_cache cache_access; struct nfs_access_cache cache_access;
...@@ -170,8 +173,9 @@ struct nfs_inode { ...@@ -170,8 +173,9 @@ struct nfs_inode {
#define NFS_INO_STALE 0x0001 /* possible stale inode */ #define NFS_INO_STALE 0x0001 /* possible stale inode */
#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */ #define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */
#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */ #define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */
#define NFS_INO_FLUSH 0x0008 /* inode is due for flushing */ #define NFS_INO_INVALID_ATTR 0x0008 /* cached attrs are invalid */
#define NFS_INO_FAKE_ROOT 0x0080 /* root inode placeholder */ #define NFS_INO_INVALID_DATA 0x0010 /* cached data is invalid */
#define NFS_INO_INVALID_ATIME 0x0020 /* cached atime is invalid */
static inline struct nfs_inode *NFS_I(struct inode *inode) static inline struct nfs_inode *NFS_I(struct inode *inode)
{ {
...@@ -186,15 +190,7 @@ static inline struct nfs_inode *NFS_I(struct inode *inode) ...@@ -186,15 +190,7 @@ static inline struct nfs_inode *NFS_I(struct inode *inode)
#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode))) #define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode)))
#define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf) #define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf)
#define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies) #define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies)
#define NFS_MTIME_UPDATE(inode) (NFS_I(inode)->cache_mtime_jiffies)
#define NFS_CACHE_CTIME(inode) (NFS_I(inode)->read_cache_ctime)
#define NFS_CACHE_MTIME(inode) (NFS_I(inode)->read_cache_mtime)
#define NFS_CACHE_ISIZE(inode) (NFS_I(inode)->read_cache_isize)
#define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr) #define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr)
#define NFS_CACHEINV(inode) \
do { \
NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
} while (0)
#define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo) #define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo)
#define NFS_MINATTRTIMEO(inode) \ #define NFS_MINATTRTIMEO(inode) \
(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
...@@ -207,10 +203,20 @@ do { \ ...@@ -207,10 +203,20 @@ do { \
#define NFS_FLAGS(inode) (NFS_I(inode)->flags) #define NFS_FLAGS(inode) (NFS_I(inode)->flags)
#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING) #define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
#define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE) #define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE)
#define NFS_FAKE_ROOT(inode) (NFS_FLAGS(inode) & NFS_INO_FAKE_ROOT)
#define NFS_FILEID(inode) (NFS_I(inode)->fileid) #define NFS_FILEID(inode) (NFS_I(inode)->fileid)
static inline int nfs_caches_unstable(struct inode *inode)
{
return atomic_read(&NFS_I(inode)->data_updates) != 0;
}
static inline void NFS_CACHEINV(struct inode *inode)
{
if (!nfs_caches_unstable(inode))
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATTR;
}
static inline int nfs_server_capable(struct inode *inode, int cap) static inline int nfs_server_capable(struct inode *inode, int cap)
{ {
return NFS_SERVER(inode)->caps & cap; return NFS_SERVER(inode)->caps & cap;
...@@ -227,13 +233,37 @@ loff_t page_offset(struct page *page) ...@@ -227,13 +233,37 @@ loff_t page_offset(struct page *page)
return ((loff_t)page->index) << PAGE_CACHE_SHIFT; return ((loff_t)page->index) << PAGE_CACHE_SHIFT;
} }
/**
* nfs_save_change_attribute - Returns the inode attribute change cookie
* @inode - pointer to inode
* The "change attribute" is updated every time we finish an operation
* that will result in a metadata change on the server.
*/
static inline long nfs_save_change_attribute(struct inode *inode)
{
return NFS_I(inode)->cache_change_attribute;
}
/**
* nfs_verify_change_attribute - Detects NFS inode cache updates
* @inode - pointer to inode
* @chattr - previously saved change attribute
* Return "false" if metadata has been updated (or is in the process of
* being updated) since the change attribute was saved.
*/
static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr)
{
return !nfs_caches_unstable(inode)
&& chattr == NFS_I(inode)->cache_change_attribute;
}
/* /*
* linux/fs/nfs/inode.c * linux/fs/nfs/inode.c
*/ */
extern void nfs_zap_caches(struct inode *); extern void nfs_zap_caches(struct inode *);
extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
struct nfs_fattr *); struct nfs_fattr *);
extern int __nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int nfs_permission(struct inode *, int, struct nameidata *); extern int nfs_permission(struct inode *, int, struct nameidata *);
extern void nfs_set_mmcred(struct inode *, struct rpc_cred *); extern void nfs_set_mmcred(struct inode *, struct rpc_cred *);
...@@ -241,6 +271,13 @@ extern int nfs_open(struct inode *, struct file *); ...@@ -241,6 +271,13 @@ extern int nfs_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
extern int nfs_setattr(struct dentry *, struct iattr *); extern int nfs_setattr(struct dentry *, struct iattr *);
extern void nfs_begin_attr_update(struct inode *);
extern void nfs_end_attr_update(struct inode *);
extern void nfs_begin_data_update(struct inode *);
extern void nfs_end_data_update(struct inode *);
/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */
extern u32 root_nfs_parse_addr(char *name); /*__init*/
/* /*
* linux/fs/nfs/file.c * linux/fs/nfs/file.c
...@@ -309,16 +346,15 @@ extern void nfs_commit_done(struct rpc_task *); ...@@ -309,16 +346,15 @@ extern void nfs_commit_done(struct rpc_task *);
* Try to write back everything synchronously (but check the * Try to write back everything synchronously (but check the
* return value!) * return value!)
*/ */
extern int nfs_sync_file(struct inode *, struct file *, unsigned long, unsigned int, int); extern int nfs_sync_inode(struct inode *, unsigned long, unsigned int, int);
extern int nfs_flush_file(struct inode *, struct file *, unsigned long, unsigned int, int); extern int nfs_flush_inode(struct inode *, unsigned long, unsigned int, int);
extern int nfs_flush_list(struct list_head *, int, int); extern int nfs_flush_list(struct list_head *, int, int);
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
extern int nfs_commit_file(struct inode *, struct file *, unsigned long, unsigned int, int); extern int nfs_commit_inode(struct inode *, unsigned long, unsigned int, int);
extern int nfs_commit_list(struct list_head *, int); extern int nfs_commit_list(struct list_head *, int);
#else #else
static inline int static inline int
nfs_commit_file(struct inode *inode, struct file *file, unsigned long offset, nfs_commit_inode(struct inode *inode, unsigned long idx_start, unsigned int npages, int how)
unsigned int len, int flags)
{ {
return 0; return 0;
} }
...@@ -333,7 +369,7 @@ nfs_have_writebacks(struct inode *inode) ...@@ -333,7 +369,7 @@ nfs_have_writebacks(struct inode *inode)
static inline int static inline int
nfs_wb_all(struct inode *inode) nfs_wb_all(struct inode *inode)
{ {
int error = nfs_sync_file(inode, 0, 0, 0, FLUSH_WAIT); int error = nfs_sync_inode(inode, 0, 0, FLUSH_WAIT);
return (error < 0) ? error : 0; return (error < 0) ? error : 0;
} }
...@@ -343,21 +379,11 @@ nfs_wb_all(struct inode *inode) ...@@ -343,21 +379,11 @@ nfs_wb_all(struct inode *inode)
static inline int static inline int
nfs_wb_page(struct inode *inode, struct page* page) nfs_wb_page(struct inode *inode, struct page* page)
{ {
int error = nfs_sync_file(inode, 0, page->index, 1, int error = nfs_sync_inode(inode, page->index, 1,
FLUSH_WAIT | FLUSH_STABLE); FLUSH_WAIT | FLUSH_STABLE);
return (error < 0) ? error : 0; return (error < 0) ? error : 0;
} }
/*
* Write back all pending writes for one user..
*/
static inline int
nfs_wb_file(struct inode *inode, struct file *file)
{
int error = nfs_sync_file(inode, file, 0, 0, FLUSH_WAIT);
return (error < 0) ? error : 0;
}
/* Hack for future NFS swap support */ /* Hack for future NFS swap support */
#ifndef IS_SWAPFILE #ifndef IS_SWAPFILE
# define IS_SWAPFILE(inode) (0) # define IS_SWAPFILE(inode) (0)
...@@ -383,20 +409,27 @@ extern int nfsroot_mount(struct sockaddr_in *, char *, struct nfs_fh *, ...@@ -383,20 +409,27 @@ extern int nfsroot_mount(struct sockaddr_in *, char *, struct nfs_fh *,
/* /*
* inline functions * inline functions
*/ */
static inline int
nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) static inline int nfs_attribute_timeout(struct inode *inode)
{ {
if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) struct nfs_inode *nfsi = NFS_I(inode);
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode); return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo);
} }
static inline int /**
nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) * nfs_revalidate_inode - Revalidate the inode attributes
* @server - pointer to nfs_server struct
* @inode - pointer to inode struct
*
* Updates inode attribute information by retrieving the data from the server.
*/
static inline int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{ {
if ((fattr->valid & NFS_ATTR_FATTR) == 0) if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
return 0; && !nfs_attribute_timeout(inode))
return __nfs_refresh_inode(inode,fattr); return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
} }
static inline loff_t static inline loff_t
...@@ -661,7 +694,7 @@ struct nfs4_mount_data; ...@@ -661,7 +694,7 @@ struct nfs4_mount_data;
#ifdef __KERNEL__ #ifdef __KERNEL__
# undef ifdebug # undef ifdebug
# ifdef NFS_DEBUG # ifdef NFS_DEBUG
# define ifdebug(fac) if (nfs_debug & NFSDBG_##fac) # define ifdebug(fac) if (unlikely(nfs_debug & NFSDBG_##fac))
# else # else
# define ifdebug(fac) if (0) # define ifdebug(fac) if (0)
# endif # endif
......
...@@ -53,7 +53,7 @@ extern void nfs_release_request(struct nfs_page *req); ...@@ -53,7 +53,7 @@ extern void nfs_release_request(struct nfs_page *req);
extern void nfs_list_add_request(struct nfs_page *, struct list_head *); extern void nfs_list_add_request(struct nfs_page *, struct list_head *);
extern int nfs_scan_list(struct list_head *, struct list_head *, extern int nfs_scan_list(struct list_head *, struct list_head *,
struct file *, unsigned long, unsigned int); unsigned long, unsigned int);
extern int nfs_coalesce_requests(struct list_head *, struct list_head *, extern int nfs_coalesce_requests(struct list_head *, struct list_head *,
unsigned int); unsigned int);
extern int nfs_wait_on_request(struct nfs_page *); extern int nfs_wait_on_request(struct nfs_page *);
......
...@@ -700,7 +700,7 @@ struct nfs_rpc_ops { ...@@ -700,7 +700,7 @@ struct nfs_rpc_ops {
struct inode_operations *dir_inode_ops; struct inode_operations *dir_inode_ops;
int (*getroot) (struct nfs_server *, struct nfs_fh *, int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *); struct nfs_fsinfo *);
int (*getattr) (struct inode *, struct nfs_fattr *); int (*getattr) (struct inode *, struct nfs_fattr *);
int (*setattr) (struct dentry *, struct nfs_fattr *, int (*setattr) (struct dentry *, struct nfs_fattr *,
struct iattr *); struct iattr *);
......
...@@ -54,7 +54,7 @@ extern unsigned int nlm_debug; ...@@ -54,7 +54,7 @@ extern unsigned int nlm_debug;
#undef ifdebug #undef ifdebug
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
# define ifdebug(fac) if (rpc_debug & RPCDBG_##fac) # define ifdebug(fac) if (unlikely(rpc_debug & RPCDBG_##fac))
# define dfprintk(fac, args...) do { ifdebug(fac) printk(args); } while(0) # define dfprintk(fac, args...) do { ifdebug(fac) printk(args); } while(0)
# define RPC_IFDEBUG(x) x # define RPC_IFDEBUG(x) x
#else #else
...@@ -92,6 +92,8 @@ enum { ...@@ -92,6 +92,8 @@ enum {
CTL_NFSDEBUG, CTL_NFSDEBUG,
CTL_NFSDDEBUG, CTL_NFSDDEBUG,
CTL_NLMDEBUG, CTL_NLMDEBUG,
CTL_SLOTTABLE_UDP,
CTL_SLOTTABLE_TCP,
}; };
#endif /* _LINUX_SUNRPC_DEBUG_H_ */ #endif /* _LINUX_SUNRPC_DEBUG_H_ */
...@@ -25,9 +25,18 @@ extern unsigned long rpc_calc_rto(struct rpc_rtt *rt, unsigned timer); ...@@ -25,9 +25,18 @@ extern unsigned long rpc_calc_rto(struct rpc_rtt *rt, unsigned timer);
static inline void rpc_set_timeo(struct rpc_rtt *rt, int timer, int ntimeo) static inline void rpc_set_timeo(struct rpc_rtt *rt, int timer, int ntimeo)
{ {
int *t;
if (!timer) if (!timer)
return; return;
rt->ntimeouts[timer-1] = ntimeo; t = &rt->ntimeouts[timer-1];
if (ntimeo < *t) {
if (*t > 0)
(*t)--;
} else {
if (ntimeo > 8)
ntimeo = 8;
*t = ntimeo;
}
} }
static inline int rpc_ntimeo(struct rpc_rtt *rt, int timer) static inline int rpc_ntimeo(struct rpc_rtt *rt, int timer)
......
...@@ -87,7 +87,7 @@ struct xdr_buf { ...@@ -87,7 +87,7 @@ struct xdr_buf {
/* /*
* Miscellaneous XDR helper functions * Miscellaneous XDR helper functions
*/ */
u32 * xdr_encode_array(u32 *p, const char *s, unsigned int len); u32 * xdr_encode_array(u32 *p, const void *s, unsigned int len);
u32 * xdr_encode_string(u32 *p, const char *s); u32 * xdr_encode_string(u32 *p, const char *s);
u32 * xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen); u32 * xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen);
u32 * xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen); u32 * xdr_decode_string_inplace(u32 *p, char **sp, int *lenp, int maxlen);
......
...@@ -28,16 +28,18 @@ ...@@ -28,16 +28,18 @@
* *
* Upper procedures may check whether a request would block waiting for * Upper procedures may check whether a request would block waiting for
* a free RPC slot by using the RPC_CONGESTED() macro. * a free RPC slot by using the RPC_CONGESTED() macro.
*
* Note: on machines with low memory we should probably use a smaller
* MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment
* reassembly will frequently run out of memory.
*/ */
#define RPC_MAXCONG (16) extern unsigned int xprt_udp_slot_table_entries;
#define RPC_MAXREQS RPC_MAXCONG extern unsigned int xprt_tcp_slot_table_entries;
#define RPC_CWNDSCALE (256)
#define RPC_MAXCWND (RPC_MAXCONG * RPC_CWNDSCALE) #define RPC_MIN_SLOT_TABLE (2U)
#define RPC_DEF_SLOT_TABLE (16U)
#define RPC_MAX_SLOT_TABLE (128U)
#define RPC_CWNDSHIFT (8U)
#define RPC_CWNDSCALE (1U << RPC_CWNDSHIFT)
#define RPC_INITCWND RPC_CWNDSCALE #define RPC_INITCWND RPC_CWNDSCALE
#define RPC_MAXCWND(xprt) ((xprt)->max_reqs << RPC_CWNDSHIFT)
#define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd) #define RPCXPRT_CONGESTED(xprt) ((xprt)->cong >= (xprt)->cwnd)
/* Default timeout values */ /* Default timeout values */
...@@ -92,7 +94,6 @@ struct rpc_rqst { ...@@ -92,7 +94,6 @@ struct rpc_rqst {
*/ */
struct rpc_task * rq_task; /* RPC task data */ struct rpc_task * rq_task; /* RPC task data */
__u32 rq_xid; /* request XID */ __u32 rq_xid; /* request XID */
struct rpc_rqst * rq_next; /* free list */
int rq_cong; /* has incremented xprt->cong */ int rq_cong; /* has incremented xprt->cong */
int rq_received; /* receive completed */ int rq_received; /* receive completed */
u32 rq_seqno; /* gss seq no. used on req. */ u32 rq_seqno; /* gss seq no. used on req. */
...@@ -102,7 +103,6 @@ struct rpc_rqst { ...@@ -102,7 +103,6 @@ struct rpc_rqst {
struct xdr_buf rq_private_buf; /* The receive buffer struct xdr_buf rq_private_buf; /* The receive buffer
* used in the softirq. * used in the softirq.
*/ */
/* /*
* For authentication (e.g. auth_des) * For authentication (e.g. auth_des)
*/ */
...@@ -146,14 +146,20 @@ struct rpc_xprt { ...@@ -146,14 +146,20 @@ struct rpc_xprt {
struct rpc_wait_queue resend; /* requests waiting to resend */ struct rpc_wait_queue resend; /* requests waiting to resend */
struct rpc_wait_queue pending; /* requests in flight */ struct rpc_wait_queue pending; /* requests in flight */
struct rpc_wait_queue backlog; /* waiting for slot */ struct rpc_wait_queue backlog; /* waiting for slot */
struct rpc_rqst * free; /* free slots */ struct list_head free; /* free slots */
struct rpc_rqst slot[RPC_MAXREQS]; struct rpc_rqst * slot; /* slot table storage */
unsigned int max_reqs; /* total slots */
unsigned long sockstate; /* Socket state */ unsigned long sockstate; /* Socket state */
unsigned char shutdown : 1, /* being shut down */ unsigned char shutdown : 1, /* being shut down */
nocong : 1, /* no congestion control */ nocong : 1, /* no congestion control */
resvport : 1, /* use a reserved port */ resvport : 1, /* use a reserved port */
stream : 1; /* TCP */ stream : 1; /* TCP */
/*
* XID
*/
__u32 xid; /* Next XID value to use */
/* /*
* State of TCP reply receive stuff * State of TCP reply receive stuff
*/ */
...@@ -163,6 +169,11 @@ struct rpc_xprt { ...@@ -163,6 +169,11 @@ struct rpc_xprt {
tcp_offset; /* fragment offset */ tcp_offset; /* fragment offset */
unsigned long tcp_copied, /* copied to request */ unsigned long tcp_copied, /* copied to request */
tcp_flags; tcp_flags;
/*
* Connection of sockets
*/
struct work_struct sock_connect;
unsigned short port;
/* /*
* Disconnection of idle sockets * Disconnection of idle sockets
*/ */
......
...@@ -1188,6 +1188,40 @@ static struct file_operations pnp_seq_fops = { ...@@ -1188,6 +1188,40 @@ static struct file_operations pnp_seq_fops = {
}; };
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
/*
* Extract IP address from the parameter string if needed. Note that we
* need to have root_server_addr set _before_ IPConfig gets called as it
* can override it.
*/
u32 __init root_nfs_parse_addr(char *name)
{
u32 addr;
int octets = 0;
char *cp, *cq;
cp = cq = name;
while (octets < 4) {
while (*cp >= '0' && *cp <= '9')
cp++;
if (cp == cq || cp - cq > 3)
break;
if (*cp == '.' || octets == 3)
octets++;
if (octets < 4)
cp++;
cq = cp;
}
if (octets == 4 && (*cp == ':' || *cp == '\0')) {
if (*cp == ':')
*cp++ = '\0';
addr = in_aton(name);
strcpy(name, cp);
} else
addr = INADDR_NONE;
return addr;
}
/* /*
* IP Autoconfig dispatcher. * IP Autoconfig dispatcher.
*/ */
...@@ -1195,6 +1229,7 @@ static struct file_operations pnp_seq_fops = { ...@@ -1195,6 +1229,7 @@ static struct file_operations pnp_seq_fops = {
static int __init ip_auto_config(void) static int __init ip_auto_config(void)
{ {
unsigned long jiff; unsigned long jiff;
u32 addr;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_net_fops_create("pnp", S_IRUGO, &pnp_seq_fops); proc_net_fops_create("pnp", S_IRUGO, &pnp_seq_fops);
...@@ -1283,6 +1318,10 @@ static int __init ip_auto_config(void) ...@@ -1283,6 +1318,10 @@ static int __init ip_auto_config(void)
ic_dev = ic_first_dev->dev; ic_dev = ic_first_dev->dev;
} }
addr = root_nfs_parse_addr(root_server_path);
if (root_server_addr == INADDR_NONE)
root_server_addr = addr;
/* /*
* Use defaults whereever applicable. * Use defaults whereever applicable.
*/ */
......
...@@ -149,7 +149,7 @@ unx_marshal(struct rpc_task *task, u32 *p, int ruid) ...@@ -149,7 +149,7 @@ unx_marshal(struct rpc_task *task, u32 *p, int ruid)
struct rpc_clnt *clnt = task->tk_client; struct rpc_clnt *clnt = task->tk_client;
struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred; struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred;
u32 *base, *hold; u32 *base, *hold;
int i, n; int i;
*p++ = htonl(RPC_AUTH_UNIX); *p++ = htonl(RPC_AUTH_UNIX);
base = p++; base = p++;
...@@ -158,10 +158,7 @@ unx_marshal(struct rpc_task *task, u32 *p, int ruid) ...@@ -158,10 +158,7 @@ unx_marshal(struct rpc_task *task, u32 *p, int ruid)
/* /*
* Copy the UTS nodename captured when the client was created. * Copy the UTS nodename captured when the client was created.
*/ */
n = clnt->cl_nodelen; p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
*p++ = htonl(n);
memcpy(p, clnt->cl_nodename, n);
p += (n + 3) >> 2;
/* Note: we don't use real uid if it involves raising privilege */ /* Note: we don't use real uid if it involves raising privilege */
if (ruid && cred->uc_puid != 0 && cred->uc_pgid != 0) { if (ruid && cred->uc_puid != 0 && cred->uc_pgid != 0) {
......
...@@ -102,19 +102,22 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, ...@@ -102,19 +102,22 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
{ {
struct rpc_version *version; struct rpc_version *version;
struct rpc_clnt *clnt = NULL; struct rpc_clnt *clnt = NULL;
int err;
int len; int len;
dprintk("RPC: creating %s client for %s (xprt %p)\n", dprintk("RPC: creating %s client for %s (xprt %p)\n",
program->name, servname, xprt); program->name, servname, xprt);
err = -EINVAL;
if (!xprt) if (!xprt)
goto out; goto out_err;
if (vers >= program->nrvers || !(version = program->version[vers])) if (vers >= program->nrvers || !(version = program->version[vers]))
goto out; goto out_err;
err = -ENOMEM;
clnt = (struct rpc_clnt *) kmalloc(sizeof(*clnt), GFP_KERNEL); clnt = (struct rpc_clnt *) kmalloc(sizeof(*clnt), GFP_KERNEL);
if (!clnt) if (!clnt)
goto out_no_clnt; goto out_err;
memset(clnt, 0, sizeof(*clnt)); memset(clnt, 0, sizeof(*clnt));
atomic_set(&clnt->cl_users, 0); atomic_set(&clnt->cl_users, 0);
atomic_set(&clnt->cl_count, 1); atomic_set(&clnt->cl_count, 1);
...@@ -149,9 +152,11 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, ...@@ -149,9 +152,11 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
clnt->cl_rtt = &clnt->cl_rtt_default; clnt->cl_rtt = &clnt->cl_rtt_default;
rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval); rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval);
if (rpc_setup_pipedir(clnt, program->pipe_dir_name) < 0) err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
if (err < 0)
goto out_no_path; goto out_no_path;
err = -ENOMEM;
if (!rpcauth_create(flavor, clnt)) { if (!rpcauth_create(flavor, clnt)) {
printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n", printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n",
flavor); flavor);
...@@ -163,20 +168,16 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, ...@@ -163,20 +168,16 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
if (clnt->cl_nodelen > UNX_MAXNODENAME) if (clnt->cl_nodelen > UNX_MAXNODENAME)
clnt->cl_nodelen = UNX_MAXNODENAME; clnt->cl_nodelen = UNX_MAXNODENAME;
memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen); memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen);
out:
return clnt; return clnt;
out_no_clnt:
printk(KERN_INFO "RPC: out of memory in rpc_create_client\n");
goto out;
out_no_auth: out_no_auth:
rpc_rmdir(clnt->cl_pathname); rpc_rmdir(clnt->cl_pathname);
out_no_path: out_no_path:
if (clnt->cl_server != clnt->cl_inline_name) if (clnt->cl_server != clnt->cl_inline_name)
kfree(clnt->cl_server); kfree(clnt->cl_server);
kfree(clnt); kfree(clnt);
clnt = NULL; out_err:
goto out; return ERR_PTR(err);
} }
/* /*
...@@ -198,11 +199,10 @@ rpc_clone_client(struct rpc_clnt *clnt) ...@@ -198,11 +199,10 @@ rpc_clone_client(struct rpc_clnt *clnt)
atomic_inc(&new->cl_parent->cl_count); atomic_inc(&new->cl_parent->cl_count);
if (new->cl_auth) if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count); atomic_inc(&new->cl_auth->au_count);
out:
return new; return new;
out_no_clnt: out_no_clnt:
printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__); printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__);
goto out; return ERR_PTR(-ENOMEM);
} }
/* /*
...@@ -611,9 +611,6 @@ call_encode(struct rpc_task *task) ...@@ -611,9 +611,6 @@ call_encode(struct rpc_task *task)
rcvbuf->page_len = 0; rcvbuf->page_len = 0;
rcvbuf->len = bufsiz; rcvbuf->len = bufsiz;
/* Zero buffer so we have automatic zero-padding of opaque & string */
memset(task->tk_buffer, 0, bufsiz);
/* Encode header and provided arguments */ /* Encode header and provided arguments */
encode = task->tk_msg.rpc_proc->p_encode; encode = task->tk_msg.rpc_proc->p_encode;
if (!(p = call_header(task))) { if (!(p = call_header(task))) {
......
...@@ -65,9 +65,11 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) ...@@ -65,9 +65,11 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
map->pm_binding = 1; map->pm_binding = 1;
spin_unlock(&pmap_lock); spin_unlock(&pmap_lock);
task->tk_status = -EACCES; /* why set this? returns -EIO below */ pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot);
if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) if (IS_ERR(pmap_clnt)) {
task->tk_status = PTR_ERR(pmap_clnt);
goto bailout; goto bailout;
}
task->tk_status = 0; task->tk_status = 0;
/* /*
...@@ -110,8 +112,9 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot) ...@@ -110,8 +112,9 @@ rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot); NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
if (!(pmap_clnt = pmap_create(hostname, sin, prot))) pmap_clnt = pmap_create(hostname, sin, prot);
return -EACCES; if (IS_ERR(pmap_clnt))
return PTR_ERR(pmap_clnt);
/* Setup the call info struct */ /* Setup the call info struct */
status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0); status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0);
...@@ -161,16 +164,18 @@ rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) ...@@ -161,16 +164,18 @@ rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
struct sockaddr_in sin; struct sockaddr_in sin;
struct rpc_portmap map; struct rpc_portmap map;
struct rpc_clnt *pmap_clnt; struct rpc_clnt *pmap_clnt;
unsigned int error = 0; int error = 0;
dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n", dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n",
prog, vers, prot, port); prog, vers, prot, port);
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (!(pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP))) { pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP);
dprintk("RPC: couldn't create pmap client\n"); if (IS_ERR(pmap_clnt)) {
return -EACCES; error = PTR_ERR(pmap_clnt);
dprintk("RPC: couldn't create pmap client. Error = %d\n", error);
return error;
} }
map.pm_prog = prog; map.pm_prog = prog;
...@@ -199,15 +204,16 @@ pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto) ...@@ -199,15 +204,16 @@ pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto)
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
/* printk("pmap: create xprt\n"); */ /* printk("pmap: create xprt\n"); */
if (!(xprt = xprt_create_proto(proto, srvaddr, NULL))) xprt = xprt_create_proto(proto, srvaddr, NULL);
return NULL; if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
xprt->addr.sin_port = htons(RPC_PMAP_PORT); xprt->addr.sin_port = htons(RPC_PMAP_PORT);
/* printk("pmap: create clnt\n"); */ /* printk("pmap: create clnt\n"); */
clnt = rpc_create_client(xprt, hostname, clnt = rpc_create_client(xprt, hostname,
&pmap_program, RPC_PMAP_VERSION, &pmap_program, RPC_PMAP_VERSION,
RPC_AUTH_NULL); RPC_AUTH_NULL);
if (!clnt) { if (IS_ERR(clnt)) {
xprt_destroy(xprt); xprt_destroy(xprt);
} else { } else {
clnt->cl_softrtry = 1; clnt->cl_softrtry = 1;
......
...@@ -530,6 +530,9 @@ __rpc_execute(struct rpc_task *task) ...@@ -530,6 +530,9 @@ __rpc_execute(struct rpc_task *task)
if (!task->tk_action) if (!task->tk_action)
break; break;
task->tk_action(task); task->tk_action(task);
/* micro-optimization to avoid spinlock */
if (RPC_IS_RUNNING(task))
continue;
} }
/* /*
...@@ -545,29 +548,31 @@ __rpc_execute(struct rpc_task *task) ...@@ -545,29 +548,31 @@ __rpc_execute(struct rpc_task *task)
} }
spin_unlock_bh(&rpc_queue_lock); spin_unlock_bh(&rpc_queue_lock);
while (RPC_IS_SLEEPING(task)) { if (!RPC_IS_SLEEPING(task))
/* sync task: sleep here */ continue;
dprintk("RPC: %4d sync task going to sleep\n", /* sync task: sleep here */
task->tk_pid); dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid);
if (current->pid == rpciod_pid) if (current->pid == rpciod_pid)
printk(KERN_ERR "RPC: rpciod waiting on sync task!\n"); printk(KERN_ERR "RPC: rpciod waiting on sync task!\n");
if (!task->tk_client->cl_intr) {
__wait_event(task->tk_wait, !RPC_IS_SLEEPING(task)); __wait_event(task->tk_wait, !RPC_IS_SLEEPING(task));
dprintk("RPC: %4d sync task resuming\n", task->tk_pid); } else {
__wait_event_interruptible(task->tk_wait, !RPC_IS_SLEEPING(task), status);
/* /*
* When a sync task receives a signal, it exits with * When a sync task receives a signal, it exits with
* -ERESTARTSYS. In order to catch any callbacks that * -ERESTARTSYS. In order to catch any callbacks that
* clean up after sleeping on some queue, we don't * clean up after sleeping on some queue, we don't
* break the loop here, but go around once more. * break the loop here, but go around once more.
*/ */
if (task->tk_client->cl_intr && signalled()) { if (status == -ERESTARTSYS) {
dprintk("RPC: %4d got signal\n", task->tk_pid); dprintk("RPC: %4d got signal\n", task->tk_pid);
task->tk_flags |= RPC_TASK_KILLED; task->tk_flags |= RPC_TASK_KILLED;
rpc_exit(task, -ERESTARTSYS); rpc_exit(task, -ERESTARTSYS);
rpc_wake_up_task(task); rpc_wake_up_task(task);
} }
} }
dprintk("RPC: %4d sync task resuming\n", task->tk_pid);
} }
if (task->tk_exit) { if (task->tk_exit) {
......
...@@ -63,6 +63,8 @@ EXPORT_SYMBOL(rpc_mkpipe); ...@@ -63,6 +63,8 @@ EXPORT_SYMBOL(rpc_mkpipe);
EXPORT_SYMBOL(xprt_create_proto); EXPORT_SYMBOL(xprt_create_proto);
EXPORT_SYMBOL(xprt_destroy); EXPORT_SYMBOL(xprt_destroy);
EXPORT_SYMBOL(xprt_set_timeout); EXPORT_SYMBOL(xprt_set_timeout);
EXPORT_SYMBOL(xprt_udp_slot_table_entries);
EXPORT_SYMBOL(xprt_tcp_slot_table_entries);
/* Client credential cache */ /* Client credential cache */
EXPORT_SYMBOL(rpcauth_register); EXPORT_SYMBOL(rpcauth_register);
......
/* /*
* linux/net/sunrpc/sysctl.c * linux/net/sunrpc/sysctl.c
* *
* Sysctl interface to sunrpc module. This is for debugging only now. * Sysctl interface to sunrpc module.
* *
* I would prefer to register the sunrpc table below sys/net, but that's * I would prefer to register the sunrpc table below sys/net, but that's
* impossible at the moment. * impossible at the moment.
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/sunrpc/types.h> #include <linux/sunrpc/types.h>
#include <linux/sunrpc/sched.h> #include <linux/sunrpc/sched.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/sunrpc/xprt.h>
/* /*
* Declare the debug flags here * Declare the debug flags here
...@@ -117,6 +118,9 @@ proc_dodebug(ctl_table *table, int write, struct file *file, ...@@ -117,6 +118,9 @@ proc_dodebug(ctl_table *table, int write, struct file *file,
return 0; return 0;
} }
static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
static ctl_table debug_table[] = { static ctl_table debug_table[] = {
{ {
.ctl_name = CTL_RPCDEBUG, .ctl_name = CTL_RPCDEBUG,
...@@ -150,6 +154,28 @@ static ctl_table debug_table[] = { ...@@ -150,6 +154,28 @@ static ctl_table debug_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dodebug .proc_handler = &proc_dodebug
}, },
{
.ctl_name = CTL_SLOTTABLE_UDP,
.procname = "udp_slot_table_entries",
.data = &xprt_udp_slot_table_entries,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_slot_table_size,
.extra2 = &max_slot_table_size
},
{
.ctl_name = CTL_SLOTTABLE_TCP,
.procname = "tcp_slot_table_entries",
.data = &xprt_tcp_slot_table_entries,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_slot_table_size,
.extra2 = &max_slot_table_size
},
{ .ctl_name = 0 } { .ctl_name = 0 }
}; };
......
...@@ -54,7 +54,7 @@ xdr_decode_netobj(u32 *p, struct xdr_netobj *obj) ...@@ -54,7 +54,7 @@ xdr_decode_netobj(u32 *p, struct xdr_netobj *obj)
} }
u32 * u32 *
xdr_encode_array(u32 *p, const char *array, unsigned int len) xdr_encode_array(u32 *p, const void *array, unsigned int len)
{ {
int quadlen = XDR_QUADLEN(len); int quadlen = XDR_QUADLEN(len);
......
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/random.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/checksum.h> #include <net/checksum.h>
...@@ -74,6 +75,7 @@ ...@@ -74,6 +75,7 @@
#define XPRT_MAX_BACKOFF (8) #define XPRT_MAX_BACKOFF (8)
#define XPRT_IDLE_TIMEOUT (5*60*HZ) #define XPRT_IDLE_TIMEOUT (5*60*HZ)
#define XPRT_MAX_RESVPORT (800)
/* /*
* Local functions * Local functions
...@@ -84,7 +86,7 @@ static void xprt_disconnect(struct rpc_xprt *); ...@@ -84,7 +86,7 @@ static void xprt_disconnect(struct rpc_xprt *);
static void xprt_connect_status(struct rpc_task *task); static void xprt_connect_status(struct rpc_task *task);
static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap, static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap,
struct rpc_timeout *to); struct rpc_timeout *to);
static struct socket *xprt_create_socket(int, struct rpc_timeout *, int); static struct socket *xprt_create_socket(struct rpc_xprt *, int, int);
static void xprt_bind_socket(struct rpc_xprt *, struct socket *); static void xprt_bind_socket(struct rpc_xprt *, struct socket *);
static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *);
...@@ -336,8 +338,8 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, int result) ...@@ -336,8 +338,8 @@ xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
/* The (cwnd >> 1) term makes sure /* The (cwnd >> 1) term makes sure
* the result gets rounded properly. */ * the result gets rounded properly. */
cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd; cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd;
if (cwnd > RPC_MAXCWND) if (cwnd > RPC_MAXCWND(xprt))
cwnd = RPC_MAXCWND; cwnd = RPC_MAXCWND(xprt);
__xprt_lock_write_next(xprt); __xprt_lock_write_next(xprt);
} else if (result == -ETIMEDOUT) { } else if (result == -ETIMEDOUT) {
cwnd >>= 1; cwnd >>= 1;
...@@ -452,17 +454,74 @@ xprt_init_autodisconnect(unsigned long data) ...@@ -452,17 +454,74 @@ xprt_init_autodisconnect(unsigned long data)
spin_unlock(&xprt->sock_lock); spin_unlock(&xprt->sock_lock);
} }
static void xprt_socket_connect(void *args)
{
struct rpc_xprt *xprt = (struct rpc_xprt *)args;
struct socket *sock = xprt->sock;
int status = -EIO;
if (xprt->shutdown) {
rpc_wake_up_status(&xprt->pending, -EIO);
return;
}
if (!xprt->addr.sin_port)
goto out_err;
/*
* Start by resetting any existing state
*/
xprt_close(xprt);
sock = xprt_create_socket(xprt, xprt->prot, xprt->resvport);
if (sock == NULL) {
/* couldn't create socket or bind to reserved port;
* this is likely a permanent error, so cause an abort */
goto out_err;
return;
}
xprt_bind_socket(xprt, sock);
xprt_sock_setbufsize(xprt);
if (!xprt->stream)
goto out;
/*
* Tell the socket layer to start connecting...
*/
status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
sizeof(xprt->addr), O_NONBLOCK);
dprintk("RPC: %p connect status %d connected %d sock state %d\n",
xprt, -status, xprt_connected(xprt), sock->sk->sk_state);
if (status >= 0)
goto out;
switch (status) {
case -EINPROGRESS:
case -EALREADY:
return;
default:
goto out_err;
}
out:
spin_lock_bh(&xprt->sock_lock);
if (xprt->snd_task)
rpc_wake_up_task(xprt->snd_task);
spin_unlock_bh(&xprt->sock_lock);
return;
out_err:
spin_lock_bh(&xprt->sock_lock);
if (xprt->snd_task) {
xprt->snd_task->tk_status = status;
rpc_wake_up_task(xprt->snd_task);
}
spin_unlock_bh(&xprt->sock_lock);
}
/* /*
* Attempt to connect a TCP socket. * Attempt to connect a TCP socket.
* *
*/ */
void void xprt_connect(struct rpc_task *task)
xprt_connect(struct rpc_task *task)
{ {
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_xprt;
struct socket *sock = xprt->sock;
struct sock *inet;
int status;
dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid, dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid,
xprt, (xprt_connected(xprt) ? "is" : "is not")); xprt, (xprt_connected(xprt) ? "is" : "is not"));
...@@ -483,79 +542,9 @@ xprt_connect(struct rpc_task *task) ...@@ -483,79 +542,9 @@ xprt_connect(struct rpc_task *task)
if (task->tk_rqstp) if (task->tk_rqstp)
task->tk_rqstp->rq_bytes_sent = 0; task->tk_rqstp->rq_bytes_sent = 0;
/* task->tk_timeout = RPC_CONNECT_TIMEOUT;
* We're here because the xprt was marked disconnected. rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL);
* Start by resetting any existing state. schedule_work(&xprt->sock_connect);
*/
xprt_close(xprt);
if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout, xprt->resvport))) {
/* couldn't create socket or bind to reserved port;
* this is likely a permanent error, so cause an abort */
task->tk_status = -EIO;
goto out_write;
}
xprt_bind_socket(xprt, sock);
xprt_sock_setbufsize(xprt);
if (!xprt->stream)
goto out_write;
inet = sock->sk;
/*
* Tell the socket layer to start connecting...
*/
status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
sizeof(xprt->addr), O_NONBLOCK);
dprintk("RPC: %4d connect status %d connected %d sock state %d\n",
task->tk_pid, -status, xprt_connected(xprt), inet->sk_state);
if (status >= 0)
return;
switch (status) {
case -EINPROGRESS:
case -EALREADY:
/* Protect against TCP socket state changes */
lock_sock(inet);
if (inet->sk_state != TCP_ESTABLISHED) {
dprintk("RPC: %4d waiting for connection\n",
task->tk_pid);
task->tk_timeout = RPC_CONNECT_TIMEOUT;
/* if the socket is already closing, delay briefly */
if ((1 << inet->sk_state) &
~(TCPF_SYN_SENT | TCPF_SYN_RECV))
task->tk_timeout = RPC_REESTABLISH_TIMEOUT;
rpc_sleep_on(&xprt->pending, task, xprt_connect_status,
NULL);
}
release_sock(inet);
break;
case -ECONNREFUSED:
case -ECONNRESET:
case -ENOTCONN:
if (!RPC_IS_SOFT(task)) {
rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
task->tk_status = -ENOTCONN;
break;
}
default:
/* Report myriad other possible returns. If this file
* system is soft mounted, just error out, like Solaris. */
if (RPC_IS_SOFT(task)) {
printk(KERN_WARNING
"RPC: error %d connecting to server %s, exiting\n",
-status, task->tk_client->cl_server);
task->tk_status = -EIO;
goto out_write;
}
printk(KERN_WARNING "RPC: error %d connecting to server %s\n",
-status, task->tk_client->cl_server);
/* This will prevent anybody else from reconnecting */
rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
task->tk_status = status;
break;
}
return; return;
out_write: out_write:
xprt_release_write(xprt, task); xprt_release_write(xprt, task);
...@@ -580,6 +569,8 @@ xprt_connect_status(struct rpc_task *task) ...@@ -580,6 +569,8 @@ xprt_connect_status(struct rpc_task *task)
task->tk_status = -EIO; task->tk_status = -EIO;
switch (task->tk_status) { switch (task->tk_status) {
case -ECONNREFUSED:
case -ECONNRESET:
case -ENOTCONN: case -ENOTCONN:
rpc_delay(task, RPC_REESTABLISH_TIMEOUT); rpc_delay(task, RPC_REESTABLISH_TIMEOUT);
return; return;
...@@ -1313,10 +1304,9 @@ do_xprt_reserve(struct rpc_task *task) ...@@ -1313,10 +1304,9 @@ do_xprt_reserve(struct rpc_task *task)
task->tk_status = 0; task->tk_status = 0;
if (task->tk_rqstp) if (task->tk_rqstp)
return; return;
if (xprt->free) { if (!list_empty(&xprt->free)) {
struct rpc_rqst *req = xprt->free; struct rpc_rqst *req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
xprt->free = req->rq_next; list_del_init(&req->rq_list);
req->rq_next = NULL;
task->tk_rqstp = req; task->tk_rqstp = req;
xprt_request_init(task, xprt); xprt_request_init(task, xprt);
return; return;
...@@ -1330,22 +1320,14 @@ do_xprt_reserve(struct rpc_task *task) ...@@ -1330,22 +1320,14 @@ do_xprt_reserve(struct rpc_task *task)
/* /*
* Allocate a 'unique' XID * Allocate a 'unique' XID
*/ */
static u32 static inline u32 xprt_alloc_xid(struct rpc_xprt *xprt)
xprt_alloc_xid(void)
{ {
static spinlock_t xid_lock = SPIN_LOCK_UNLOCKED; return xprt->xid++;
static int need_init = 1; }
static u32 xid;
u32 ret; static inline void xprt_init_xid(struct rpc_xprt *xprt)
{
spin_lock(&xid_lock); get_random_bytes(&xprt->xid, sizeof(xprt->xid));
if (unlikely(need_init)) {
xid = get_seconds() << 12;
need_init = 0;
}
ret = xid++;
spin_unlock(&xid_lock);
return ret;
} }
/* /*
...@@ -1359,8 +1341,7 @@ xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) ...@@ -1359,8 +1341,7 @@ xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
req->rq_timeout = xprt->timeout; req->rq_timeout = xprt->timeout;
req->rq_task = task; req->rq_task = task;
req->rq_xprt = xprt; req->rq_xprt = xprt;
req->rq_xid = xprt_alloc_xid(); req->rq_xid = xprt_alloc_xid(xprt);
INIT_LIST_HEAD(&req->rq_list);
dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid,
req, req->rq_xid); req, req->rq_xid);
} }
...@@ -1391,9 +1372,7 @@ xprt_release(struct rpc_task *task) ...@@ -1391,9 +1372,7 @@ xprt_release(struct rpc_task *task)
dprintk("RPC: %4d release request %p\n", task->tk_pid, req); dprintk("RPC: %4d release request %p\n", task->tk_pid, req);
spin_lock(&xprt->xprt_lock); spin_lock(&xprt->xprt_lock);
req->rq_next = xprt->free; list_add(&req->rq_list, &xprt->free);
xprt->free = req;
xprt_clear_backlog(xprt); xprt_clear_backlog(xprt);
spin_unlock(&xprt->xprt_lock); spin_unlock(&xprt->xprt_lock);
} }
...@@ -1424,6 +1403,9 @@ xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr) ...@@ -1424,6 +1403,9 @@ xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr)
to->to_exponential = 0; to->to_exponential = 0;
} }
unsigned int xprt_udp_slot_table_entries = RPC_DEF_SLOT_TABLE;
unsigned int xprt_tcp_slot_table_entries = RPC_DEF_SLOT_TABLE;
/* /*
* Initialize an RPC client * Initialize an RPC client
*/ */
...@@ -1431,21 +1413,33 @@ static struct rpc_xprt * ...@@ -1431,21 +1413,33 @@ static struct rpc_xprt *
xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to)
{ {
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
unsigned int entries;
size_t slot_table_size;
struct rpc_rqst *req; struct rpc_rqst *req;
int i;
dprintk("RPC: setting up %s transport...\n", dprintk("RPC: setting up %s transport...\n",
proto == IPPROTO_UDP? "UDP" : "TCP"); proto == IPPROTO_UDP? "UDP" : "TCP");
entries = (proto == IPPROTO_TCP)?
xprt_tcp_slot_table_entries : xprt_udp_slot_table_entries;
if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL) if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL)
return NULL; return ERR_PTR(-ENOMEM);
memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */ memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */
xprt->max_reqs = entries;
slot_table_size = entries * sizeof(xprt->slot[0]);
xprt->slot = kmalloc(slot_table_size, GFP_KERNEL);
if (xprt->slot == NULL) {
kfree(xprt);
return ERR_PTR(-ENOMEM);
}
memset(xprt->slot, 0, slot_table_size);
xprt->addr = *ap; xprt->addr = *ap;
xprt->prot = proto; xprt->prot = proto;
xprt->stream = (proto == IPPROTO_TCP)? 1 : 0; xprt->stream = (proto == IPPROTO_TCP)? 1 : 0;
if (xprt->stream) { if (xprt->stream) {
xprt->cwnd = RPC_MAXCWND; xprt->cwnd = RPC_MAXCWND(xprt);
xprt->nocong = 1; xprt->nocong = 1;
} else } else
xprt->cwnd = RPC_INITCWND; xprt->cwnd = RPC_INITCWND;
...@@ -1453,12 +1447,15 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) ...@@ -1453,12 +1447,15 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to)
spin_lock_init(&xprt->xprt_lock); spin_lock_init(&xprt->xprt_lock);
init_waitqueue_head(&xprt->cong_wait); init_waitqueue_head(&xprt->cong_wait);
INIT_LIST_HEAD(&xprt->free);
INIT_LIST_HEAD(&xprt->recv); INIT_LIST_HEAD(&xprt->recv);
INIT_WORK(&xprt->sock_connect, xprt_socket_connect, xprt);
INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt); INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt);
init_timer(&xprt->timer); init_timer(&xprt->timer);
xprt->timer.function = xprt_init_autodisconnect; xprt->timer.function = xprt_init_autodisconnect;
xprt->timer.data = (unsigned long) xprt; xprt->timer.data = (unsigned long) xprt;
xprt->last_used = jiffies; xprt->last_used = jiffies;
xprt->port = XPRT_MAX_RESVPORT;
/* Set timeout parameters */ /* Set timeout parameters */
if (to) { if (to) {
...@@ -1473,15 +1470,16 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) ...@@ -1473,15 +1470,16 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to)
INIT_RPC_WAITQ(&xprt->backlog, "xprt_backlog"); INIT_RPC_WAITQ(&xprt->backlog, "xprt_backlog");
/* initialize free list */ /* initialize free list */
for (i = 0, req = xprt->slot; i < RPC_MAXREQS-1; i++, req++) for (req = &xprt->slot[entries-1]; req >= &xprt->slot[0]; req--)
req->rq_next = req + 1; list_add(&req->rq_list, &xprt->free);
req->rq_next = NULL;
xprt->free = xprt->slot; xprt_init_xid(xprt);
/* Check whether we want to use a reserved port */ /* Check whether we want to use a reserved port */
xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0; xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0;
dprintk("RPC: created transport %p\n", xprt); dprintk("RPC: created transport %p with %u slots\n", xprt,
xprt->max_reqs);
return xprt; return xprt;
} }
...@@ -1489,31 +1487,28 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) ...@@ -1489,31 +1487,28 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to)
/* /*
* Bind to a reserved port * Bind to a reserved port
*/ */
static inline int static inline int xprt_bindresvport(struct rpc_xprt *xprt, struct socket *sock)
xprt_bindresvport(struct socket *sock)
{ {
struct sockaddr_in myaddr; struct sockaddr_in myaddr = {
.sin_family = AF_INET,
};
int err, port; int err, port;
kernel_cap_t saved_cap = current->cap_effective;
/* Override capabilities. /* Were we already bound to a given port? Try to reuse it */
* They were checked in xprt_create_proto i.e. at mount time port = xprt->port;
*/
cap_raise(current->cap_effective, CAP_NET_BIND_SERVICE);
memset(&myaddr, 0, sizeof(myaddr));
myaddr.sin_family = AF_INET;
port = 800;
do { do {
myaddr.sin_port = htons(port); myaddr.sin_port = htons(port);
err = sock->ops->bind(sock, (struct sockaddr *) &myaddr, err = sock->ops->bind(sock, (struct sockaddr *) &myaddr,
sizeof(myaddr)); sizeof(myaddr));
} while (err == -EADDRINUSE && --port > 0); if (err == 0) {
current->cap_effective = saved_cap; xprt->port = port;
return 0;
if (err < 0) }
printk("RPC: Can't bind to reserved port (%d).\n", -err); if (--port == 0)
port = XPRT_MAX_RESVPORT;
} while (err == -EADDRINUSE && port != xprt->port);
printk("RPC: Can't bind to reserved port (%d).\n", -err);
return err; return err;
} }
...@@ -1563,11 +1558,11 @@ xprt_sock_setbufsize(struct rpc_xprt *xprt) ...@@ -1563,11 +1558,11 @@ xprt_sock_setbufsize(struct rpc_xprt *xprt)
return; return;
if (xprt->rcvsize) { if (xprt->rcvsize) {
sk->sk_userlocks |= SOCK_RCVBUF_LOCK; sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
sk->sk_rcvbuf = xprt->rcvsize * RPC_MAXCONG * 2; sk->sk_rcvbuf = xprt->rcvsize * xprt->max_reqs * 2;
} }
if (xprt->sndsize) { if (xprt->sndsize) {
sk->sk_userlocks |= SOCK_SNDBUF_LOCK; sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
sk->sk_sndbuf = xprt->sndsize * RPC_MAXCONG * 2; sk->sk_sndbuf = xprt->sndsize * xprt->max_reqs * 2;
sk->sk_write_space(sk); sk->sk_write_space(sk);
} }
} }
...@@ -1576,8 +1571,7 @@ xprt_sock_setbufsize(struct rpc_xprt *xprt) ...@@ -1576,8 +1571,7 @@ xprt_sock_setbufsize(struct rpc_xprt *xprt)
* Datastream sockets are created here, but xprt_connect will create * Datastream sockets are created here, but xprt_connect will create
* and connect stream sockets. * and connect stream sockets.
*/ */
static struct socket * static struct socket * xprt_create_socket(struct rpc_xprt *xprt, int proto, int resvport)
xprt_create_socket(int proto, struct rpc_timeout *to, int resvport)
{ {
struct socket *sock; struct socket *sock;
int type, err; int type, err;
...@@ -1593,7 +1587,7 @@ xprt_create_socket(int proto, struct rpc_timeout *to, int resvport) ...@@ -1593,7 +1587,7 @@ xprt_create_socket(int proto, struct rpc_timeout *to, int resvport)
} }
/* If the caller has the capability, bind to a reserved port */ /* If the caller has the capability, bind to a reserved port */
if (resvport && xprt_bindresvport(sock) < 0) { if (resvport && xprt_bindresvport(xprt, sock) < 0) {
printk("RPC: can't bind to reserved port.\n"); printk("RPC: can't bind to reserved port.\n");
goto failed; goto failed;
} }
...@@ -1614,16 +1608,11 @@ xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) ...@@ -1614,16 +1608,11 @@ xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
xprt = xprt_setup(proto, sap, to); xprt = xprt_setup(proto, sap, to);
if (!xprt) if (IS_ERR(xprt))
goto out_bad; dprintk("RPC: xprt_create_proto failed\n");
else
dprintk("RPC: xprt_create_proto created xprt %p\n", xprt); dprintk("RPC: xprt_create_proto created xprt %p\n", xprt);
return xprt; return xprt;
out_bad:
dprintk("RPC: xprt_create_proto failed\n");
if (xprt)
kfree(xprt);
return NULL;
} }
/* /*
...@@ -1662,6 +1651,7 @@ xprt_destroy(struct rpc_xprt *xprt) ...@@ -1662,6 +1651,7 @@ xprt_destroy(struct rpc_xprt *xprt)
dprintk("RPC: destroying transport %p\n", xprt); dprintk("RPC: destroying transport %p\n", xprt);
xprt_shutdown(xprt); xprt_shutdown(xprt);
xprt_close(xprt); xprt_close(xprt);
kfree(xprt->slot);
kfree(xprt); kfree(xprt);
return 0; return 0;
......
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