// SPDX-License-Identifier: GPL-2.0 #include "bcachefs.h" #include "enumerated_ref.h" #include "util.h" #include #ifdef ENUMERATED_REF_DEBUG void enumerated_ref_get(struct enumerated_ref *ref, unsigned idx) { BUG_ON(idx >= ref->nr); atomic_long_inc(&ref->refs[idx]); } bool __enumerated_ref_tryget(struct enumerated_ref *ref, unsigned idx) { BUG_ON(idx >= ref->nr); return atomic_long_inc_not_zero(&ref->refs[idx]); } bool enumerated_ref_tryget(struct enumerated_ref *ref, unsigned idx) { BUG_ON(idx >= ref->nr); return !ref->dying && atomic_long_inc_not_zero(&ref->refs[idx]); } void enumerated_ref_put(struct enumerated_ref *ref, unsigned idx) { BUG_ON(idx >= ref->nr); long v = atomic_long_dec_return(&ref->refs[idx]); BUG_ON(v < 0); if (v) return; for (unsigned i = 0; i < ref->nr; i++) if (atomic_long_read(&ref->refs[i])) return; if (ref->stop_fn) ref->stop_fn(ref); complete(&ref->stop_complete); } #endif #ifndef ENUMERATED_REF_DEBUG static void enumerated_ref_kill_cb(struct percpu_ref *percpu_ref) { struct enumerated_ref *ref = container_of(percpu_ref, struct enumerated_ref, ref); if (ref->stop_fn) ref->stop_fn(ref); complete(&ref->stop_complete); } #endif void enumerated_ref_stop_async(struct enumerated_ref *ref) { reinit_completion(&ref->stop_complete); #ifndef ENUMERATED_REF_DEBUG percpu_ref_kill(&ref->ref); #else ref->dying = true; for (unsigned i = 0; i < ref->nr; i++) enumerated_ref_put(ref, i); #endif } void enumerated_ref_stop(struct enumerated_ref *ref, const char * const names[]) { enumerated_ref_stop_async(ref); while (!wait_for_completion_timeout(&ref->stop_complete, HZ * 10)) { struct printbuf buf = PRINTBUF; prt_str(&buf, "Waited for 10 seconds to shutdown enumerated ref\n"); prt_str(&buf, "Outstanding refs:\n"); enumerated_ref_to_text(&buf, ref, names); printk(KERN_ERR "%s", buf.buf); printbuf_exit(&buf); } } void enumerated_ref_start(struct enumerated_ref *ref) { #ifndef ENUMERATED_REF_DEBUG percpu_ref_reinit(&ref->ref); #else ref->dying = false; for (unsigned i = 0; i < ref->nr; i++) { BUG_ON(atomic_long_read(&ref->refs[i])); atomic_long_inc(&ref->refs[i]); } #endif } void enumerated_ref_exit(struct enumerated_ref *ref) { #ifndef ENUMERATED_REF_DEBUG percpu_ref_exit(&ref->ref); #else kfree(ref->refs); ref->refs = NULL; ref->nr = 0; #endif } int enumerated_ref_init(struct enumerated_ref *ref, unsigned nr, void (*stop_fn)(struct enumerated_ref *)) { init_completion(&ref->stop_complete); ref->stop_fn = stop_fn; #ifndef ENUMERATED_REF_DEBUG return percpu_ref_init(&ref->ref, enumerated_ref_kill_cb, PERCPU_REF_INIT_DEAD, GFP_KERNEL); #else ref->refs = kzalloc(sizeof(ref->refs[0]) * nr, GFP_KERNEL); if (!ref->refs) return -ENOMEM; ref->nr = nr; return 0; #endif } void enumerated_ref_to_text(struct printbuf *out, struct enumerated_ref *ref, const char * const names[]) { #ifdef ENUMERATED_REF_DEBUG bch2_printbuf_tabstop_push(out, 32); for (unsigned i = 0; i < ref->nr; i++) prt_printf(out, "%s\t%li\n", names[i], atomic_long_read(&ref->refs[i])); #else prt_str(out, "(not in debug mode)\n"); #endif }