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