diff mbox series

[net-next,v3,2/2] netcons: Add udp send fail statistics to netconsole

Message ID 20240912173608.1821083-2-max@kutsevol.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [net-next,v3,1/2] netpoll: Make netpoll_send_udp return status instead of void | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 16 this patch: 16
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 2 maintainers not CCed: corbet@lwn.net linux-doc@vger.kernel.org
netdev/build_clang success Errors and warnings before: 16 this patch: 16
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 16 this patch: 16
netdev/checkpatch warning WARNING: line length of 95 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-09-13--15-00 (tests: 764)

Commit Message

Maksym Kutsevol Sept. 12, 2024, 5:28 p.m. UTC
Enhance observability of netconsole. Packet sends can fail.
Start tracking at least two failure possibilities: ENOMEM and
NET_XMIT_DROP for every target. Stats are exposed via an additional
attribute in CONFIGFS.

The exposed statistics allows easier debugging of cases when netconsole
messages were not seen by receivers, eliminating the guesswork if the
sender thinks that messages in question were sent out.

Stats are not reset on enable/disable/change remote ip/etc, they
belong to the netcons target itself.

Reported-by: Breno Leitao <leitao@debian.org>
Closes: https://lore.kernel.org/all/ZsWoUzyK5du9Ffl+@gmail.com/
Signed-off-by: Maksym Kutsevol <max@kutsevol.com>
---
Changelog:
v3:
 * cleanup the accidental slip of debugging addons.
 * use IS_ENABLED() instead of #ifdef. Always have stats field.

v2:
 * https://lore.kernel.org/netdev/20240828214524.1867954-2-max@kutsevol.com/
 * fixed commit message wording and reported-by reference.
 * not hiding netconsole_target_stats when CONFIG_NETCONSOLE_DYNAMIC
   is not enabled.
 * rename stats attribute in configfs to transmit_errors and make it
   a single u64 value, which is a sum of errors that occured.
 * make a wrapper function to count errors instead of a return result
   classifier one.
 * use u64_stats_sync.h to manage stats.

v1:
 * https://lore.kernel.org/netdev/20240824215130.2134153-2-max@kutsevol.com/

 Documentation/networking/netconsole.rst |  5 ++-
 drivers/net/netconsole.c                | 60 +++++++++++++++++++++++--
 2 files changed, 60 insertions(+), 5 deletions(-)

Comments

Breno Leitao Sept. 12, 2024, 5:49 p.m. UTC | #1
Hello Maksym,

Thanks for the patch, it is looking good. A few nits:

On Thu, Sep 12, 2024 at 10:28:52AM -0700, Maksym Kutsevol wrote:
> +/**
> + * netpoll_send_udp_count_errs - Wrapper for netpoll_send_udp that counts errors
> + * @nt: target to send message to
> + * @msg: message to send
> + * @len: length of message
> + *
> + * Calls netpoll_send_udp and classifies the return value. If an error
> + * occurred it increments statistics in nt->stats accordingly.
> + * Only calls netpoll_send_udp if CONFIG_NETCONSOLE_DYNAMIC is disabled.
> + */
> +static void netpoll_send_udp_count_errs(struct netconsole_target *nt, const char *msg, int len)
> +{
> +	int result = netpoll_send_udp(&nt->np, msg, len);

Would you get a "variable defined but not used" type of eror if
CONFIG_NETCONSOLE_DYNAMIC is disabled?

> +
> +	if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
> +		if (result == NET_XMIT_DROP) {
> +			u64_stats_update_begin(&nt->stats.syncp);
> +			u64_stats_inc(&nt->stats.xmit_drop_count);
> +			u64_stats_update_end(&nt->stats.syncp);
> +		} else if (result == -ENOMEM) {
> +			u64_stats_update_begin(&nt->stats.syncp);
> +			u64_stats_inc(&nt->stats.enomem_count);
> +			u64_stats_update_end(&nt->stats.syncp);
> +		}
> +	}

Would this look better?

	if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
		u64_stats_update_begin(&nt->stats.syncp);

		if (result == NET_XMIT_DROP)
			u64_stats_inc(&nt->stats.xmit_drop_count);
		else if (result == -ENOMEM)
			u64_stats_inc(&nt->stats.enomem_count);
		else
			WARN_ONCE(true, "invalid result: %d\n", result)

		u64_stats_update_end(&nt->stats.syncp);
	}

Thanks
--breno
Maksym Kutsevol Sept. 12, 2024, 5:58 p.m. UTC | #2
Hey Breno,
Thanks for looking into this.

On Thu, Sep 12, 2024 at 1:49 PM Breno Leitao <leitao@debian.org> wrote:
>
> Hello Maksym,
>
> Thanks for the patch, it is looking good. A few nits:
>
> On Thu, Sep 12, 2024 at 10:28:52AM -0700, Maksym Kutsevol wrote:
> > +/**
> > + * netpoll_send_udp_count_errs - Wrapper for netpoll_send_udp that counts errors
> > + * @nt: target to send message to
> > + * @msg: message to send
> > + * @len: length of message
> > + *
> > + * Calls netpoll_send_udp and classifies the return value. If an error
> > + * occurred it increments statistics in nt->stats accordingly.
> > + * Only calls netpoll_send_udp if CONFIG_NETCONSOLE_DYNAMIC is disabled.
> > + */
> > +static void netpoll_send_udp_count_errs(struct netconsole_target *nt, const char *msg, int len)
> > +{
> > +     int result = netpoll_send_udp(&nt->np, msg, len);
>
> Would you get a "variable defined but not used" type of eror if
> CONFIG_NETCONSOLE_DYNAMIC is disabled?
>
Most probably yes, I'll check. If so, I'll add __maybe_unused in the
next iteration.

> > +
> > +     if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
> > +             if (result == NET_XMIT_DROP) {
> > +                     u64_stats_update_begin(&nt->stats.syncp);
> > +                     u64_stats_inc(&nt->stats.xmit_drop_count);
> > +                     u64_stats_update_end(&nt->stats.syncp);
> > +             } else if (result == -ENOMEM) {
> > +                     u64_stats_update_begin(&nt->stats.syncp);
> > +                     u64_stats_inc(&nt->stats.enomem_count);
> > +                     u64_stats_update_end(&nt->stats.syncp);
> > +             }
> > +     }
>
> Would this look better?
>
>         if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
>                 u64_stats_update_begin(&nt->stats.syncp);
>
>                 if (result == NET_XMIT_DROP)
>                         u64_stats_inc(&nt->stats.xmit_drop_count);
>                 else if (result == -ENOMEM)
>                         u64_stats_inc(&nt->stats.enomem_count);
>                 else
>                         WARN_ONCE(true, "invalid result: %d\n", result)
>
>                 u64_stats_update_end(&nt->stats.syncp);
>         }
>
1. It will warn on positive result
2. If the last `else` is removed, it attempts locking when the result
is positive, so I'd not do it this way.



> Thanks
> --breno
Maksym Kutsevol Sept. 12, 2024, 6:05 p.m. UTC | #3
On Thu, Sep 12, 2024 at 1:58 PM Maksym Kutsevol <max@kutsevol.com> wrote:
>
> Hey Breno,
> Thanks for looking into this.
>
> On Thu, Sep 12, 2024 at 1:49 PM Breno Leitao <leitao@debian.org> wrote:
> >
> > Hello Maksym,
> >
> > Thanks for the patch, it is looking good. A few nits:
> >
> > On Thu, Sep 12, 2024 at 10:28:52AM -0700, Maksym Kutsevol wrote:
> > > +/**
> > > + * netpoll_send_udp_count_errs - Wrapper for netpoll_send_udp that counts errors
> > > + * @nt: target to send message to
> > > + * @msg: message to send
> > > + * @len: length of message
> > > + *
> > > + * Calls netpoll_send_udp and classifies the return value. If an error
> > > + * occurred it increments statistics in nt->stats accordingly.
> > > + * Only calls netpoll_send_udp if CONFIG_NETCONSOLE_DYNAMIC is disabled.
> > > + */
> > > +static void netpoll_send_udp_count_errs(struct netconsole_target *nt, const char *msg, int len)
> > > +{
> > > +     int result = netpoll_send_udp(&nt->np, msg, len);
> >
> > Would you get a "variable defined but not used" type of eror if
> > CONFIG_NETCONSOLE_DYNAMIC is disabled?
> >
> Most probably yes, I'll check. If so, I'll add __maybe_unused in the
> next iteration.

No, there's no warning. As it's used and then optimized out by the compiler.
Breno Leitao Sept. 12, 2024, 7:13 p.m. UTC | #4
On Thu, Sep 12, 2024 at 01:58:12PM -0400, Maksym Kutsevol wrote:
> Hey Breno,
> Thanks for looking into this.
> 
> On Thu, Sep 12, 2024 at 1:49 PM Breno Leitao <leitao@debian.org> wrote:
> >
> > Hello Maksym,
> >
> > Thanks for the patch, it is looking good. A few nits:
> >
> > On Thu, Sep 12, 2024 at 10:28:52AM -0700, Maksym Kutsevol wrote:
> > > +/**
> > > + * netpoll_send_udp_count_errs - Wrapper for netpoll_send_udp that counts errors
> > > + * @nt: target to send message to
> > > + * @msg: message to send
> > > + * @len: length of message
> > > + *
> > > + * Calls netpoll_send_udp and classifies the return value. If an error
> > > + * occurred it increments statistics in nt->stats accordingly.
> > > + * Only calls netpoll_send_udp if CONFIG_NETCONSOLE_DYNAMIC is disabled.
> > > + */
> > > +static void netpoll_send_udp_count_errs(struct netconsole_target *nt, const char *msg, int len)
> > > +{
> > > +     int result = netpoll_send_udp(&nt->np, msg, len);
> >
> > Would you get a "variable defined but not used" type of eror if
> > CONFIG_NETCONSOLE_DYNAMIC is disabled?
> >
> Most probably yes, I'll check. If so, I'll add __maybe_unused in the
> next iteration.
> 
> > > +
> > > +     if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
> > > +             if (result == NET_XMIT_DROP) {
> > > +                     u64_stats_update_begin(&nt->stats.syncp);
> > > +                     u64_stats_inc(&nt->stats.xmit_drop_count);
> > > +                     u64_stats_update_end(&nt->stats.syncp);
> > > +             } else if (result == -ENOMEM) {
> > > +                     u64_stats_update_begin(&nt->stats.syncp);
> > > +                     u64_stats_inc(&nt->stats.enomem_count);
> > > +                     u64_stats_update_end(&nt->stats.syncp);
> > > +             }
> > > +     }
> >
> > Would this look better?
> >
> >         if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
> >                 u64_stats_update_begin(&nt->stats.syncp);
> >
> >                 if (result == NET_XMIT_DROP)
> >                         u64_stats_inc(&nt->stats.xmit_drop_count);
> >                 else if (result == -ENOMEM)
> >                         u64_stats_inc(&nt->stats.enomem_count);
> >                 else
> >                         WARN_ONCE(true, "invalid result: %d\n", result)
> >
> >                 u64_stats_update_end(&nt->stats.syncp);
> >         }
> >
> 1. It will warn on positive result
> 2. If the last `else` is removed, it attempts locking when the result
> is positive, so I'd not do it this way.

Correct. We could replace the WARN_ONCE(true, ..) by
WARN_ONCE(result,..), but this might look worse.

Let's keep the way you proposed.

Other than that, the patch looks good.

Thanks
diff mbox series

Patch

diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
index d55c2a22ec7a..94c4680fdf3e 100644
--- a/Documentation/networking/netconsole.rst
+++ b/Documentation/networking/netconsole.rst
@@ -124,7 +124,7 @@  To remove a target::
 
 The interface exposes these parameters of a netconsole target to userspace:
 
-	==============  =================================       ============
+	=============== =================================       ============
 	enabled		Is this target currently enabled?	(read-write)
 	extended	Extended mode enabled			(read-write)
 	release		Prepend kernel release to message	(read-write)
@@ -135,7 +135,8 @@  The interface exposes these parameters of a netconsole target to userspace:
 	remote_ip	Remote agent's IP address		(read-write)
 	local_mac	Local interface's MAC address		(read-only)
 	remote_mac	Remote agent's MAC address		(read-write)
-	==============  =================================       ============
+	transmit_errors	Number of packet send errors		(read-only)
+	=============== =================================       ============
 
 The "enabled" attribute is also used to control whether the parameters of
 a target can be updated or not -- you can modify the parameters of only
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 01cf33fa7503..fe6f29171a83 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -36,6 +36,7 @@ 
 #include <linux/inet.h>
 #include <linux/configfs.h>
 #include <linux/etherdevice.h>
+#include <linux/u64_stats_sync.h>
 #include <linux/utsname.h>
 #include <linux/rtnetlink.h>
 
@@ -90,6 +91,12 @@  static DEFINE_MUTEX(target_cleanup_list_lock);
  */
 static struct console netconsole_ext;
 
+struct netconsole_target_stats  {
+	u64_stats_t xmit_drop_count;
+	u64_stats_t enomem_count;
+	struct u64_stats_sync syncp;
+} __aligned(2 * sizeof(u64));
+
 /**
  * struct netconsole_target - Represents a configured netconsole target.
  * @list:	Links this target into the target_list.
@@ -97,6 +104,7 @@  static struct console netconsole_ext;
  * @userdata_group:	Links to the userdata configfs hierarchy
  * @userdata_complete:	Cached, formatted string of append
  * @userdata_length:	String length of userdata_complete
+ * @stats:	Packet send stats for the target. Used for debugging.
  * @enabled:	On / off knob to enable / disable target.
  *		Visible from userspace (read-write).
  *		We maintain a strict 1:1 correspondence between this and
@@ -124,6 +132,7 @@  struct netconsole_target {
 	char userdata_complete[MAX_USERDATA_ENTRY_LENGTH * MAX_USERDATA_ITEMS];
 	size_t			userdata_length;
 #endif
+	struct netconsole_target_stats stats;
 	bool			enabled;
 	bool			extended;
 	bool			release;
@@ -262,6 +271,7 @@  static void netconsole_process_cleanups_core(void)
  *				|	remote_ip
  *				|	local_mac
  *				|	remote_mac
+ *				|	transmit_errors
  *				|	userdata/
  *				|		<key>/
  *				|			value
@@ -371,6 +381,21 @@  static ssize_t remote_mac_show(struct config_item *item, char *buf)
 	return sysfs_emit(buf, "%pM\n", to_target(item)->np.remote_mac);
 }
 
+static ssize_t transmit_errors_show(struct config_item *item, char *buf)
+{
+	struct netconsole_target *nt = to_target(item);
+	u64 xmit_drop_count, enomem_count;
+	unsigned int start;
+
+	do {
+		start = u64_stats_fetch_begin(&nt->stats.syncp);
+		xmit_drop_count = u64_stats_read(&nt->stats.xmit_drop_count);
+		enomem_count = u64_stats_read(&nt->stats.enomem_count);
+	} while (u64_stats_fetch_retry(&nt->stats.syncp, start));
+
+	return sysfs_emit(buf, "%llu\n", xmit_drop_count + enomem_count);
+}
+
 /*
  * This one is special -- targets created through the configfs interface
  * are not enabled (and the corresponding netpoll activated) by default.
@@ -842,6 +867,7 @@  CONFIGFS_ATTR(, remote_ip);
 CONFIGFS_ATTR_RO(, local_mac);
 CONFIGFS_ATTR(, remote_mac);
 CONFIGFS_ATTR(, release);
+CONFIGFS_ATTR_RO(, transmit_errors);
 
 static struct configfs_attribute *netconsole_target_attrs[] = {
 	&attr_enabled,
@@ -854,6 +880,7 @@  static struct configfs_attribute *netconsole_target_attrs[] = {
 	&attr_remote_ip,
 	&attr_local_mac,
 	&attr_remote_mac,
+	&attr_transmit_errors,
 	NULL,
 };
 
@@ -1058,6 +1085,33 @@  static struct notifier_block netconsole_netdev_notifier = {
 	.notifier_call  = netconsole_netdev_event,
 };
 
+/**
+ * netpoll_send_udp_count_errs - Wrapper for netpoll_send_udp that counts errors
+ * @nt: target to send message to
+ * @msg: message to send
+ * @len: length of message
+ *
+ * Calls netpoll_send_udp and classifies the return value. If an error
+ * occurred it increments statistics in nt->stats accordingly.
+ * Only calls netpoll_send_udp if CONFIG_NETCONSOLE_DYNAMIC is disabled.
+ */
+static void netpoll_send_udp_count_errs(struct netconsole_target *nt, const char *msg, int len)
+{
+	int result = netpoll_send_udp(&nt->np, msg, len);
+
+	if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
+		if (result == NET_XMIT_DROP) {
+			u64_stats_update_begin(&nt->stats.syncp);
+			u64_stats_inc(&nt->stats.xmit_drop_count);
+			u64_stats_update_end(&nt->stats.syncp);
+		} else if (result == -ENOMEM) {
+			u64_stats_update_begin(&nt->stats.syncp);
+			u64_stats_inc(&nt->stats.enomem_count);
+			u64_stats_update_end(&nt->stats.syncp);
+		}
+	}
+}
+
 /**
  * send_ext_msg_udp - send extended log message to target
  * @nt: target to send message to
@@ -1106,7 +1160,7 @@  static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
 					     "%s", userdata);
 
 		msg_ready = buf;
-		netpoll_send_udp(&nt->np, msg_ready, msg_len);
+		netpoll_send_udp_count_errs(nt, msg_ready, msg_len);
 		return;
 	}
 
@@ -1169,7 +1223,7 @@  static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
 			this_offset += this_chunk;
 		}
 
-		netpoll_send_udp(&nt->np, buf, this_header + this_offset);
+		netpoll_send_udp_count_errs(nt, buf, this_header + this_offset);
 		offset += this_offset;
 	}
 }
@@ -1215,7 +1269,7 @@  static void write_msg(struct console *con, const char *msg, unsigned int len)
 			tmp = msg;
 			for (left = len; left;) {
 				frag = min(left, MAX_PRINT_CHUNK);
-				netpoll_send_udp(&nt->np, tmp, frag);
+				netpoll_send_udp_count_errs(nt, tmp, frag);
 				tmp += frag;
 				left -= frag;
 			}