mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 23:13:01 +02:00
bcachefs: enumerated_ref.c
Factor out the debug code for rw filesystem refs into a small library. In release mode an enumerated ref is a normal percpu refcount, but in debug mode all enumerated users of the ref get their own atomic_long_t ref - making it much easier to chase down refcount usage bugs for when a refcount has many users. For debugging, we have enumerated_ref_to_text(), which prints the current value of each different user. Additionally, in debug mode enumerated_ref_stop() has a 10 second timeout, after which it will dump outstanding refcounts. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
6d67de1079
commit
f5241e4127
|
@ -35,6 +35,7 @@ bcachefs-y := \
|
|||
disk_accounting.o \
|
||||
disk_groups.o \
|
||||
ec.o \
|
||||
enumerated_ref.o \
|
||||
errcode.o \
|
||||
error.o \
|
||||
extents.o \
|
||||
|
|
144
fs/bcachefs/enumerated_ref.c
Normal file
144
fs/bcachefs/enumerated_ref.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "bcachefs.h"
|
||||
#include "enumerated_ref.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <linux/completion.h>
|
||||
|
||||
#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
|
||||
}
|
66
fs/bcachefs/enumerated_ref.h
Normal file
66
fs/bcachefs/enumerated_ref.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BCACHEFS_ENUMERATED_REF_H
|
||||
#define _BCACHEFS_ENUMERATED_REF_H
|
||||
|
||||
#include "enumerated_ref_types.h"
|
||||
|
||||
/*
|
||||
* A refcount where the users are enumerated: in debug mode, we create sepate
|
||||
* refcounts for each user, to make leaks and refcount errors easy to track
|
||||
* down:
|
||||
*/
|
||||
|
||||
#ifdef ENUMERATED_REF_DEBUG
|
||||
void enumerated_ref_get(struct enumerated_ref *, unsigned);
|
||||
bool __enumerated_ref_tryget(struct enumerated_ref *, unsigned);
|
||||
bool enumerated_ref_tryget(struct enumerated_ref *, unsigned);
|
||||
void enumerated_ref_put(struct enumerated_ref *, unsigned);
|
||||
#else
|
||||
|
||||
static inline void enumerated_ref_get(struct enumerated_ref *ref, unsigned idx)
|
||||
{
|
||||
percpu_ref_get(&ref->ref);
|
||||
}
|
||||
|
||||
static inline bool __enumerated_ref_tryget(struct enumerated_ref *ref, unsigned idx)
|
||||
{
|
||||
return percpu_ref_tryget(&ref->ref);
|
||||
}
|
||||
|
||||
static inline bool enumerated_ref_tryget(struct enumerated_ref *ref, unsigned idx)
|
||||
{
|
||||
return percpu_ref_tryget_live(&ref->ref);
|
||||
}
|
||||
|
||||
static inline void enumerated_ref_put(struct enumerated_ref *ref, unsigned idx)
|
||||
{
|
||||
percpu_ref_put(&ref->ref);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool enumerated_ref_is_zero(struct enumerated_ref *ref)
|
||||
{
|
||||
#ifndef ENUMERATED_REF_DEBUG
|
||||
return percpu_ref_is_zero(&ref->ref);
|
||||
#else
|
||||
for (unsigned i = 0; i < ref->nr; i++)
|
||||
if (atomic_long_read(&ref->refs[i]))
|
||||
return false;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void enumerated_ref_stop_async(struct enumerated_ref *);
|
||||
void enumerated_ref_stop(struct enumerated_ref *, const char * const[]);
|
||||
void enumerated_ref_start(struct enumerated_ref *);
|
||||
|
||||
void enumerated_ref_exit(struct enumerated_ref *);
|
||||
int enumerated_ref_init(struct enumerated_ref *, unsigned,
|
||||
void (*stop_fn)(struct enumerated_ref *));
|
||||
|
||||
struct printbuf;
|
||||
void enumerated_ref_to_text(struct printbuf *,
|
||||
struct enumerated_ref *,
|
||||
const char * const[]);
|
||||
|
||||
#endif /* _BCACHEFS_ENUMERATED_REF_H */
|
19
fs/bcachefs/enumerated_ref_types.h
Normal file
19
fs/bcachefs/enumerated_ref_types.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BCACHEFS_ENUMERATED_REF_TYPES_H
|
||||
#define _BCACHEFS_ENUMERATED_REF_TYPES_H
|
||||
|
||||
#include <linux/percpu-refcount.h>
|
||||
|
||||
struct enumerated_ref {
|
||||
#ifdef ENUMERATED_REF_DEBUG
|
||||
unsigned nr;
|
||||
bool dying;
|
||||
atomic_long_t *refs;
|
||||
#else
|
||||
struct percpu_ref ref;
|
||||
#endif
|
||||
void (*stop_fn)(struct enumerated_ref *);
|
||||
struct completion stop_complete;
|
||||
};
|
||||
|
||||
#endif /* _BCACHEFS_ENUMERATED_REF_TYPES_H */
|
Loading…
Reference in New Issue
Block a user