1 // SPDX-License-Identifier: GPL-2.0
2
3 #include "bcachefs.h"
4 #include "enumerated_ref.h"
5 #include "util.h"
6
7 #include <linux/completion.h>
8
9 #ifdef ENUMERATED_REF_DEBUG
enumerated_ref_get(struct enumerated_ref * ref,unsigned idx)10 void enumerated_ref_get(struct enumerated_ref *ref, unsigned idx)
11 {
12 BUG_ON(idx >= ref->nr);
13 atomic_long_inc(&ref->refs[idx]);
14 }
15
__enumerated_ref_tryget(struct enumerated_ref * ref,unsigned idx)16 bool __enumerated_ref_tryget(struct enumerated_ref *ref, unsigned idx)
17 {
18 BUG_ON(idx >= ref->nr);
19 return atomic_long_inc_not_zero(&ref->refs[idx]);
20 }
21
enumerated_ref_tryget(struct enumerated_ref * ref,unsigned idx)22 bool enumerated_ref_tryget(struct enumerated_ref *ref, unsigned idx)
23 {
24 BUG_ON(idx >= ref->nr);
25 return !ref->dying &&
26 atomic_long_inc_not_zero(&ref->refs[idx]);
27 }
28
enumerated_ref_put(struct enumerated_ref * ref,unsigned idx)29 void enumerated_ref_put(struct enumerated_ref *ref, unsigned idx)
30 {
31 BUG_ON(idx >= ref->nr);
32 long v = atomic_long_dec_return(&ref->refs[idx]);
33
34 BUG_ON(v < 0);
35 if (v)
36 return;
37
38 for (unsigned i = 0; i < ref->nr; i++)
39 if (atomic_long_read(&ref->refs[i]))
40 return;
41
42 if (ref->stop_fn)
43 ref->stop_fn(ref);
44 complete(&ref->stop_complete);
45 }
46 #endif
47
48 #ifndef ENUMERATED_REF_DEBUG
enumerated_ref_kill_cb(struct percpu_ref * percpu_ref)49 static void enumerated_ref_kill_cb(struct percpu_ref *percpu_ref)
50 {
51 struct enumerated_ref *ref =
52 container_of(percpu_ref, struct enumerated_ref, ref);
53
54 if (ref->stop_fn)
55 ref->stop_fn(ref);
56 complete(&ref->stop_complete);
57 }
58 #endif
59
enumerated_ref_stop_async(struct enumerated_ref * ref)60 void enumerated_ref_stop_async(struct enumerated_ref *ref)
61 {
62 reinit_completion(&ref->stop_complete);
63
64 #ifndef ENUMERATED_REF_DEBUG
65 percpu_ref_kill(&ref->ref);
66 #else
67 ref->dying = true;
68 for (unsigned i = 0; i < ref->nr; i++)
69 enumerated_ref_put(ref, i);
70 #endif
71 }
72
enumerated_ref_stop(struct enumerated_ref * ref,const char * const names[])73 void enumerated_ref_stop(struct enumerated_ref *ref,
74 const char * const names[])
75 {
76 enumerated_ref_stop_async(ref);
77 while (!wait_for_completion_timeout(&ref->stop_complete, HZ * 10)) {
78 struct printbuf buf = PRINTBUF;
79
80 prt_str(&buf, "Waited for 10 seconds to shutdown enumerated ref\n");
81 prt_str(&buf, "Outstanding refs:\n");
82 enumerated_ref_to_text(&buf, ref, names);
83 printk(KERN_ERR "%s", buf.buf);
84 printbuf_exit(&buf);
85 }
86 }
87
enumerated_ref_start(struct enumerated_ref * ref)88 void enumerated_ref_start(struct enumerated_ref *ref)
89 {
90 #ifndef ENUMERATED_REF_DEBUG
91 percpu_ref_reinit(&ref->ref);
92 #else
93 ref->dying = false;
94 for (unsigned i = 0; i < ref->nr; i++) {
95 BUG_ON(atomic_long_read(&ref->refs[i]));
96 atomic_long_inc(&ref->refs[i]);
97 }
98 #endif
99 }
100
enumerated_ref_exit(struct enumerated_ref * ref)101 void enumerated_ref_exit(struct enumerated_ref *ref)
102 {
103 #ifndef ENUMERATED_REF_DEBUG
104 percpu_ref_exit(&ref->ref);
105 #else
106 kfree(ref->refs);
107 ref->refs = NULL;
108 ref->nr = 0;
109 #endif
110 }
111
enumerated_ref_init(struct enumerated_ref * ref,unsigned nr,void (* stop_fn)(struct enumerated_ref *))112 int enumerated_ref_init(struct enumerated_ref *ref, unsigned nr,
113 void (*stop_fn)(struct enumerated_ref *))
114 {
115 init_completion(&ref->stop_complete);
116 ref->stop_fn = stop_fn;
117
118 #ifndef ENUMERATED_REF_DEBUG
119 return percpu_ref_init(&ref->ref, enumerated_ref_kill_cb,
120 PERCPU_REF_INIT_DEAD, GFP_KERNEL);
121 #else
122 ref->refs = kzalloc(sizeof(ref->refs[0]) * nr, GFP_KERNEL);
123 if (!ref->refs)
124 return -ENOMEM;
125
126 ref->nr = nr;
127 return 0;
128 #endif
129 }
130
enumerated_ref_to_text(struct printbuf * out,struct enumerated_ref * ref,const char * const names[])131 void enumerated_ref_to_text(struct printbuf *out,
132 struct enumerated_ref *ref,
133 const char * const names[])
134 {
135 #ifdef ENUMERATED_REF_DEBUG
136 bch2_printbuf_tabstop_push(out, 32);
137
138 for (unsigned i = 0; i < ref->nr; i++)
139 prt_printf(out, "%s\t%li\n", names[i],
140 atomic_long_read(&ref->refs[i]));
141 #else
142 prt_str(out, "(not in debug mode)\n");
143 #endif
144 }
145