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