2008-06-10 20:52:53

by Peter Staubach

[permalink] [raw]
Subject: [PATCH] make "noac" and "actimeo=0" work correctly

--- linux-2.6.25.i686/fs/nfs/inode.c.org
+++ linux-2.6.25.i686/fs/nfs/inode.c
@@ -703,7 +714,7 @@ int nfs_attribute_timeout(struct inode *

if (nfs_have_delegation(inode, FMODE_READ))
return 0;
- return !time_in_range(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
+ return !nfs_time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
}

/**
@@ -1088,7 +1113,7 @@ static int nfs_update_inode(struct inode
nfsi->attrtimeo_timestamp = now;
nfsi->last_updated = now;
} else {
- if (!time_in_range(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
+ if (!nfs_time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now;
--- linux-2.6.25.i686/fs/nfs/dir.c.org
+++ linux-2.6.25.i686/fs/nfs/dir.c
@@ -1807,7 +1830,7 @@ static int nfs_access_get_cached(struct
cache = nfs_access_search_rbtree(inode, cred);
if (cache == NULL)
goto out;
- if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
+ if (!nfs_time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
goto out_stale;
res->jiffies = cache->jiffies;
res->cred = cache->cred;
--- linux-2.6.25.i686/include/linux/nfs_fs.h.org
+++ linux-2.6.25.i686/include/linux/nfs_fs.h
@@ -121,7 +121,7 @@ struct nfs_inode {
*
* We need to revalidate the cached attrs for this inode if
*
- * jiffies - read_cache_jiffies > attrtimeo
+ * jiffies - read_cache_jiffies >= attrtimeo
*/
unsigned long read_cache_jiffies;
unsigned long attrtimeo;
@@ -244,6 +244,22 @@ static inline unsigned NFS_MAXATTRTIMEO(
return S_ISDIR(inode->i_mode) ? nfss->acdirmax : nfss->acregmax;
}

+static inline int nfs_time_in_range_open(unsigned long a,
+ unsigned long b, unsigned long c)
+{
+ /*
+ * If c is less then b, then the jiffies have wrapped.
+ * If so, then check to see if a is between b and the
+ * max jiffies value or between 0 and the value of c.
+ * This is the range between b and c.
+ *
+ * Otherwise, just check to see whether a is in [b, c).
+ */
+ if (c < b)
+ return time_after_eq(a, b) || time_before(a, c);
+ return time_after_eq(a, b) && time_before(a, c);
+}
+
static inline int NFS_STALE(const struct inode *inode)
{
return test_bit(NFS_INO_STALE, &NFS_I(inode)->flags);


Attachments:
attrtimeo.f-9 (2.46 kB)

2008-07-08 16:08:30

by Peter Staubach

[permalink] [raw]
Subject: Re: [PATCH V2] make "noac" and "actimeo=0" work correctly

--- linux-2.6.25.i686/fs/nfs/dir.c.org
+++ linux-2.6.25.i686/fs/nfs/dir.c
@@ -1808,7 +1808,7 @@ static int nfs_access_get_cached(struct
cache = nfs_access_search_rbtree(inode, cred);
if (cache == NULL)
goto out;
- if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
+ if (!nfs_time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
goto out_stale;
res->jiffies = cache->jiffies;
res->cred = cache->cred;
--- linux-2.6.25.i686/fs/nfs/inode.c.org
+++ linux-2.6.25.i686/fs/nfs/inode.c
@@ -706,14 +706,7 @@ int nfs_attribute_timeout(struct inode *

if (nfs_have_delegation(inode, FMODE_READ))
return 0;
- /*
- * Special case: if the attribute timeout is set to 0, then always
- * treat the cache as having expired (unless holding
- * a delegation).
- */
- if (nfsi->attrtimeo == 0)
- return 1;
- return !time_in_range(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
+ return !nfs_time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
}

/**
@@ -1098,7 +1091,7 @@ static int nfs_update_inode(struct inode
nfsi->attrtimeo_timestamp = now;
nfsi->last_updated = now;
} else {
- if (!time_in_range(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
+ if (!nfs_time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now;
--- linux-2.6.25.i686/include/linux/nfs_fs.h.org
+++ linux-2.6.25.i686/include/linux/nfs_fs.h
@@ -121,7 +121,7 @@ struct nfs_inode {
*
* We need to revalidate the cached attrs for this inode if
*
- * jiffies - read_cache_jiffies > attrtimeo
+ * jiffies - read_cache_jiffies >= attrtimeo
*/
unsigned long read_cache_jiffies;
unsigned long attrtimeo;
@@ -244,6 +244,22 @@ static inline unsigned NFS_MAXATTRTIMEO(
return S_ISDIR(inode->i_mode) ? nfss->acdirmax : nfss->acregmax;
}

+static inline int nfs_time_in_range_open(unsigned long a,
+ unsigned long b, unsigned long c)
+{
+ /*
+ * If c is less then b, then the jiffies have wrapped.
+ * If so, then check to see if a is between b and the
+ * max jiffies value or between 0 and the value of c.
+ * This is the range between b and c.
+ *
+ * Otherwise, just check to see whether a is in [b, c).
+ */
+ if (c < b)
+ return time_after_eq(a, b) || time_before(a, c);
+ return time_after_eq(a, b) && time_before(a, c);
+}
+
static inline int NFS_STALE(const struct inode *inode)
{
return test_bit(NFS_INO_STALE, &NFS_I(inode)->flags);


Attachments:
attrtimeo.devel (2.66 kB)

2008-07-10 15:59:03

by Chuck Lever

[permalink] [raw]
Subject: Re: [PATCH V2] make "noac" and "actimeo=0" work correctly

Hi Peter-

On Tue, Jul 8, 2008 at 12:08 PM, Peter Staubach <[email protected]> wrote:
> Hi.
>
> I've been looking at a bugzilla which describes a problem where
> a customer was advised to use either the "noac" or "actimeo=0"
> mount options to solve a consistency problem that they were
> seeing in the file attributes. It turned out that this solution
> did not work reliably for them because sometimes, the local
> attribute cache was believed to be valid and not timed out.
> (With an attribute cache timeout of 0, the cache should always
> appear to be timed out.)
>
> In looking at this situation, it appears to me that the problem
> is that the attribute cache timeout code has an off-by-one
> error in it. It is assuming that the cache is valid in the
> region, [read_cache_jiffies, read_cache_jiffies + attrtimeo]. The
> cache should be considered valid only in the region,
> [read_cache_jiffies, read_cache_jiffies + attrtimeo). With this
> change, the options, "noac" and "actimeo=0", work as originally
> expected.
>
> While I was there, I addressed a problem with the jiffies
> overflowing on 32 bit systems. When overflow occurs, the
> value of read_cache_jiffies + attrtimeo can be less then the
> value of read_cache_jiffies. This would cause an unnecessary
> GETATTR over the wire.
>
> Thoughts and/or comments? This is an updated patch which includes
> the previous support which was added to correct the noac/actimeo=0
> handling.

A couple of random thoughts below.

> Signed-off-by: Peter Staubach <[email protected]>
>
>
> --- linux-2.6.25.i686/fs/nfs/dir.c.org
> +++ linux-2.6.25.i686/fs/nfs/dir.c
> @@ -1808,7 +1808,7 @@ static int nfs_access_get_cached(struct
> cache = nfs_access_search_rbtree(inode, cred);
> if (cache == NULL)
> goto out;
> - if (!time_in_range(jiffies, cache->jiffies, cache->jiffies +
> nfsi->attrtimeo))
> + if (!nfs_time_in_range_open(jiffies, cache->jiffies, cache->jiffies
> + nfsi->attrtimeo))
> goto out_stale;
> res->jiffies = cache->jiffies;
> res->cred = cache->cred;
> --- linux-2.6.25.i686/fs/nfs/inode.c.org
> +++ linux-2.6.25.i686/fs/nfs/inode.c
> @@ -706,14 +706,7 @@ int nfs_attribute_timeout(struct inode *
>
> if (nfs_have_delegation(inode, FMODE_READ))
> return 0;
> - /*
> - * Special case: if the attribute timeout is set to 0, then always
> - * treat the cache as having expired (unless holding
> - * a delegation).
> - */
> - if (nfsi->attrtimeo == 0)
> - return 1;
> - return !time_in_range(jiffies, nfsi->read_cache_jiffies,
> nfsi->read_cache_jiffies + nfsi->attrtimeo);
> + return !nfs_time_in_range_open(jiffies, nfsi->read_cache_jiffies,
> nfsi->read_cache_jiffies + nfsi->attrtimeo);
> }
>
> /**
> @@ -1098,7 +1091,7 @@ static int nfs_update_inode(struct inode
> nfsi->attrtimeo_timestamp = now;
> nfsi->last_updated = now;
> } else {
> - if (!time_in_range(now, nfsi->attrtimeo_timestamp,
> nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
> + if (!nfs_time_in_range_open(now, nfsi->attrtimeo_timestamp,
> nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
> if ((nfsi->attrtimeo <<= 1) >
> NFS_MAXATTRTIMEO(inode))
> nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
> nfsi->attrtimeo_timestamp = now;
> --- linux-2.6.25.i686/include/linux/nfs_fs.h.org
> +++ linux-2.6.25.i686/include/linux/nfs_fs.h
> @@ -121,7 +121,7 @@ struct nfs_inode {
> *
> * We need to revalidate the cached attrs for this inode if
> *
> - * jiffies - read_cache_jiffies > attrtimeo
> + * jiffies - read_cache_jiffies >= attrtimeo
> */
> unsigned long read_cache_jiffies;
> unsigned long attrtimeo;
> @@ -244,6 +244,22 @@ static inline unsigned NFS_MAXATTRTIMEO(
> return S_ISDIR(inode->i_mode) ? nfss->acdirmax : nfss->acregmax;
> }
>
> +static inline int nfs_time_in_range_open(unsigned long a,
> + unsigned long b, unsigned long c)

All of nfs_time_in_range_open's callers use a sum of 'b' and
'nfsi->attrtimeo' for 'c'. Would it be cleaner to pass in
nfsi->attrtimeo' rather than 'b + nfsi->attrtimeo' and do the sum
here? It might make sense to explicitly check nfsi->attrtimeo for
zero here to document the special behavior of "actimeo=0".

Alternately, checking explicitly if b and c are equal might accomplish
the same without changing the synopsis.

Also, all of nfs_time_in_range_open's callers negate the return value.
Would the code in the callers be cleaner if that negation was moved
into nfs_time_in_range_open? You might rename
nfs_time_in_range_open() as nfs_cache_has_expired(), for example, to
make the 'if' statements in the callers make sense in English.

My feeling is that if you have to sit and stare at this for 5 minutes
to understand precisely how it works, it has already become too
obfuscated. In addition to fixing any bugs, I wonder if we can make
it easier to understand and maintain as well.

> +{
> + /*
> + * If c is less then b, then the jiffies have wrapped.
> + * If so, then check to see if a is between b and the
> + * max jiffies value or between 0 and the value of c.
> + * This is the range between b and c.

include/linux/jiffies.h claims it handles jiffy wrapping correctly.
Why isn't time_in_range() sufficient if 'c' has wrapped? If it isn't,
should you fix time_in_range() too?

You could then simplify this to "return b != c && time_in_range(a, b,
c);" or something like that. Or if you negate the return value here:

static inline nfs_attributes_have_expired(unsigned long current,
unsigned long
start, unsigned long end)
{
return (start == end) || !time_in_range(current, start, end);
}

My 0.02USD.

> + *
> + * Otherwise, just check to see whether a is in [b, c).
> + */
> + if (c < b)
> + return time_after_eq(a, b) || time_before(a, c);
> + return time_after_eq(a, b) && time_before(a, c);
> +}
> +
> static inline int NFS_STALE(const struct inode *inode)
> {
> return test_bit(NFS_INO_STALE, &NFS_I(inode)->flags);

--
Chuck Lever