Commit 448cfb18 authored by Trond Myklebust's avatar Trond Myklebust

[PATCH] Support for micro/nanosecond [acm]time in the NFS client

Given Andi Kleen's patch that introduces of VFS-level support for
nanosecond time resolutions, we should finally be able to export the
existing NFS client level support to userland.

In order to do so, the following patch will convert all NFS private 'raw
u64' values into the kernel-supported struct timespec directly in the
xdr_encode/xdr_decode routines.

It adds support for the nanosecond field in NFS 'setattr' calls, and in
nfs_refresh_inode().

Finally, there are a few cleanups in the nfs_refresh_inode code that
convert multiple use of the NFS_*(inode) macros into a single
dereference of NFS_I(inode).
parent 33ec2bbf
......@@ -656,9 +656,7 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
goto out_no_inode;
if (inode->i_state & I_NEW) {
__u64 new_size, new_mtime;
loff_t new_isize;
time_t new_atime;
struct nfs_inode *nfsi = NFS_I(inode);
/* We set i_ino for the few things that still rely on it,
* such as stat(2) */
......@@ -686,24 +684,17 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
else
init_special_inode(inode, inode->i_mode, fattr->rdev);
new_mtime = fattr->mtime;
new_size = fattr->size;
new_isize = nfs_size_to_loff_t(fattr->size);
new_atime = nfs_time_to_secs(fattr->atime);
NFS_READTIME(inode) = fattr->timestamp;
NFS_CACHE_CTIME(inode) = fattr->ctime;
inode->i_ctime.tv_sec = nfs_time_to_secs(fattr->ctime);
inode->i_ctime.tv_nsec = nfs_time_to_nsecs(fattr->ctime);
inode->i_atime.tv_sec = new_atime;
NFS_CACHE_MTIME(inode) = new_mtime;
inode->i_mtime.tv_sec = nfs_time_to_secs(new_mtime);
inode->i_mtime.tv_nsec = nfs_time_to_nsecs(new_mtime);
NFS_MTIME_UPDATE(inode) = fattr->timestamp;
NFS_CACHE_ISIZE(inode) = new_size;
nfsi->read_cache_jiffies = fattr->timestamp;
inode->i_atime = fattr->atime;
inode->i_mtime = fattr->mtime;
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)
NFS_CHANGE_ATTR(inode) = fattr->change_attr;
inode->i_size = new_isize;
nfsi->change_attr = fattr->change_attr;
inode->i_size = nfs_size_to_loff_t(fattr->size);
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid;
......@@ -718,10 +709,10 @@ __nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_blocks = fattr->du.nfs2.blocks;
inode->i_blksize = fattr->du.nfs2.blocksize;
}
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
NFS_I(inode)->cache_access.cred = NULL;
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies;
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
nfsi->cache_access.cred = NULL;
unlock_new_inode(inode);
} else
......@@ -787,9 +778,10 @@ printk("nfs_setattr: revalidate failed, error=%d\n", error);
* now to avoid invalidating the page cache.
*/
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);
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 */
......@@ -962,14 +954,18 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
static inline
int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
{
s64 cdif;
struct nfs_inode *nfsi = NFS_I(inode);
long cdif;
if (time_after(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
if (time_after(jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo))
goto out_valid;
if ((cdif = (s64)fattr->ctime - (s64)NFS_CACHE_CTIME(inode)) > 0)
cdif = fattr->ctime.tv_sec - nfsi->read_cache_ctime.tv_sec;
if (cdif == 0)
cdif = fattr->ctime.tv_nsec - nfsi->read_cache_ctime.tv_nsec;
if (cdif > 0)
goto out_valid;
/* Ugh... */
if (cdif == 0 && fattr->size > NFS_CACHE_ISIZE(inode))
if (cdif == 0 && fattr->size > nfsi->read_cache_isize)
goto out_valid;
return -1;
out_valid:
......@@ -991,19 +987,20 @@ int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
int
__nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
__u64 new_size, new_mtime;
struct nfs_inode *nfsi = NFS_I(inode);
__u64 new_size;
loff_t new_isize;
struct timespec new_atime;
int invalid = 0;
int mtime_update = 0;
dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n",
inode->i_sb->s_id, inode->i_ino,
atomic_read(&inode->i_count), fattr->valid);
if (NFS_FILEID(inode) != fattr->fileid) {
if (nfsi->fileid != fattr->fileid) {
printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
"expected (%s/0x%Lx), got (%s/0x%Lx)\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode),
inode->i_sb->s_id, (long long)nfsi->fileid,
inode->i_sb->s_id, (long long)fattr->fileid);
goto out_err;
}
......@@ -1017,12 +1014,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
goto out_changed;
new_mtime = fattr->mtime;
new_size = fattr->size;
new_isize = nfs_size_to_loff_t(fattr->size);
new_atime.tv_sec = nfs_time_to_secs(fattr->atime);
new_atime.tv_nsec = nfs_time_to_nsecs(fattr->atime);
/* Avoid races */
if (nfs_fattr_obsolete(inode, fattr))
goto out_nochange;
......@@ -1030,13 +1024,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
/*
* Update the read time so we don't revalidate too often.
*/
NFS_READTIME(inode) = fattr->timestamp;
nfsi->read_cache_jiffies = fattr->timestamp;
/*
* Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
* NOT inode->i_size!!!
*/
if (NFS_CACHE_ISIZE(inode) != new_size) {
if (nfsi->read_cache_isize != new_size) {
#ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
#endif
......@@ -1048,15 +1042,16 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
* can change this value in VFS without requiring a
* cache revalidation.
*/
if (NFS_CACHE_MTIME(inode) != new_mtime) {
if (!timespec_equal(&nfsi->read_cache_mtime, &fattr->mtime)) {
#ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
#endif
invalid = 1;
mtime_update = 1;
}
if ((fattr->valid & NFS_ATTR_FATTR_V4)
&& NFS_CHANGE_ATTR(inode) != fattr->change_attr) {
&& nfsi->change_attr != fattr->change_attr) {
#ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n",
inode->i_sb->s_id, inode->i_ino);
......@@ -1070,12 +1065,12 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
* operation, so there's no need to invalidate the caches.
*/
if ((fattr->valid & NFS_ATTR_PRE_CHANGE)
&& NFS_CHANGE_ATTR(inode) == fattr->pre_change_attr) {
&& nfsi->change_attr == fattr->pre_change_attr) {
invalid = 0;
}
else if ((fattr->valid & NFS_ATTR_WCC)
&& NFS_CACHE_ISIZE(inode) == fattr->pre_size
&& NFS_CACHE_MTIME(inode) == fattr->pre_mtime) {
&& nfsi->read_cache_isize == fattr->pre_size
&& timespec_equal(&nfsi->read_cache_mtime, &fattr->pre_mtime)) {
invalid = 0;
}
......@@ -1086,21 +1081,18 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
if (nfs_have_writebacks(inode) && new_isize < inode->i_size)
new_isize = inode->i_size;
NFS_CACHE_CTIME(inode) = fattr->ctime;
inode->i_ctime.tv_sec = nfs_time_to_secs(fattr->ctime);
inode->i_ctime.tv_nsec = nfs_time_to_nsecs(fattr->ctime);
inode->i_atime = new_atime;
nfsi->read_cache_ctime = fattr->ctime;
inode->i_ctime = fattr->ctime;
inode->i_atime = fattr->atime;
if (NFS_CACHE_MTIME(inode) != new_mtime) {
if (mtime_update) {
if (invalid)
NFS_MTIME_UPDATE(inode) = fattr->timestamp;
NFS_CACHE_MTIME(inode) = new_mtime;
inode->i_mtime.tv_sec = nfs_time_to_secs(new_mtime);
inode->i_mtime.tv_nsec = nfs_time_to_nsecs(new_mtime);
nfsi->cache_mtime_jiffies = fattr->timestamp;
nfsi->read_cache_mtime = fattr->mtime;
inode->i_mtime = fattr->mtime;
}
NFS_CACHE_ISIZE(inode) = new_size;
nfsi->read_cache_isize = new_size;
inode->i_size = new_isize;
if (inode->i_mode != fattr->mode ||
......@@ -1114,7 +1106,7 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
}
if (fattr->valid & NFS_ATTR_FATTR_V4)
NFS_CHANGE_ATTR(inode) = fattr->change_attr;
nfsi->change_attr = fattr->change_attr;
inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink;
......@@ -1134,20 +1126,20 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
/* Update attrtimeo value */
if (invalid) {
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies;
invalidate_inode_pages(inode->i_mapping);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
} else if (time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
} else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies;
}
return 0;
out_nochange:
if (!timespec_equal(&new_atime, &inode->i_atime))
inode->i_atime = new_atime;
if (!timespec_equal(&fattr->atime, &inode->i_atime))
inode->i_atime = fattr->atime;
return 0;
out_changed:
/*
......
......@@ -86,10 +86,20 @@ xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
}
static inline u32*
xdr_decode_time(u32 *p, u64 *timep)
xdr_encode_time(u32 *p, struct timespec *timep)
{
u64 tmp = (u64)ntohl(*p++) << 32;
*timep = tmp + (u64)ntohl(*p++);
*p++ = htonl(timep->tv_sec);
/* Convert nanoseconds into microseconds */
*p++ = htonl(timep->tv_nsec / 1000);
return p;
}
static inline u32*
xdr_decode_time(u32 *p, struct timespec *timep)
{
timep->tv_sec = ntohl(*p++);
/* Convert microseconds into nanoseconds */
timep->tv_nsec = ntohl(*p++) * 1000;
return p;
}
......@@ -131,16 +141,14 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
SATTR(p, attr, ATTR_SIZE, ia_size);
if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
*p++ = htonl(attr->ia_atime.tv_sec);
*p++ = 0;
p = xdr_encode_time(p, &attr->ia_atime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
}
if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
*p++ = htonl(attr->ia_mtime.tv_sec);
*p++ = 0;
p = xdr_encode_time(p, &attr->ia_mtime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
......
......@@ -128,26 +128,18 @@ xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
* nanosecond field.
*/
static inline u32 *
xdr_encode_time(u32 *p, time_t time)
xdr_encode_time3(u32 *p, struct timespec *timep)
{
*p++ = htonl(time);
*p++ = 0;
*p++ = htonl(timep->tv_sec);
*p++ = htonl(timep->tv_nsec);
return p;
}
static inline u32 *
xdr_decode_time3(u32 *p, u64 *timep)
xdr_decode_time3(u32 *p, struct timespec *timep)
{
u64 tmp = (u64)ntohl(*p++) << 32;
*timep = tmp + (u64)ntohl(*p++);
return p;
}
static inline u32 *
xdr_encode_time3(u32 *p, u64 time)
{
*p++ = htonl(time >> 32);
*p++ = htonl(time & 0xFFFFFFFF);
timep->tv_sec = ntohl(*p++);
timep->tv_nsec = ntohl(*p++);
return p;
}
......@@ -212,7 +204,7 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
}
if (attr->ia_valid & ATTR_ATIME_SET) {
*p++ = xdr_two;
p = xdr_encode_time(p, attr->ia_atime.tv_sec);
p = xdr_encode_time3(p, &attr->ia_atime);
} else if (attr->ia_valid & ATTR_ATIME) {
*p++ = xdr_one;
} else {
......@@ -220,7 +212,7 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
}
if (attr->ia_valid & ATTR_MTIME_SET) {
*p++ = xdr_two;
p = xdr_encode_time(p, attr->ia_mtime.tv_sec);
p = xdr_encode_time3(p, &attr->ia_mtime);
} else if (attr->ia_valid & ATTR_MTIME) {
*p++ = xdr_one;
} else {
......@@ -288,7 +280,7 @@ nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
p = xdr_encode_sattr(p, args->sattr);
*p++ = htonl(args->guard);
if (args->guard)
p = xdr_encode_time3(p, args->guardtime);
p = xdr_encode_time3(p, &args->guardtime);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
......
......@@ -563,13 +563,13 @@ nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short por
{
struct nfs4_setclientid *setclientid = GET_OP(cp, setclientid);
struct nfs_server *server = cp->server;
struct timeval tv;
struct timespec tv;
u32 *p;
do_gettimeofday(&tv);
p = (u32 *)setclientid->sc_verifier;
tv = CURRENT_TIME;
p = (u32 *)setclientid->sc_verifier;
*p++ = tv.tv_sec;
*p++ = tv.tv_usec;
*p++ = tv.tv_nsec;
setclientid->sc_name = server->ip_addr;
sprintf(setclientid->sc_netid, "udp");
sprintf(setclientid->sc_uaddr, "%s.%d.%d", server->ip_addr, port >> 8, port & 255);
......
......@@ -873,8 +873,8 @@ xdr_error: \
} while (0)
#define READTIME(x) do { \
p++; \
(x) = (u64)ntohl(*p++) << 32; \
(x) |= ntohl(*p++); \
(x.tv_sec) = ntohl(*p++); \
(x.tv_nsec) = ntohl(*p++); \
} while (0)
#define COPYMEM(x,nbytes) do { \
memcpy((x), p, nbytes); \
......@@ -1228,19 +1228,19 @@ decode_getattr(struct nfs4_compound *cp, int nfserr, struct nfs4_getattr *getatt
READ_BUF(12);
len += 12;
READTIME(nfp->atime);
dprintk("read_attrs: atime=%d\n", (int)nfp->atime);
dprintk("read_attrs: atime=%ld\n", (long)nfp->atime.tv_sec);
}
if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
READ_BUF(12);
len += 12;
READTIME(nfp->ctime);
dprintk("read_attrs: ctime=%d\n", (int)nfp->ctime);
dprintk("read_attrs: ctime=%ld\n", (long)nfp->ctime.tv_sec);
}
if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
READ_BUF(12);
len += 12;
READTIME(nfp->mtime);
dprintk("read_attrs: mtime=%d\n", (int)nfp->mtime);
dprintk("read_attrs: mtime=%ld\n", (long)nfp->mtime.tv_sec);
}
if (len != attrlen)
goto xdr_error;
......
......@@ -118,8 +118,8 @@ struct nfs_inode {
* mtime != read_cache_mtime
*/
unsigned long read_cache_jiffies;
__u64 read_cache_ctime;
__u64 read_cache_mtime;
struct timespec read_cache_ctime;
struct timespec read_cache_mtime;
__u64 read_cache_isize;
unsigned long attrtimeo;
unsigned long attrtimeo_timestamp;
......@@ -416,20 +416,6 @@ nfs_fileid_to_ino_t(u64 fileid)
return ino;
}
static inline time_t
nfs_time_to_secs(__u64 time)
{
return (time_t)(time >> 32);
}
static inline u32
nfs_time_to_nsecs(__u64 time)
{
return time & 0xffffffff;
}
/* NFS root */
extern void * nfs_root_data(void);
......
......@@ -6,8 +6,8 @@
struct nfs_fattr {
unsigned short valid; /* which fields are valid */
__u64 pre_size; /* pre_op_attr.size */
__u64 pre_mtime; /* pre_op_attr.mtime */
__u64 pre_ctime; /* pre_op_attr.ctime */
struct timespec pre_mtime; /* pre_op_attr.mtime */
struct timespec pre_ctime; /* pre_op_attr.ctime */
enum nfs_ftype type; /* always use NFSv2 types */
__u32 mode;
__u32 nlink;
......@@ -32,9 +32,9 @@ struct nfs_fattr {
} nfs4;
} fsid_u;
__u64 fileid;
__u64 atime;
__u64 mtime;
__u64 ctime;
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
__u64 change_attr; /* NFSv4 change attribute */
__u64 pre_change_attr;/* pre-op NFSv4 change attribute */
unsigned long timestamp;
......@@ -219,7 +219,7 @@ struct nfs3_sattrargs {
struct nfs_fh * fh;
struct iattr * sattr;
unsigned int guard;
__u64 guardtime;
struct timespec guardtime;
};
struct nfs3_diropargs {
......
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