lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <jejsptfg7cqmbcm467tb72gg3mwsqge6iz4qy4wy5ifev2sgim@hukyfgsr74xj>
Date: Fri, 7 Nov 2025 05:23:26 -0800
From: Breno Leitao <leitao@...ian.org>
To: Gustavo Luiz Duarte <gustavold@...il.com>
Cc: Andre Carvalho <asantostc@...il.com>, Simon Horman <horms@...nel.org>, 
	Andrew Lunn <andrew+netdev@...n.ch>, "David S. Miller" <davem@...emloft.net>, 
	Eric Dumazet <edumazet@...gle.com>, Jakub Kicinski <kuba@...nel.org>, 
	Paolo Abeni <pabeni@...hat.com>, Shuah Khan <shuah@...nel.org>, netdev@...r.kernel.org, 
	linux-kernel@...r.kernel.org, linux-kselftest@...r.kernel.org
Subject: Re: [PATCH net-next 2/4] netconsole: Split userdata and sysdata

On Wed, Nov 05, 2025 at 09:06:44AM -0800, Gustavo Luiz Duarte wrote:
> Separate userdata and sysdata into distinct buffers to enable independent
> management. Previously, both were stored in a single extradata_complete
> buffer with a fixed size that accommodated both types of data.
> 
> This separation allows:
> - userdata to grow dynamically (in subsequent patch)
> - sysdata to remain in a small static buffer
> - removal of complex entry counting logic that tracked both types together
> 
> The split also simplifies the code by eliminating the need to check total
> entry count across both userdata and sysdata when enabling features,
> which allows to drop holding su_mutex on sysdata_*_enabled_store().
> 
> No functional change in this patch, just structural preparation for
> dynamic userdata allocation.
> 
> Signed-off-by: Gustavo Luiz Duarte <gustavold@...il.com>
> ---
>  drivers/net/netconsole.c | 204 +++++++++++++++++++----------------------------
>  1 file changed, 84 insertions(+), 120 deletions(-)
> 
> diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
> index 0a8ba7c4bc9d..e780c884db83 100644
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
> @@ -50,7 +50,8 @@ MODULE_LICENSE("GPL");
>  /* The number 3 comes from userdata entry format characters (' ', '=', '\n') */
>  #define MAX_EXTRADATA_NAME_LEN		(MAX_EXTRADATA_ENTRY_LEN - \
>  					MAX_EXTRADATA_VALUE_LEN - 3)
> -#define MAX_EXTRADATA_ITEMS		16
> +#define MAX_USERDATA_ITEMS		16

Do we still need to have MAX_USERDATA_ITEMS cap with your new approach?

> +#define MAX_SYSDATA_ITEMS		4

Can we have this one inside enum sysdata_feature?

Something as:

  enum sysdata_feature {
      SYSDATA_CPU_NR = BIT(0),
      SYSDATA_TASKNAME = BIT(1),
      SYSDATA_RELEASE = BIT(2),
      SYSDATA_MSGID = BIT(3),
      MAX_SYSDATA_ITEMS = 4  /* Sentinel: highest bit position */
  };

>  #define MAX_PRINT_CHUNK			1000
>  
>  static char config[MAX_PARAM_LENGTH];
> @@ -122,8 +123,9 @@ enum sysdata_feature {
>   * @list:	Links this target into the target_list.
>   * @group:	Links us into the configfs subsystem hierarchy.
>   * @userdata_group:	Links to the userdata configfs hierarchy
> - * @extradata_complete:	Cached, formatted string of append
> - * @userdata_length:	String length of usedata in extradata_complete.
> + * @userdata:		Cached, formatted string of append
> + * @userdata_length:	String length of userdata.
> + * @sysdata:		Cached, formatted string of append
>   * @sysdata_fields:	Sysdata features enabled.
>   * @msgcounter:	Message sent counter.
>   * @stats:	Packet send stats for the target. Used for debugging.
> @@ -152,8 +154,10 @@ struct netconsole_target {
>  #ifdef	CONFIG_NETCONSOLE_DYNAMIC
>  	struct config_group	group;
>  	struct config_group	userdata_group;
> -	char extradata_complete[MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS];
> +	char			userdata[MAX_EXTRADATA_ENTRY_LEN * MAX_USERDATA_ITEMS];
>  	size_t			userdata_length;
> +	char			sysdata[MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS];
> +
>  	/* bit-wise with sysdata_feature bits */
>  	u32			sysdata_fields;
>  	/* protected by target_list_lock */
> @@ -802,28 +806,14 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf,
>  	return ret;
>  }
>  
> -/* Count number of entries we have in extradata.
> - * This is important because the extradata_complete only supports
> - * MAX_EXTRADATA_ITEMS entries. Before enabling any new {user,sys}data
> - * feature, number of entries needs to checked for available space.
> +/* Count number of entries we have in userdata.
> + * This is important because userdata only supports MAX_USERDATA_ITEMS
> + * entries. Before enabling any new userdata feature, number of entries needs
> + * to checked for available space.
>   */
> -static size_t count_extradata_entries(struct netconsole_target *nt)
> +static size_t count_userdata_entries(struct netconsole_target *nt)
>  {
> -	size_t entries;
> -
> -	/* Userdata entries */
> -	entries = list_count_nodes(&nt->userdata_group.cg_children);
> -	/* Plus sysdata entries */
> -	if (nt->sysdata_fields & SYSDATA_CPU_NR)
> -		entries += 1;
> -	if (nt->sysdata_fields & SYSDATA_TASKNAME)
> -		entries += 1;
> -	if (nt->sysdata_fields & SYSDATA_RELEASE)
> -		entries += 1;
> -	if (nt->sysdata_fields & SYSDATA_MSGID)
> -		entries += 1;
> -
> -	return entries;
> +	return list_count_nodes(&nt->userdata_group.cg_children);
>  }
>  
>  static ssize_t remote_mac_store(struct config_item *item, const char *buf,
> @@ -894,13 +884,13 @@ static void update_userdata(struct netconsole_target *nt)
>  
>  	/* Clear the current string in case the last userdatum was deleted */
>  	nt->userdata_length = 0;
> -	nt->extradata_complete[0] = 0;
> +	nt->userdata[0] = 0;
>  
>  	list_for_each(entry, &nt->userdata_group.cg_children) {
>  		struct userdatum *udm_item;
>  		struct config_item *item;
>  
> -		if (child_count >= MAX_EXTRADATA_ITEMS) {
> +		if (child_count >= MAX_USERDATA_ITEMS) {
>  			spin_unlock_irqrestore(&target_list_lock, flags);
>  			WARN_ON_ONCE(1);
>  			return;
> @@ -914,11 +904,11 @@ static void update_userdata(struct netconsole_target *nt)
>  		if (strnlen(udm_item->value, MAX_EXTRADATA_VALUE_LEN) == 0)
>  			continue;
>  
> -		/* This doesn't overflow extradata_complete since it will write
> -		 * one entry length (1/MAX_EXTRADATA_ITEMS long), entry count is
> +		/* This doesn't overflow userdata since it will write
> +		 * one entry length (1/MAX_USERDATA_ITEMS long), entry count is
>  		 * checked to not exceed MAX items with child_count above
>  		 */
> -		nt->userdata_length += scnprintf(&nt->extradata_complete[nt->userdata_length],
> +		nt->userdata_length += scnprintf(&nt->userdata[nt->userdata_length],
>  						 MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n",
>  						 item->ci_name, udm_item->value);
>  	}
> @@ -960,7 +950,7 @@ static void disable_sysdata_feature(struct netconsole_target *nt,
>  				    enum sysdata_feature feature)
>  {
>  	nt->sysdata_fields &= ~feature;
> -	nt->extradata_complete[nt->userdata_length] = 0;
> +	nt->sysdata[0] = 0;
>  }
>  
>  static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
> @@ -979,12 +969,6 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
>  	if (msgid_enabled == curr)
>  		goto unlock_ok;
>  
> -	if (msgid_enabled &&
> -	    count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
> -		ret = -ENOSPC;
> -		goto unlock;
> -	}
> -
>  	if (msgid_enabled)
>  		nt->sysdata_fields |= SYSDATA_MSGID;
>  	else
> @@ -992,7 +976,6 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
>  
>  unlock_ok:
>  	ret = strnlen(buf, count);
> -unlock:
>  	mutex_unlock(&dynamic_netconsole_mutex);
>  	return ret;
>  }
> @@ -1013,12 +996,6 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item,
>  	if (release_enabled == curr)
>  		goto unlock_ok;
>  
> -	if (release_enabled &&
> -	    count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
> -		ret = -ENOSPC;
> -		goto unlock;
> -	}
> -
>  	if (release_enabled)
>  		nt->sysdata_fields |= SYSDATA_RELEASE;
>  	else
> @@ -1026,7 +1003,6 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item,
>  
>  unlock_ok:
>  	ret = strnlen(buf, count);
> -unlock:
>  	mutex_unlock(&dynamic_netconsole_mutex);
>  	return ret;
>  }
> @@ -1047,12 +1023,6 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item,
>  	if (taskname_enabled == curr)
>  		goto unlock_ok;
>  
> -	if (taskname_enabled &&
> -	    count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
> -		ret = -ENOSPC;
> -		goto unlock;
> -	}
> -
>  	if (taskname_enabled)
>  		nt->sysdata_fields |= SYSDATA_TASKNAME;
>  	else
> @@ -1060,7 +1030,6 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item,
>  
>  unlock_ok:
>  	ret = strnlen(buf, count);
> -unlock:
>  	mutex_unlock(&dynamic_netconsole_mutex);
>  	return ret;
>  }
> @@ -1083,27 +1052,16 @@ static ssize_t sysdata_cpu_nr_enabled_store(struct config_item *item,
>  		/* no change requested */
>  		goto unlock_ok;
>  
> -	if (cpu_nr_enabled &&
> -	    count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
> -		/* user wants the new feature, but there is no space in the
> -		 * buffer.
> -		 */
> -		ret = -ENOSPC;
> -		goto unlock;
> -	}
> -
>  	if (cpu_nr_enabled)
>  		nt->sysdata_fields |= SYSDATA_CPU_NR;
>  	else
> -		/* This is special because extradata_complete might have
> -		 * remaining data from previous sysdata, and it needs to be
> -		 * cleaned.
> +		/* This is special because sysdata might have remaining data
> +		 * from previous sysdata, and it needs to be cleaned.
>  		 */
>  		disable_sysdata_feature(nt, SYSDATA_CPU_NR);
>  
>  unlock_ok:
>  	ret = strnlen(buf, count);
> -unlock:
>  	mutex_unlock(&dynamic_netconsole_mutex);
>  	return ret;
>  }
> @@ -1146,7 +1104,7 @@ static struct config_item *userdatum_make_item(struct config_group *group,
>  
>  	ud = to_userdata(&group->cg_item);
>  	nt = userdata_to_target(ud);
> -	if (count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS)
> +	if (count_userdata_entries(nt) >= MAX_USERDATA_ITEMS)
>  		return ERR_PTR(-ENOSPC);
>  
>  	udm = kzalloc(sizeof(*udm), GFP_KERNEL);
> @@ -1353,22 +1311,21 @@ static void populate_configfs_item(struct netconsole_target *nt,
>  
>  static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset)
>  {
> -	/* Append cpu=%d at extradata_complete after userdata str */
> -	return scnprintf(&nt->extradata_complete[offset],
> +	return scnprintf(&nt->sysdata[offset],
>  			 MAX_EXTRADATA_ENTRY_LEN, " cpu=%u\n",

This is confusing. It is writing to sysdata but checking extradata entry len.

>  static int sysdata_append_taskname(struct netconsole_target *nt, int offset)
>  {
> -	return scnprintf(&nt->extradata_complete[offset],
> +	return scnprintf(&nt->sysdata[offset],
>  			 MAX_EXTRADATA_ENTRY_LEN, " taskname=%s\n",

Same as above.

>  }
>  
>  static int sysdata_append_release(struct netconsole_target *nt, int offset)
>  {
> -	return scnprintf(&nt->extradata_complete[offset],
> +	return scnprintf(&nt->sysdata[offset],
>  			 MAX_EXTRADATA_ENTRY_LEN, " release=%s\n",
>  			 init_utsname()->release);
>  }
> @@ -1376,46 +1333,36 @@ static int sysdata_append_release(struct netconsole_target *nt, int offset)
>  static int sysdata_append_msgid(struct netconsole_target *nt, int offset)
>  {
>  	wrapping_assign_add(nt->msgcounter, 1);
> -	return scnprintf(&nt->extradata_complete[offset],
> +	return scnprintf(&nt->sysdata[offset],
>  			 MAX_EXTRADATA_ENTRY_LEN, " msgid=%u\n",
>  			 nt->msgcounter);
>  }
>  
>  /*
> - * prepare_extradata - append sysdata at extradata_complete in runtime
> + * prepare_sysdata - append sysdata in runtime
>   * @nt: target to send message to
>   */
> -static int prepare_extradata(struct netconsole_target *nt)
> +static int prepare_sysdata(struct netconsole_target *nt)
>  {
> -	int extradata_len;
> -
> -	/* userdata was appended when configfs write helper was called
> -	 * by update_userdata().
> -	 */
> -	extradata_len = nt->userdata_length;
> +	int sysdata_len = 0;
>  
>  	if (!nt->sysdata_fields)
>  		goto out;
>  
>  	if (nt->sysdata_fields & SYSDATA_CPU_NR)
> -		extradata_len += sysdata_append_cpu_nr(nt, extradata_len);
> +		sysdata_len += sysdata_append_cpu_nr(nt, sysdata_len);
>  	if (nt->sysdata_fields & SYSDATA_TASKNAME)
> -		extradata_len += sysdata_append_taskname(nt, extradata_len);
> +		sysdata_len += sysdata_append_taskname(nt, sysdata_len);
>  	if (nt->sysdata_fields & SYSDATA_RELEASE)
> -		extradata_len += sysdata_append_release(nt, extradata_len);
> +		sysdata_len += sysdata_append_release(nt, sysdata_len);
>  	if (nt->sysdata_fields & SYSDATA_MSGID)
> -		extradata_len += sysdata_append_msgid(nt, extradata_len);
> +		sysdata_len += sysdata_append_msgid(nt, sysdata_len);
>  
> -	WARN_ON_ONCE(extradata_len >
> -		     MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS);
> +	WARN_ON_ONCE(sysdata_len >
> +		     MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS);
>  
>  out:
> -	return extradata_len;
> -}
> -#else /* CONFIG_NETCONSOLE_DYNAMIC not set */
> -static int prepare_extradata(struct netconsole_target *nt)
> -{
> -	return 0;
> +	return sysdata_len;
>  }
>  #endif	/* CONFIG_NETCONSOLE_DYNAMIC */
>  
> @@ -1517,13 +1464,8 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt,
>  				      int msg_len,
>  				      int release_len)
>  {
> -	const char *extradata = NULL;
>  	const char *release;
>  
> -#ifdef CONFIG_NETCONSOLE_DYNAMIC
> -	extradata = nt->extradata_complete;
> -#endif
> -
>  	if (release_len) {
>  		release = init_utsname()->release;
>  
> @@ -1533,11 +1475,11 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt,
>  		memcpy(nt->buf, msg, msg_len);
>  	}
>  
> -	if (extradata)
> -		msg_len += scnprintf(&nt->buf[msg_len],
> -				     MAX_PRINT_CHUNK - msg_len,
> -				     "%s", extradata);
> -
> +#ifdef CONFIG_NETCONSOLE_DYNAMIC
> +	msg_len += scnprintf(&nt->buf[msg_len],
> +			     MAX_PRINT_CHUNK - msg_len,
> +			     "%s%s", nt->userdata, nt->sysdata);
> +#endif

I am not sure I like this ifdef in here. Can you if userdata or sysdata are
valid, and then scnprintf() instead of using ifdef?

>  				 const char *msgbody, int header_len,
> -				 int msgbody_len, int extradata_len)
> +				 int msgbody_len, int sysdata_len)
>  {
> -	const char *extradata = NULL;
> +	const char *userdata = NULL;
> +	const char *sysdata = NULL;
>  	int body_len, offset = 0;
> -	int extradata_offset = 0;
> +	int userdata_offset = 0;
>  	int msgbody_offset = 0;
> +	int sysdata_offset = 0;
> +	int userdata_len = 0;
>  
>  #ifdef CONFIG_NETCONSOLE_DYNAMIC
> -	extradata = nt->extradata_complete;
> +	userdata = nt->userdata;
> +	sysdata = nt->sysdata;
> +	userdata_len = nt->userdata_length;
>  #endif
> -	if (WARN_ON_ONCE(!extradata && extradata_len != 0))
> +	if (WARN_ON_ONCE(!userdata && userdata_len != 0))
> +		return;
> +
> +	if (WARN_ON_ONCE(!sysdata && sysdata_len != 0))
>  		return;
>  
>  	/* body_len represents the number of bytes that will be sent. This is
>  	 * bigger than MAX_PRINT_CHUNK, thus, it will be split in multiple
>  	 * packets
>  	 */
> -	body_len = msgbody_len + extradata_len;
> +	body_len = msgbody_len + userdata_len + sysdata_len;
>  
>  	/* In each iteration of the while loop below, we send a packet
>  	 * containing the header and a portion of the body. The body is
> -	 * composed of two parts: msgbody and extradata. We keep track of how
> -	 * many bytes have been sent so far using the offset variable, which
> -	 * ranges from 0 to the total length of the body.
> +	 * composed of three parts: msgbody, userdata and sysdata.
> +	 * We keep track of how many bytes have been sent from each part using
> +	 * the *_offset variables.
> +	 * We keep track of how many bytes have been sent overall using the
> +	 * offset variable, which ranges from 0 to the total length of the
> +	 * body.
>  	 */
>  	while (offset < body_len) {
>  		int this_header = header_len;
> @@ -1594,12 +1547,20 @@ static void send_fragmented_body(struct netconsole_target *nt,
>  		msgbody_offset += this_chunk;
>  		this_offset += this_chunk;
>  
> -		/* after msgbody, append extradata */
> -		this_chunk = min(extradata_len - extradata_offset,
> +		/* after msgbody, append userdata */
> +		this_chunk = min(userdata_len - userdata_offset,

Please assign this "userdata_len - userdata_offset" to a variable and give it a
name, so, it help us to reason about the code. 

>  				 MAX_PRINT_CHUNK - this_header - this_offset);
>  		memcpy(nt->buf + this_header + this_offset,
> -		       extradata + extradata_offset, this_chunk);
> -		extradata_offset += this_chunk;
> +		       userdata + userdata_offset, this_chunk);
> +		userdata_offset += this_chunk;
> +		this_offset += this_chunk;
> +
> +		/* after userdata, append sysdata */
> +		this_chunk = min(sysdata_len - sysdata_offset,
> +				 MAX_PRINT_CHUNK - this_header - this_offset);
> +		memcpy(nt->buf + this_header + this_offset,
> +		       sysdata + sysdata_offset, this_chunk);

s/this_header/this_header_offset?

Now that you are touching this code, please review these variable to keep them named correct.

Maybe adding _ptr for pointer, and _offset for offsets and _len for lenghts?

Thank you for your work reasong about all of this!
--breno

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ