Merge tag 'xtensa-20210902' of git://github.com/jcmvbkbc/linux-xtensa
[linux-2.6-microblaze.git] / fs / fscache / cookie.c
index 751bc5b..cd42be6 100644 (file)
@@ -19,6 +19,8 @@ static atomic_t fscache_object_debug_id = ATOMIC_INIT(0);
 
 #define fscache_cookie_hash_shift 15
 static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_shift];
+static LIST_HEAD(fscache_cookies);
+static DEFINE_RWLOCK(fscache_cookies_lock);
 
 static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie,
                                            loff_t object_size);
@@ -29,21 +31,29 @@ static int fscache_attach_object(struct fscache_cookie *cookie,
 
 static void fscache_print_cookie(struct fscache_cookie *cookie, char prefix)
 {
-       struct hlist_node *object;
+       struct fscache_object *object;
+       struct hlist_node *o;
        const u8 *k;
        unsigned loop;
 
-       pr_err("%c-cookie c=%p [p=%p fl=%lx nc=%u na=%u]\n",
-              prefix, cookie, cookie->parent, cookie->flags,
+       pr_err("%c-cookie c=%08x [p=%08x fl=%lx nc=%u na=%u]\n",
+              prefix,
+              cookie->debug_id,
+              cookie->parent ? cookie->parent->debug_id : 0,
+              cookie->flags,
               atomic_read(&cookie->n_children),
               atomic_read(&cookie->n_active));
-       pr_err("%c-cookie d=%p n=%p\n",
-              prefix, cookie->def, cookie->netfs_data);
-
-       object = READ_ONCE(cookie->backing_objects.first);
-       if (object)
-               pr_err("%c-cookie o=%p\n",
-                      prefix, hlist_entry(object, struct fscache_object, cookie_link));
+       pr_err("%c-cookie d=%p{%s} n=%p\n",
+              prefix,
+              cookie->def,
+              cookie->def ? cookie->def->name : "?",
+              cookie->netfs_data);
+
+       o = READ_ONCE(cookie->backing_objects.first);
+       if (o) {
+               object = hlist_entry(o, struct fscache_object, cookie_link);
+               pr_err("%c-cookie o=%u\n", prefix, object->debug_id);
+       }
 
        pr_err("%c-key=[%u] '", prefix, cookie->key_len);
        k = (cookie->key_len <= sizeof(cookie->inline_key)) ?
@@ -57,6 +67,9 @@ void fscache_free_cookie(struct fscache_cookie *cookie)
 {
        if (cookie) {
                BUG_ON(!hlist_empty(&cookie->backing_objects));
+               write_lock(&fscache_cookies_lock);
+               list_del(&cookie->proc_link);
+               write_unlock(&fscache_cookies_lock);
                if (cookie->aux_len > sizeof(cookie->inline_aux))
                        kfree(cookie->aux);
                if (cookie->key_len > sizeof(cookie->inline_key))
@@ -74,10 +87,8 @@ void fscache_free_cookie(struct fscache_cookie *cookie)
 static int fscache_set_key(struct fscache_cookie *cookie,
                           const void *index_key, size_t index_key_len)
 {
-       unsigned long long h;
        u32 *buf;
        int bufs;
-       int i;
 
        bufs = DIV_ROUND_UP(index_key_len, sizeof(*buf));
 
@@ -91,17 +102,7 @@ static int fscache_set_key(struct fscache_cookie *cookie,
        }
 
        memcpy(buf, index_key, index_key_len);
-
-       /* Calculate a hash and combine this with the length in the first word
-        * or first half word
-        */
-       h = (unsigned long)cookie->parent;
-       h += index_key_len + cookie->type;
-
-       for (i = 0; i < bufs; i++)
-               h += buf[i];
-
-       cookie->key_hash = h ^ (h >> 32);
+       cookie->key_hash = fscache_hash(0, buf, bufs);
        return 0;
 }
 
@@ -129,6 +130,8 @@ static long fscache_compare_cookie(const struct fscache_cookie *a,
        return memcmp(ka, kb, a->key_len);
 }
 
+static atomic_t fscache_cookie_debug_id = ATOMIC_INIT(1);
+
 /*
  * Allocate a cookie.
  */
@@ -161,8 +164,9 @@ struct fscache_cookie *fscache_alloc_cookie(
                        goto nomem;
        }
 
-       atomic_set(&cookie->usage, 1);
+       refcount_set(&cookie->ref, 1);
        atomic_set(&cookie->n_children, 0);
+       cookie->debug_id = atomic_inc_return(&fscache_cookie_debug_id);
 
        /* We keep the active count elevated until relinquishment to prevent an
         * attempt to wake up every time the object operations queue quiesces.
@@ -181,6 +185,10 @@ struct fscache_cookie *fscache_alloc_cookie(
        /* radix tree insertion won't use the preallocation pool unless it's
         * told it may not wait */
        INIT_RADIX_TREE(&cookie->stores, GFP_NOFS & ~__GFP_DIRECT_RECLAIM);
+
+       write_lock(&fscache_cookies_lock);
+       list_add_tail(&cookie->proc_link, &fscache_cookies);
+       write_unlock(&fscache_cookies_lock);
        return cookie;
 
 nomem:
@@ -217,8 +225,8 @@ struct fscache_cookie *fscache_hash_cookie(struct fscache_cookie *candidate)
 
 collision:
        if (test_and_set_bit(FSCACHE_COOKIE_ACQUIRED, &cursor->flags)) {
-               trace_fscache_cookie(cursor, fscache_cookie_collision,
-                                    atomic_read(&cursor->usage));
+               trace_fscache_cookie(cursor->debug_id, refcount_read(&cursor->ref),
+                                    fscache_cookie_collision);
                pr_err("Duplicate cookie detected\n");
                fscache_print_cookie(cursor, 'O');
                fscache_print_cookie(candidate, 'N');
@@ -297,7 +305,8 @@ struct fscache_cookie *__fscache_acquire_cookie(
 
        cookie = fscache_hash_cookie(candidate);
        if (!cookie) {
-               trace_fscache_cookie(candidate, fscache_cookie_discard, 1);
+               trace_fscache_cookie(candidate->debug_id, 1,
+                                    fscache_cookie_discard);
                goto out;
        }
 
@@ -355,7 +364,7 @@ void __fscache_enable_cookie(struct fscache_cookie *cookie,
                             bool (*can_enable)(void *data),
                             void *data)
 {
-       _enter("%p", cookie);
+       _enter("%x", cookie->debug_id);
 
        trace_fscache_enable(cookie);
 
@@ -452,10 +461,8 @@ static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie,
 
        /* we may be required to wait for lookup to complete at this point */
        if (!fscache_defer_lookup) {
-               _debug("non-deferred lookup %p", &cookie->flags);
                wait_on_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP,
                            TASK_UNINTERRUPTIBLE);
-               _debug("complete");
                if (test_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags))
                        goto unavailable;
        }
@@ -480,7 +487,7 @@ static int fscache_alloc_object(struct fscache_cache *cache,
        struct fscache_object *object;
        int ret;
 
-       _enter("%p,%p{%s}", cache, cookie, cookie->def->name);
+       _enter("%s,%x{%s}", cache->tag->name, cookie->debug_id, cookie->def->name);
 
        spin_lock(&cookie->lock);
        hlist_for_each_entry(object, &cookie->backing_objects,
@@ -600,8 +607,6 @@ static int fscache_attach_object(struct fscache_cookie *cookie,
 
        /* Attach to the cookie.  The object already has a ref on it. */
        hlist_add_head(&object->cookie_link, &cookie->backing_objects);
-
-       fscache_objlist_add(object);
        ret = 0;
 
 cant_attach_object:
@@ -658,7 +663,7 @@ EXPORT_SYMBOL(__fscache_invalidate);
  */
 void __fscache_wait_on_invalidate(struct fscache_cookie *cookie)
 {
-       _enter("%p", cookie);
+       _enter("%x", cookie->debug_id);
 
        wait_on_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING,
                    TASK_UNINTERRUPTIBLE);
@@ -713,7 +718,7 @@ void __fscache_disable_cookie(struct fscache_cookie *cookie,
        struct fscache_object *object;
        bool awaken = false;
 
-       _enter("%p,%u", cookie, invalidate);
+       _enter("%x,%u", cookie->debug_id, invalidate);
 
        trace_fscache_disable(cookie);
 
@@ -803,8 +808,8 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie,
                return;
        }
 
-       _enter("%p{%s,%p,%d},%d",
-              cookie, cookie->def->name, cookie->netfs_data,
+       _enter("%x{%s,%d},%d",
+              cookie->debug_id, cookie->def->name,
               atomic_read(&cookie->n_active), retire);
 
        trace_fscache_relinquish(cookie, retire);
@@ -821,13 +826,12 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie,
        BUG_ON(!radix_tree_empty(&cookie->stores));
 
        if (cookie->parent) {
-               ASSERTCMP(atomic_read(&cookie->parent->usage), >, 0);
+               ASSERTCMP(refcount_read(&cookie->parent->ref), >, 0);
                ASSERTCMP(atomic_read(&cookie->parent->n_children), >, 0);
                atomic_dec(&cookie->parent->n_children);
        }
 
        /* Dispose of the netfs's link to the cookie */
-       ASSERTCMP(atomic_read(&cookie->usage), >, 0);
        fscache_cookie_put(cookie, fscache_cookie_put_relinquish);
 
        _leave("");
@@ -857,17 +861,17 @@ void fscache_cookie_put(struct fscache_cookie *cookie,
                        enum fscache_cookie_trace where)
 {
        struct fscache_cookie *parent;
-       int usage;
+       int ref;
 
-       _enter("%p", cookie);
+       _enter("%x", cookie->debug_id);
 
        do {
-               usage = atomic_dec_return(&cookie->usage);
-               trace_fscache_cookie(cookie, where, usage);
+               unsigned int cookie_debug_id = cookie->debug_id;
+               bool zero = __refcount_dec_and_test(&cookie->ref, &ref);
 
-               if (usage > 0)
+               trace_fscache_cookie(cookie_debug_id, ref - 1, where);
+               if (!zero)
                        return;
-               BUG_ON(usage < 0);
 
                parent = cookie->parent;
                fscache_unhash_cookie(cookie);
@@ -880,6 +884,19 @@ void fscache_cookie_put(struct fscache_cookie *cookie,
        _leave("");
 }
 
+/*
+ * Get a reference to a cookie.
+ */
+struct fscache_cookie *fscache_cookie_get(struct fscache_cookie *cookie,
+                                         enum fscache_cookie_trace where)
+{
+       int ref;
+
+       __refcount_inc(&cookie->ref, &ref);
+       trace_fscache_cookie(cookie->debug_id, ref + 1, where);
+       return cookie;
+}
+
 /*
  * check the consistency between the netfs inode and the backing cache
  *
@@ -958,3 +975,97 @@ inconsistent:
        return -ESTALE;
 }
 EXPORT_SYMBOL(__fscache_check_consistency);
+
+/*
+ * Generate a list of extant cookies in /proc/fs/fscache/cookies
+ */
+static int fscache_cookies_seq_show(struct seq_file *m, void *v)
+{
+       struct fscache_cookie *cookie;
+       unsigned int keylen = 0, auxlen = 0;
+       char _type[3], *type;
+       u8 *p;
+
+       if (v == &fscache_cookies) {
+               seq_puts(m,
+                        "COOKIE   PARENT   USAGE CHILD ACT TY FL  DEF              NETFS_DATA\n"
+                        "======== ======== ===== ===== === == === ================ ==========\n"
+                        );
+               return 0;
+       }
+
+       cookie = list_entry(v, struct fscache_cookie, proc_link);
+
+       switch (cookie->type) {
+       case 0:
+               type = "IX";
+               break;
+       case 1:
+               type = "DT";
+               break;
+       default:
+               snprintf(_type, sizeof(_type), "%02u",
+                        cookie->type);
+               type = _type;
+               break;
+       }
+
+       seq_printf(m,
+                  "%08x %08x %5u %5u %3u %s %03lx %-16s %px",
+                  cookie->debug_id,
+                  cookie->parent ? cookie->parent->debug_id : 0,
+                  refcount_read(&cookie->ref),
+                  atomic_read(&cookie->n_children),
+                  atomic_read(&cookie->n_active),
+                  type,
+                  cookie->flags,
+                  cookie->def->name,
+                  cookie->netfs_data);
+
+       keylen = cookie->key_len;
+       auxlen = cookie->aux_len;
+
+       if (keylen > 0 || auxlen > 0) {
+               seq_puts(m, " ");
+               p = keylen <= sizeof(cookie->inline_key) ?
+                       cookie->inline_key : cookie->key;
+               for (; keylen > 0; keylen--)
+                       seq_printf(m, "%02x", *p++);
+               if (auxlen > 0) {
+                       seq_puts(m, ", ");
+                       p = auxlen <= sizeof(cookie->inline_aux) ?
+                               cookie->inline_aux : cookie->aux;
+                       for (; auxlen > 0; auxlen--)
+                               seq_printf(m, "%02x", *p++);
+               }
+       }
+
+       seq_puts(m, "\n");
+       return 0;
+}
+
+static void *fscache_cookies_seq_start(struct seq_file *m, loff_t *_pos)
+       __acquires(fscache_cookies_lock)
+{
+       read_lock(&fscache_cookies_lock);
+       return seq_list_start_head(&fscache_cookies, *_pos);
+}
+
+static void *fscache_cookies_seq_next(struct seq_file *m, void *v, loff_t *_pos)
+{
+       return seq_list_next(v, &fscache_cookies, _pos);
+}
+
+static void fscache_cookies_seq_stop(struct seq_file *m, void *v)
+       __releases(rcu)
+{
+       read_unlock(&fscache_cookies_lock);
+}
+
+
+const struct seq_operations fscache_cookies_seq_ops = {
+       .start  = fscache_cookies_seq_start,
+       .next   = fscache_cookies_seq_next,
+       .stop   = fscache_cookies_seq_stop,
+       .show   = fscache_cookies_seq_show,
+};