Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[cascardo/linux.git] / net / sunrpc / cache.c
index b40f956..a72de07 100644 (file)
@@ -50,12 +50,6 @@ static void cache_init(struct cache_head *h)
        h->last_refresh = now;
 }
 
-static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
-{
-       return  (h->expiry_time < seconds_since_boot()) ||
-               (detail->flush_time > h->last_refresh);
-}
-
 struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
                                       struct cache_head *key, int hash)
 {
@@ -201,7 +195,7 @@ static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h)
        return sunrpc_cache_pipe_upcall(cd, h);
 }
 
-static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h)
+static inline int cache_is_valid(struct cache_head *h)
 {
        if (!test_bit(CACHE_VALID, &h->flags))
                return -EAGAIN;
@@ -227,16 +221,15 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
        int rv;
 
        write_lock(&detail->hash_lock);
-       rv = cache_is_valid(detail, h);
-       if (rv != -EAGAIN) {
-               write_unlock(&detail->hash_lock);
-               return rv;
+       rv = cache_is_valid(h);
+       if (rv == -EAGAIN) {
+               set_bit(CACHE_NEGATIVE, &h->flags);
+               cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
+               rv = -ENOENT;
        }
-       set_bit(CACHE_NEGATIVE, &h->flags);
-       cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
        write_unlock(&detail->hash_lock);
        cache_fresh_unlocked(h, detail);
-       return -ENOENT;
+       return rv;
 }
 
 /*
@@ -260,7 +253,7 @@ int cache_check(struct cache_detail *detail,
        long refresh_age, age;
 
        /* First decide return status as best we can */
-       rv = cache_is_valid(detail, h);
+       rv = cache_is_valid(h);
 
        /* now see if we want to start an upcall */
        refresh_age = (h->expiry_time - h->last_refresh);
@@ -269,19 +262,17 @@ int cache_check(struct cache_detail *detail,
        if (rqstp == NULL) {
                if (rv == -EAGAIN)
                        rv = -ENOENT;
-       } else if (rv == -EAGAIN || age > refresh_age/2) {
+       } else if (rv == -EAGAIN ||
+                  (h->expiry_time != 0 && age > refresh_age/2)) {
                dprintk("RPC:       Want update, refage=%ld, age=%ld\n",
                                refresh_age, age);
                if (!test_and_set_bit(CACHE_PENDING, &h->flags)) {
                        switch (cache_make_upcall(detail, h)) {
                        case -EINVAL:
-                               clear_bit(CACHE_PENDING, &h->flags);
-                               cache_revisit_request(h);
                                rv = try_to_negate_entry(detail, h);
                                break;
                        case -EAGAIN:
-                               clear_bit(CACHE_PENDING, &h->flags);
-                               cache_revisit_request(h);
+                               cache_fresh_unlocked(h, detail);
                                break;
                        }
                }
@@ -293,7 +284,7 @@ int cache_check(struct cache_detail *detail,
                         * Request was not deferred; handle it as best
                         * we can ourselves:
                         */
-                       rv = cache_is_valid(detail, h);
+                       rv = cache_is_valid(h);
                        if (rv == -EAGAIN)
                                rv = -ETIMEDOUT;
                }
@@ -310,7 +301,7 @@ EXPORT_SYMBOL_GPL(cache_check);
  * a current pointer into that list and into the table
  * for that entry.
  *
- * Each time clean_cache is called it finds the next non-empty entry
+ * Each time cache_clean is called it finds the next non-empty entry
  * in the current table and walks the list in that entry
  * looking for entries that can be removed.
  *
@@ -457,9 +448,8 @@ static int cache_clean(void)
                        current_index ++;
                spin_unlock(&cache_list_lock);
                if (ch) {
-                       if (test_and_clear_bit(CACHE_PENDING, &ch->flags))
-                               cache_dequeue(current_detail, ch);
-                       cache_revisit_request(ch);
+                       set_bit(CACHE_CLEANED, &ch->flags);
+                       cache_fresh_unlocked(ch, d);
                        cache_put(ch, d);
                }
        } else
@@ -1036,23 +1026,32 @@ static int cache_release(struct inode *inode, struct file *filp,
 
 static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
 {
-       struct cache_queue *cq;
+       struct cache_queue *cq, *tmp;
+       struct cache_request *cr;
+       struct list_head dequeued;
+
+       INIT_LIST_HEAD(&dequeued);
        spin_lock(&queue_lock);
-       list_for_each_entry(cq, &detail->queue, list)
+       list_for_each_entry_safe(cq, tmp, &detail->queue, list)
                if (!cq->reader) {
-                       struct cache_request *cr = container_of(cq, struct cache_request, q);
+                       cr = container_of(cq, struct cache_request, q);
                        if (cr->item != ch)
                                continue;
+                       if (test_bit(CACHE_PENDING, &ch->flags))
+                               /* Lost a race and it is pending again */
+                               break;
                        if (cr->readers != 0)
                                continue;
-                       list_del(&cr->q.list);
-                       spin_unlock(&queue_lock);
-                       cache_put(cr->item, detail);
-                       kfree(cr->buf);
-                       kfree(cr);
-                       return;
+                       list_move(&cr->q.list, &dequeued);
                }
        spin_unlock(&queue_lock);
+       while (!list_empty(&dequeued)) {
+               cr = list_entry(dequeued.next, struct cache_request, q.list);
+               list_del(&cr->q.list);
+               cache_put(cr->item, detail);
+               kfree(cr->buf);
+               kfree(cr);
+       }
 }
 
 /*
@@ -1166,6 +1165,7 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
 
        char *buf;
        struct cache_request *crq;
+       int ret = 0;
 
        if (!detail->cache_request)
                return -EINVAL;
@@ -1174,6 +1174,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
                warn_no_listener(detail);
                return -EINVAL;
        }
+       if (test_bit(CACHE_CLEANED, &h->flags))
+               /* Too late to make an upcall */
+               return -EAGAIN;
 
        buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!buf)
@@ -1191,10 +1194,18 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
        crq->len = 0;
        crq->readers = 0;
        spin_lock(&queue_lock);
-       list_add_tail(&crq->q.list, &detail->queue);
+       if (test_bit(CACHE_PENDING, &h->flags))
+               list_add_tail(&crq->q.list, &detail->queue);
+       else
+               /* Lost a race, no longer PENDING, so don't enqueue */
+               ret = -EAGAIN;
        spin_unlock(&queue_lock);
        wake_up(&queue_wait);
-       return 0;
+       if (ret == -EAGAIN) {
+               kfree(buf);
+               kfree(crq);
+       }
+       return ret;
 }
 EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);