linux/drivers/md/bcache/closure.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Asynchronous refcounty things
   4 *
   5 * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com>
   6 * Copyright 2012 Google, Inc.
   7 */
   8
   9#include <linux/debugfs.h>
  10#include <linux/module.h>
  11#include <linux/seq_file.h>
  12#include <linux/sched/debug.h>
  13
  14#include "closure.h"
  15
  16static inline void closure_put_after_sub(struct closure *cl, int flags)
  17{
  18        int r = flags & CLOSURE_REMAINING_MASK;
  19
  20        BUG_ON(flags & CLOSURE_GUARD_MASK);
  21        BUG_ON(!r && (flags & ~CLOSURE_DESTRUCTOR));
  22
  23        if (!r) {
  24                if (cl->fn && !(flags & CLOSURE_DESTRUCTOR)) {
  25                        atomic_set(&cl->remaining,
  26                                   CLOSURE_REMAINING_INITIALIZER);
  27                        closure_queue(cl);
  28                } else {
  29                        struct closure *parent = cl->parent;
  30                        closure_fn *destructor = cl->fn;
  31
  32                        closure_debug_destroy(cl);
  33
  34                        if (destructor)
  35                                destructor(cl);
  36
  37                        if (parent)
  38                                closure_put(parent);
  39                }
  40        }
  41}
  42
  43/* For clearing flags with the same atomic op as a put */
  44void closure_sub(struct closure *cl, int v)
  45{
  46        closure_put_after_sub(cl, atomic_sub_return(v, &cl->remaining));
  47}
  48
  49/*
  50 * closure_put - decrement a closure's refcount
  51 */
  52void closure_put(struct closure *cl)
  53{
  54        closure_put_after_sub(cl, atomic_dec_return(&cl->remaining));
  55}
  56
  57/*
  58 * closure_wake_up - wake up all closures on a wait list, without memory barrier
  59 */
  60void __closure_wake_up(struct closure_waitlist *wait_list)
  61{
  62        struct llist_node *list;
  63        struct closure *cl, *t;
  64        struct llist_node *reverse = NULL;
  65
  66        list = llist_del_all(&wait_list->list);
  67
  68        /* We first reverse the list to preserve FIFO ordering and fairness */
  69        reverse = llist_reverse_order(list);
  70
  71        /* Then do the wakeups */
  72        llist_for_each_entry_safe(cl, t, reverse, list) {
  73                closure_set_waiting(cl, 0);
  74                closure_sub(cl, CLOSURE_WAITING + 1);
  75        }
  76}
  77
  78/**
  79 * closure_wait - add a closure to a waitlist
  80 * @waitlist: will own a ref on @cl, which will be released when
  81 * closure_wake_up() is called on @waitlist.
  82 * @cl: closure pointer.
  83 *
  84 */
  85bool closure_wait(struct closure_waitlist *waitlist, struct closure *cl)
  86{
  87        if (atomic_read(&cl->remaining) & CLOSURE_WAITING)
  88                return false;
  89
  90        closure_set_waiting(cl, _RET_IP_);
  91        atomic_add(CLOSURE_WAITING + 1, &cl->remaining);
  92        llist_add(&cl->list, &waitlist->list);
  93
  94        return true;
  95}
  96
  97struct closure_syncer {
  98        struct task_struct      *task;
  99        int                     done;
 100};
 101
 102static void closure_sync_fn(struct closure *cl)
 103{
 104        struct closure_syncer *s = cl->s;
 105        struct task_struct *p;
 106
 107        rcu_read_lock();
 108        p = READ_ONCE(s->task);
 109        s->done = 1;
 110        wake_up_process(p);
 111        rcu_read_unlock();
 112}
 113
 114void __sched __closure_sync(struct closure *cl)
 115{
 116        struct closure_syncer s = { .task = current };
 117
 118        cl->s = &s;
 119        continue_at(cl, closure_sync_fn, NULL);
 120
 121        while (1) {
 122                set_current_state(TASK_UNINTERRUPTIBLE);
 123                if (s.done)
 124                        break;
 125                schedule();
 126        }
 127
 128        __set_current_state(TASK_RUNNING);
 129}
 130
 131#ifdef CONFIG_BCACHE_CLOSURES_DEBUG
 132
 133static LIST_HEAD(closure_list);
 134static DEFINE_SPINLOCK(closure_list_lock);
 135
 136void closure_debug_create(struct closure *cl)
 137{
 138        unsigned long flags;
 139
 140        BUG_ON(cl->magic == CLOSURE_MAGIC_ALIVE);
 141        cl->magic = CLOSURE_MAGIC_ALIVE;
 142
 143        spin_lock_irqsave(&closure_list_lock, flags);
 144        list_add(&cl->all, &closure_list);
 145        spin_unlock_irqrestore(&closure_list_lock, flags);
 146}
 147
 148void closure_debug_destroy(struct closure *cl)
 149{
 150        unsigned long flags;
 151
 152        BUG_ON(cl->magic != CLOSURE_MAGIC_ALIVE);
 153        cl->magic = CLOSURE_MAGIC_DEAD;
 154
 155        spin_lock_irqsave(&closure_list_lock, flags);
 156        list_del(&cl->all);
 157        spin_unlock_irqrestore(&closure_list_lock, flags);
 158}
 159
 160static struct dentry *closure_debug;
 161
 162static int debug_show(struct seq_file *f, void *data)
 163{
 164        struct closure *cl;
 165
 166        spin_lock_irq(&closure_list_lock);
 167
 168        list_for_each_entry(cl, &closure_list, all) {
 169                int r = atomic_read(&cl->remaining);
 170
 171                seq_printf(f, "%p: %pS -> %pS p %p r %i ",
 172                           cl, (void *) cl->ip, cl->fn, cl->parent,
 173                           r & CLOSURE_REMAINING_MASK);
 174
 175                seq_printf(f, "%s%s\n",
 176                           test_bit(WORK_STRUCT_PENDING_BIT,
 177                                    work_data_bits(&cl->work)) ? "Q" : "",
 178                           r & CLOSURE_RUNNING  ? "R" : "");
 179
 180                if (r & CLOSURE_WAITING)
 181                        seq_printf(f, " W %pS\n",
 182                                   (void *) cl->waiting_on);
 183
 184                seq_printf(f, "\n");
 185        }
 186
 187        spin_unlock_irq(&closure_list_lock);
 188        return 0;
 189}
 190
 191DEFINE_SHOW_ATTRIBUTE(debug);
 192
 193void  __init closure_debug_init(void)
 194{
 195        if (!IS_ERR_OR_NULL(bcache_debug))
 196                /*
 197                 * it is unnecessary to check return value of
 198                 * debugfs_create_file(), we should not care
 199                 * about this.
 200                 */
 201                closure_debug = debugfs_create_file(
 202                        "closures", 0400, bcache_debug, NULL, &debug_fops);
 203}
 204#endif
 205
 206MODULE_AUTHOR("Kent Overstreet <koverstreet@google.com>");
 207MODULE_LICENSE("GPL");
 208