video: rk322x: fix crash on suspend/resume
[firefly-linux-kernel-4.4.55.git] / net / sunrpc / cache.c
index 4a2340a5440115dd4b758d4490793306b2bb4641..5e4f815c2b34d22fba11ac2443e9d66bc27c779c 100644 (file)
 static bool cache_defer_req(struct cache_req *req, struct cache_head *item);
 static void cache_revisit_request(struct cache_head *item);
 
-static void cache_init(struct cache_head *h)
+static void cache_init(struct cache_head *h, struct cache_detail *detail)
 {
        time_t now = seconds_since_boot();
        INIT_HLIST_NODE(&h->cache_list);
        h->flags = 0;
        kref_init(&h->ref);
        h->expiry_time = now + CACHE_NEW_EXPIRY;
+       if (now <= detail->flush_time)
+               /* ensure it isn't already expired */
+               now = detail->flush_time + 1;
        h->last_refresh = now;
 }
 
@@ -81,7 +84,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
         * we might get lose if we need to
         * cache_put it soon.
         */
-       cache_init(new);
+       cache_init(new, detail);
        detail->init(new, key);
 
        write_lock(&detail->hash_lock);
@@ -116,10 +119,15 @@ EXPORT_SYMBOL_GPL(sunrpc_cache_lookup);
 
 static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
 
-static void cache_fresh_locked(struct cache_head *head, time_t expiry)
+static void cache_fresh_locked(struct cache_head *head, time_t expiry,
+                              struct cache_detail *detail)
 {
+       time_t now = seconds_since_boot();
+       if (now <= detail->flush_time)
+               /* ensure it isn't immediately treated as expired */
+               now = detail->flush_time + 1;
        head->expiry_time = expiry;
-       head->last_refresh = seconds_since_boot();
+       head->last_refresh = now;
        smp_wmb(); /* paired with smp_rmb() in cache_is_valid() */
        set_bit(CACHE_VALID, &head->flags);
 }
@@ -149,7 +157,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
                                set_bit(CACHE_NEGATIVE, &old->flags);
                        else
                                detail->update(old, new);
-                       cache_fresh_locked(old, new->expiry_time);
+                       cache_fresh_locked(old, new->expiry_time, detail);
                        write_unlock(&detail->hash_lock);
                        cache_fresh_unlocked(old, detail);
                        return old;
@@ -162,7 +170,7 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
                cache_put(old, detail);
                return NULL;
        }
-       cache_init(tmp);
+       cache_init(tmp, detail);
        detail->init(tmp, old);
 
        write_lock(&detail->hash_lock);
@@ -173,8 +181,8 @@ struct cache_head *sunrpc_cache_update(struct cache_detail *detail,
        hlist_add_head(&tmp->cache_list, &detail->hash_table[hash]);
        detail->entries++;
        cache_get(tmp);
-       cache_fresh_locked(tmp, new->expiry_time);
-       cache_fresh_locked(old, 0);
+       cache_fresh_locked(tmp, new->expiry_time, detail);
+       cache_fresh_locked(old, 0, detail);
        write_unlock(&detail->hash_lock);
        cache_fresh_unlocked(tmp, detail);
        cache_fresh_unlocked(old, detail);
@@ -219,7 +227,8 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
        rv = cache_is_valid(h);
        if (rv == -EAGAIN) {
                set_bit(CACHE_NEGATIVE, &h->flags);
-               cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
+               cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY,
+                                  detail);
                rv = -ENOENT;
        }
        write_unlock(&detail->hash_lock);
@@ -487,10 +496,13 @@ EXPORT_SYMBOL_GPL(cache_flush);
 
 void cache_purge(struct cache_detail *detail)
 {
-       detail->flush_time = LONG_MAX;
+       time_t now = seconds_since_boot();
+       if (detail->flush_time >= now)
+               now = detail->flush_time + 1;
+       /* 'now' is the maximum value any 'last_refresh' can have */
+       detail->flush_time = now;
        detail->nextcheck = seconds_since_boot();
        cache_flush();
-       detail->flush_time = 1;
 }
 EXPORT_SYMBOL_GPL(cache_purge);
 
@@ -1436,6 +1448,7 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
 {
        char tbuf[20];
        char *bp, *ep;
+       time_t then, now;
 
        if (*ppos || count > sizeof(tbuf)-1)
                return -EINVAL;
@@ -1447,8 +1460,22 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
                return -EINVAL;
 
        bp = tbuf;
-       cd->flush_time = get_expiry(&bp);
-       cd->nextcheck = seconds_since_boot();
+       then = get_expiry(&bp);
+       now = seconds_since_boot();
+       cd->nextcheck = now;
+       /* Can only set flush_time to 1 second beyond "now", or
+        * possibly 1 second beyond flushtime.  This is because
+        * flush_time never goes backwards so it mustn't get too far
+        * ahead of time.
+        */
+       if (then >= now) {
+               /* Want to flush everything, so behave like cache_purge() */
+               if (cd->flush_time >= now)
+                       now = cd->flush_time + 1;
+               then = now;
+       }
+
+       cd->flush_time = then;
        cache_flush();
 
        *ppos += count;