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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Peter-

On Tue, Jul 8, 2008 at 12:08 PM, Peter Staubach <staubach@xxxxxxxxxx> 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 <staubach@xxxxxxxxxx>
>
>
> --- 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
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux