[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <bfb749bd1c9580225270657130b1516168e000f3.camel@kernel.org>
Date: Tue, 25 Mar 2025 07:47:49 -0400
From: Jeff Layton <jlayton@...nel.org>
To: Eric Dumazet <edumazet@...gle.com>
Cc: "David S. Miller" <davem@...emloft.net>, Jakub Kicinski
<kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>, Simon Horman
<horms@...nel.org>, netdev@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] net: add a debugfs files for showing netns refcount
tracking info
On Tue, 2025-03-25 at 06:21 +0100, Eric Dumazet wrote:
> On Mon, Mar 24, 2025 at 9:24 PM Jeff Layton <jlayton@...nel.org> wrote:
> >
> > CONFIG_NET_NS_REFCNT_TRACKER currently has no convenient way to display
> > its tracking info. Add a new net_ns directory in debugfs. Have a
> > directory in there for every net, with refcnt and notrefcnt files that
> > show the currently tracked active and passive references.
> >
> > Signed-off-by: Jeff Layton <jlayton@...nel.org>
> > ---
> > Recently, I had a need to track down some long-held netns references,
> > and discovered CONFIG_NET_NS_REFCNT_TRACKER. The main thing that seemed
> > to be missing from it though is a simple way to view the currently held
> > references on the netns. This adds files in debugfs for this.
>
> Thanks for working on this, this is a very good idea.
>
Thanks. I needed it when I was tracking down refs held by NFS and RPC
clients. My thinking was that we could add the netns specific dirs to
debugfs with the refcnt and notrefcnt files in it for now, and could
add new files to it later as needed for other netns-related things.
>
> > +#define MAX_NS_DEBUG_BUFSIZE (32 * PAGE_SIZE)
> > +
> > +static int
> > +ns_debug_tracker_show(struct seq_file *f, void *v)
> > +{
> > + struct ref_tracker_dir *tracker = f->private;
> > + int len, bufsize = PAGE_SIZE;
> > + char *buf;
> > +
> > + for (;;) {
> > + buf = kvmalloc(bufsize, GFP_KERNEL);
> > + if (!buf)
> > + return -ENOMEM;
> > +
> > + len = ref_tracker_dir_snprint(tracker, buf, bufsize);
> > + if (len < bufsize)
> > + break;
> > +
> > + kvfree(buf);
> > + bufsize *= 2;
> > + if (bufsize > MAX_NS_DEBUG_BUFSIZE)
> > + return -ENOBUFS;
> > + }
> > + seq_write(f, buf, len);
> > + kvfree(buf);
> > + return 0;
> > +}
>
> I guess we could first change ref_tracker_dir_snprint(tracker, buf, bufsize)
> to ref_tracker_dir_snprint(tracker, buf, bufsize, &needed) to avoid
> too many tries in this loop.
>
> Most of ref_tracker_dir_snprint() runs with hard irq being disabled...
>
Ouch, yeah that sounds like a good idea.
>
> diff --git a/drivers/gpu/drm/i915/intel_wakeref.c
> b/drivers/gpu/drm/i915/intel_wakeref.c
> index 87f2460473127af65a9a793c7f1934fafe41e79e..6650421b4f00c318adec72cd7c17a76832f14cce
> 100644
> --- a/drivers/gpu/drm/i915/intel_wakeref.c
> +++ b/drivers/gpu/drm/i915/intel_wakeref.c
> @@ -208,7 +208,7 @@ void intel_ref_tracker_show(struct ref_tracker_dir *dir,
> if (!buf)
> return;
>
> - count = ref_tracker_dir_snprint(dir, buf, buf_size);
> + count = ref_tracker_dir_snprint(dir, buf, buf_size, NULL);
> if (!count)
> goto free;
> /* printk does not like big buffers, so we split it */
> diff --git a/include/linux/ref_tracker.h b/include/linux/ref_tracker.h
> index 8eac4f3d52547ccbaf9dcd09962ce80d26fbdff8..19bd42088434b661810082350a9d5afcbff6a88a
> 100644
> --- a/include/linux/ref_tracker.h
> +++ b/include/linux/ref_tracker.h
> @@ -46,7 +46,7 @@ void ref_tracker_dir_print_locked(struct ref_tracker_dir *dir,
> void ref_tracker_dir_print(struct ref_tracker_dir *dir,
> unsigned int display_limit);
>
> -int ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf,
> size_t size);
> +int ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf,
> size_t size, size_t *needed);
>
> int ref_tracker_alloc(struct ref_tracker_dir *dir,
> struct ref_tracker **trackerp, gfp_t gfp);
> @@ -77,7 +77,7 @@ static inline void ref_tracker_dir_print(struct
> ref_tracker_dir *dir,
> }
>
> static inline int ref_tracker_dir_snprint(struct ref_tracker_dir *dir,
> - char *buf, size_t size)
> + char *buf, size_t size,
> size_t *needed)
> {
> return 0;
> }
> diff --git a/lib/ref_tracker.c b/lib/ref_tracker.c
> index cf5609b1ca79361763abe5a3a98484a3ee591ff2..d8d02dab7ce67caf91ae22f9391abe2c92481c7f
> 100644
> --- a/lib/ref_tracker.c
> +++ b/lib/ref_tracker.c
> @@ -65,6 +65,7 @@ ref_tracker_get_stats(struct ref_tracker_dir *dir,
> unsigned int limit)
> struct ostream {
> char *buf;
> int size, used;
> + size_t needed;
> };
>
> #define pr_ostream(stream, fmt, args...) \
> @@ -76,6 +77,7 @@ struct ostream {
> } else { \
> int ret, len = _s->size - _s->used; \
> ret = snprintf(_s->buf + _s->used, len, pr_fmt(fmt), ##args); \
> + _s->needed += ret; \
> _s->used += min(ret, len); \
> } \
> })
> @@ -141,7 +143,7 @@ void ref_tracker_dir_print(struct ref_tracker_dir *dir,
> }
> EXPORT_SYMBOL(ref_tracker_dir_print);
>
> -int ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf,
> size_t size)
> +int ref_tracker_dir_snprint(struct ref_tracker_dir *dir, char *buf,
> size_t size, size_t *needed)
> {
> struct ostream os = { .buf = buf, .size = size };
> unsigned long flags;
> @@ -150,6 +152,8 @@ int ref_tracker_dir_snprint(struct ref_tracker_dir
> *dir, char *buf, size_t size)
> __ref_tracker_dir_pr_ostream(dir, 16, &os);
> spin_unlock_irqrestore(&dir->lock, flags);
>
> + if (needed)
> + *needed = os.needed;
> return os.used;
> }
> EXPORT_SYMBOL(ref_tracker_dir_snprint);
> diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
> index ce4b01cc7aca15ddf74f4160580871868e693fb8..61ce889ab29c2b726eab064b0ecb39838db30229
> 100644
> --- a/net/core/net_namespace.c
> +++ b/net/core/net_namespace.c
> @@ -1529,13 +1529,14 @@ struct ns_debug_net {
> struct dentry *notrefcnt;
> };
>
> -#define MAX_NS_DEBUG_BUFSIZE (32 * PAGE_SIZE)
> +#define MAX_NS_DEBUG_BUFSIZE (1 << 20)
>
> static int
> ns_debug_tracker_show(struct seq_file *f, void *v)
> {
> struct ref_tracker_dir *tracker = f->private;
> int len, bufsize = PAGE_SIZE;
> + size_t needed;
> char *buf;
>
> for (;;) {
> @@ -1543,12 +1544,12 @@ ns_debug_tracker_show(struct seq_file *f, void *v)
> if (!buf)
> return -ENOMEM;
>
> - len = ref_tracker_dir_snprint(tracker, buf, bufsize);
> + len = ref_tracker_dir_snprint(tracker, buf, bufsize, &needed);
> if (len < bufsize)
> break;
>
> kvfree(buf);
> - bufsize *= 2;
> + bufsize = round_up(needed, PAGE_SIZE);
> if (bufsize > MAX_NS_DEBUG_BUFSIZE)
> return -ENOBUFS;
> }
--
Jeff Layton <jlayton@...nel.org>
Powered by blists - more mailing lists