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
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 
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 
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 
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
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 
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 
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 
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 
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 
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 
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