[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CANn89iL44SvNKgK-fbm2+bWUpCk+cT0LFVaMGj7HdVOkRiW9Vg@mail.gmail.com>
Date: Tue, 25 Mar 2025 06:21:44 +0100
From: Eric Dumazet <edumazet@...gle.com>
To: Jeff Layton <jlayton@...nel.org>
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 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.
> +#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...
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;
}
Powered by blists - more mailing lists