Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'netconsole-cpu-population'

Breno Leitao says:

====================
netconsole: Add support for CPU population

The current implementation of netconsole sends all log messages in
parallel, which can lead to an intermixed and interleaved output on the
receiving side. This makes it challenging to demultiplex the messages
and attribute them to their originating CPUs.

As a result, users and developers often struggle to effectively analyze
and debug the parallel log output received through netconsole.

Example of a message got from produciton hosts:

------------[ cut here ]------------
------------[ cut here ]------------
refcount_t: saturated; leaking memory.
WARNING: CPU: 2 PID: 1613668 at lib/refcount.c:22 refcount_warn_saturate+0x5e/0xe0
refcount_t: addition on 0; use-after-free.
WARNING: CPU: 26 PID: 4139916 at lib/refcount.c:25 refcount_warn_saturate+0x7d/0xe0
Modules linked in: bpf_preload(E) vhost_net(E) tun(E) vhost(E)

This series of patches introduces a new feature to the netconsole
subsystem that allows the automatic population of the CPU number in the
userdata field for each log message. This enhancement provides several
benefits:

* Improved demultiplexing of parallel log output: When multiple CPUs are
sending messages concurrently, the added CPU number in the userdata
makes it easier to differentiate and attribute the messages to their
originating CPUs.

* Better visibility into message sources: The CPU number information
gives users and developers more insight into which specific CPU a
particular log message came from, which can be valuable for debugging
and analysis.

The changes in this series are as follows Patches::

Patch "consolidate send buffers into netconsole_target struct"
=================================================

Move the static buffers to netconsole target, from static declaration
in send_msg_no_fragmentation() and send_msg_fragmented().

Patch "netconsole: Rename userdata to extradata"
=================================================
Create the a concept of extradata, which encompasses the concept of
userdata and the upcoming sysdatao

Sysdata is a new concept being added, which is basically fields that are
populated by the kernel. At this time only the CPU#, but, there is a
desire to add current task name, kernel release version, etc.

Patch "netconsole: Helper to count number of used entries"
===========================================================
Create a simple helper to count number of entries in extradata. I am
separating this in a function since it will need to count userdata and
sysdata. For instance, when the user adds an extra userdata, we need to
check if there is space, counting the previous data entries (from
userdata and cpu data)

Patch "Introduce configfs helpers for sysdata features"
======================================================
Create the concept of sysdata feature in the netconsole target, and
create the configfs helpers to enable the bit in nt->sysdata

Patch "Include sysdata in extradata entry count"
================================================
Add the concept of sysdata when counting for available space in the
buffer. This will protect users from creating new userdata/sysdata if
there is no more space

Patch "netconsole: add support for sysdata and CPU population"
===============================================================
This is the core patch. Basically add a new option to enable automatic
CPU number population in the netconsole userdata Provides a new "cpu_nr"
sysfs attribute to control this feature

Patch "netconsole: selftest: test CPU number auto-population"
=============================================================
Expands the existing netconsole selftest to verify the CPU number
auto-population functionality Ensures the received netconsole messages
contain the expected "cpu=<CPU>" entry in the message. Test different
permutation with userdata

Patch "netconsole: docs: Add documentation for CPU number auto-population"
=============================================================================
Updates the netconsole documentation to explain the new CPU number
auto-population feature Provides instructions on how to enable and use
the feature

I believe these changes will be a valuable addition to the netconsole
subsystem, enhancing its usefulness for kernel developers and users.

PS: This patchset is on top of the patch that created
netcons_fragmented_msg selftest:

https://lore.kernel.org/all/20250203-netcons_frag_msgs-v1-1-5bc6bedf2ac0@debian.org/

---
Changes in v5:
- Fixed a kernel doc syntax syntax (Simon)
- Link to v4: https://lore.kernel.org/r/20250204-netcon_cpu-v4-0-9480266ef556@debian.org

Changes in v4:
- Fixed Kernel doc for netconsole_target (Simon)
- Fixed a typo in disable_sysdata_feature (Simon)
- Improved sysdata_cpu_nr_show() to return !! in a bit-wise operation
- Link to v3: https://lore.kernel.org/r/20250124-netcon_cpu-v3-0-12a0d286ba1d@debian.org

Changes in v3:
- Moved the buffer into netconsole_target, avoiding static functions in
the send path (Jakub).
- Fix a documentation error (Randy Dunlap)
- Created a function that handle all the extradata, consolidating it in
a single place (Jakub)
- Split the patch even more, trying to simplify the review.
- Link to v2: https://lore.kernel.org/r/20250115-netcon_cpu-v2-0-95971b44dc56@debian.org

Changes in v2:
- Create the concept of extradata and sysdata. This will make the design
easier to understand, and the code easier to read.
* Basically extradata encompasses userdata and the new sysdata.
Userdata originates from user, and sysdata originates in kernel.
- Improved the test to send from a very specific CPU, which can be
checked to be correct on the other side, as suggested by Jakub.
- Fixed a bug where CPU # was populated at the wrong place
- Link to v1: https://lore.kernel.org/r/20241113-netcon_cpu-v1-0-d187bf7c0321@debian.org
====================

Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

+427 -64
+45
Documentation/networking/netconsole.rst
··· 17 17 18 18 Userdata append support by Matthew Wood <thepacketgeek@gmail.com>, Jan 22 2024 19 19 20 + Sysdata append support by Breno Leitao <leitao@debian.org>, Jan 15 2025 21 + 20 22 Please send bug reports to Matt Mackall <mpm@selenic.com> 21 23 Satyam Sharma <satyam.sharma@gmail.com>, and Cong Wang <xiyou.wangcong@gmail.com> 22 24 ··· 239 237 val2 240 238 241 239 It is recommended to not write user data values with newlines. 240 + 241 + CPU number auto population in userdata 242 + -------------------------------------- 243 + 244 + Inside the netconsole configfs hierarchy, there is a file called 245 + `cpu_nr` under the `userdata` directory. This file is used to enable or disable 246 + the automatic CPU number population feature. This feature automatically 247 + populates the CPU number that is sending the message. 248 + 249 + To enable the CPU number auto-population:: 250 + 251 + echo 1 > /sys/kernel/config/netconsole/target1/userdata/cpu_nr 252 + 253 + When this option is enabled, the netconsole messages will include an additional 254 + line in the userdata field with the format `cpu=<cpu_number>`. This allows the 255 + receiver of the netconsole messages to easily differentiate and demultiplex 256 + messages originating from different CPUs, which is particularly useful when 257 + dealing with parallel log output. 258 + 259 + Example:: 260 + 261 + echo "This is a message" > /dev/kmsg 262 + 12,607,22085407756,-;This is a message 263 + cpu=42 264 + 265 + In this example, the message was sent by CPU 42. 266 + 267 + .. note:: 268 + 269 + If the user has set a conflicting `cpu` key in the userdata dictionary, 270 + both keys will be reported, with the kernel-populated entry appearing after 271 + the user one. For example:: 272 + 273 + # User-defined CPU entry 274 + mkdir -p /sys/kernel/config/netconsole/target1/userdata/cpu 275 + echo "1" > /sys/kernel/config/netconsole/target1/userdata/cpu/value 276 + 277 + Output might look like:: 278 + 279 + 12,607,22085407756,-;This is a message 280 + cpu=1 281 + cpu=42 # kernel-populated value 282 + 242 283 243 284 Extended console: 244 285 =================
+197 -64
drivers/net/netconsole.c
··· 45 45 MODULE_LICENSE("GPL"); 46 46 47 47 #define MAX_PARAM_LENGTH 256 48 - #define MAX_USERDATA_ENTRY_LENGTH 256 49 - #define MAX_USERDATA_VALUE_LENGTH 200 48 + #define MAX_EXTRADATA_ENTRY_LEN 256 49 + #define MAX_EXTRADATA_VALUE_LEN 200 50 50 /* The number 3 comes from userdata entry format characters (' ', '=', '\n') */ 51 - #define MAX_USERDATA_NAME_LENGTH (MAX_USERDATA_ENTRY_LENGTH - \ 52 - MAX_USERDATA_VALUE_LENGTH - 3) 53 - #define MAX_USERDATA_ITEMS 16 51 + #define MAX_EXTRADATA_NAME_LEN (MAX_EXTRADATA_ENTRY_LEN - \ 52 + MAX_EXTRADATA_VALUE_LEN - 3) 53 + #define MAX_EXTRADATA_ITEMS 16 54 54 #define MAX_PRINT_CHUNK 1000 55 55 56 56 static char config[MAX_PARAM_LENGTH]; ··· 97 97 struct u64_stats_sync syncp; 98 98 }; 99 99 100 + /* Features enabled in sysdata. Contrary to userdata, this data is populated by 101 + * the kernel. The fields are designed as bitwise flags, allowing multiple 102 + * features to be set in sysdata_fields. 103 + */ 104 + enum sysdata_feature { 105 + /* Populate the CPU that sends the message */ 106 + CPU_NR = BIT(0), 107 + }; 108 + 100 109 /** 101 110 * struct netconsole_target - Represents a configured netconsole target. 102 111 * @list: Links this target into the target_list. 103 112 * @group: Links us into the configfs subsystem hierarchy. 104 113 * @userdata_group: Links to the userdata configfs hierarchy 105 - * @userdata_complete: Cached, formatted string of append 106 - * @userdata_length: String length of userdata_complete 114 + * @extradata_complete: Cached, formatted string of append 115 + * @userdata_length: String length of usedata in extradata_complete. 116 + * @sysdata_fields: Sysdata features enabled. 107 117 * @stats: Packet send stats for the target. Used for debugging. 108 118 * @enabled: On / off knob to enable / disable target. 109 119 * Visible from userspace (read-write). ··· 133 123 * remote_ip (read-write) 134 124 * local_mac (read-only) 135 125 * remote_mac (read-write) 126 + * @buf: The buffer used to send the full msg to the network stack 136 127 */ 137 128 struct netconsole_target { 138 129 struct list_head list; 139 130 #ifdef CONFIG_NETCONSOLE_DYNAMIC 140 131 struct config_group group; 141 132 struct config_group userdata_group; 142 - char userdata_complete[MAX_USERDATA_ENTRY_LENGTH * MAX_USERDATA_ITEMS]; 133 + char extradata_complete[MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS]; 143 134 size_t userdata_length; 135 + /* bit-wise with sysdata_feature bits */ 136 + u32 sysdata_fields; 144 137 #endif 145 138 struct netconsole_target_stats stats; 146 139 bool enabled; 147 140 bool extended; 148 141 bool release; 149 142 struct netpoll np; 143 + /* protected by target_list_lock */ 144 + char buf[MAX_PRINT_CHUNK]; 150 145 }; 151 146 152 147 #ifdef CONFIG_NETCONSOLE_DYNAMIC ··· 409 394 } while (u64_stats_fetch_retry(&nt->stats.syncp, start)); 410 395 411 396 return sysfs_emit(buf, "%llu\n", xmit_drop_count + enomem_count); 397 + } 398 + 399 + /* configfs helper to display if cpu_nr sysdata feature is enabled */ 400 + static ssize_t sysdata_cpu_nr_enabled_show(struct config_item *item, char *buf) 401 + { 402 + struct netconsole_target *nt = to_target(item->ci_parent); 403 + bool cpu_nr_enabled; 404 + 405 + mutex_lock(&dynamic_netconsole_mutex); 406 + cpu_nr_enabled = !!(nt->sysdata_fields & CPU_NR); 407 + mutex_unlock(&dynamic_netconsole_mutex); 408 + 409 + return sysfs_emit(buf, "%d\n", cpu_nr_enabled); 412 410 } 413 411 414 412 /* ··· 687 659 return ret; 688 660 } 689 661 662 + /* Count number of entries we have in extradata. 663 + * This is important because the extradata_complete only supports 664 + * MAX_EXTRADATA_ITEMS entries. Before enabling any new {user,sys}data 665 + * feature, number of entries needs to checked for available space. 666 + */ 667 + static size_t count_extradata_entries(struct netconsole_target *nt) 668 + { 669 + size_t entries; 670 + 671 + /* Userdata entries */ 672 + entries = list_count_nodes(&nt->userdata_group.cg_children); 673 + /* Plus sysdata entries */ 674 + if (nt->sysdata_fields & CPU_NR) 675 + entries += 1; 676 + 677 + return entries; 678 + } 679 + 690 680 static ssize_t remote_mac_store(struct config_item *item, const char *buf, 691 681 size_t count) 692 682 { ··· 733 687 734 688 struct userdatum { 735 689 struct config_item item; 736 - char value[MAX_USERDATA_VALUE_LENGTH]; 690 + char value[MAX_EXTRADATA_VALUE_LEN]; 737 691 }; 738 692 739 693 static struct userdatum *to_userdatum(struct config_item *item) ··· 770 724 771 725 /* Clear the current string in case the last userdatum was deleted */ 772 726 nt->userdata_length = 0; 773 - nt->userdata_complete[0] = 0; 727 + nt->extradata_complete[0] = 0; 774 728 775 729 list_for_each(entry, &nt->userdata_group.cg_children) { 776 730 struct userdatum *udm_item; 777 731 struct config_item *item; 778 732 779 - if (WARN_ON_ONCE(child_count >= MAX_USERDATA_ITEMS)) 733 + if (WARN_ON_ONCE(child_count >= MAX_EXTRADATA_ITEMS)) 780 734 break; 781 735 child_count++; 782 736 ··· 784 738 udm_item = to_userdatum(item); 785 739 786 740 /* Skip userdata with no value set */ 787 - if (strnlen(udm_item->value, MAX_USERDATA_VALUE_LENGTH) == 0) 741 + if (strnlen(udm_item->value, MAX_EXTRADATA_VALUE_LEN) == 0) 788 742 continue; 789 743 790 - /* This doesn't overflow userdata_complete since it will write 791 - * one entry length (1/MAX_USERDATA_ITEMS long), entry count is 744 + /* This doesn't overflow extradata_complete since it will write 745 + * one entry length (1/MAX_EXTRADATA_ITEMS long), entry count is 792 746 * checked to not exceed MAX items with child_count above 793 747 */ 794 - complete_idx += scnprintf(&nt->userdata_complete[complete_idx], 795 - MAX_USERDATA_ENTRY_LENGTH, " %s=%s\n", 748 + complete_idx += scnprintf(&nt->extradata_complete[complete_idx], 749 + MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n", 796 750 item->ci_name, udm_item->value); 797 751 } 798 - nt->userdata_length = strnlen(nt->userdata_complete, 799 - sizeof(nt->userdata_complete)); 752 + nt->userdata_length = strnlen(nt->extradata_complete, 753 + sizeof(nt->extradata_complete)); 800 754 } 801 755 802 756 static ssize_t userdatum_value_store(struct config_item *item, const char *buf, ··· 807 761 struct userdata *ud; 808 762 ssize_t ret; 809 763 810 - if (count > MAX_USERDATA_VALUE_LENGTH) 764 + if (count > MAX_EXTRADATA_VALUE_LEN) 811 765 return -EMSGSIZE; 812 766 813 767 mutex_lock(&dynamic_netconsole_mutex); ··· 826 780 return ret; 827 781 } 828 782 783 + /* disable_sysdata_feature - Disable sysdata feature and clean sysdata 784 + * @nt: target that is disabling the feature 785 + * @feature: feature being disabled 786 + */ 787 + static void disable_sysdata_feature(struct netconsole_target *nt, 788 + enum sysdata_feature feature) 789 + { 790 + nt->sysdata_fields &= ~feature; 791 + nt->extradata_complete[nt->userdata_length] = 0; 792 + } 793 + 794 + /* configfs helper to sysdata cpu_nr feature */ 795 + static ssize_t sysdata_cpu_nr_enabled_store(struct config_item *item, 796 + const char *buf, size_t count) 797 + { 798 + struct netconsole_target *nt = to_target(item->ci_parent); 799 + bool cpu_nr_enabled, curr; 800 + ssize_t ret; 801 + 802 + ret = kstrtobool(buf, &cpu_nr_enabled); 803 + if (ret) 804 + return ret; 805 + 806 + mutex_lock(&dynamic_netconsole_mutex); 807 + curr = nt->sysdata_fields & CPU_NR; 808 + if (cpu_nr_enabled == curr) 809 + /* no change requested */ 810 + goto unlock_ok; 811 + 812 + if (cpu_nr_enabled && 813 + count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 814 + /* user wants the new feature, but there is no space in the 815 + * buffer. 816 + */ 817 + ret = -ENOSPC; 818 + goto unlock; 819 + } 820 + 821 + if (cpu_nr_enabled) 822 + nt->sysdata_fields |= CPU_NR; 823 + else 824 + /* This is special because extradata_complete might have 825 + * remaining data from previous sysdata, and it needs to be 826 + * cleaned. 827 + */ 828 + disable_sysdata_feature(nt, CPU_NR); 829 + 830 + unlock_ok: 831 + ret = strnlen(buf, count); 832 + unlock: 833 + mutex_unlock(&dynamic_netconsole_mutex); 834 + return ret; 835 + } 836 + 829 837 CONFIGFS_ATTR(userdatum_, value); 838 + CONFIGFS_ATTR(sysdata_, cpu_nr_enabled); 830 839 831 840 static struct configfs_attribute *userdatum_attrs[] = { 832 841 &userdatum_attr_value, ··· 909 808 struct netconsole_target *nt; 910 809 struct userdatum *udm; 911 810 struct userdata *ud; 912 - size_t child_count; 913 811 914 - if (strlen(name) > MAX_USERDATA_NAME_LENGTH) 812 + if (strlen(name) > MAX_EXTRADATA_NAME_LEN) 915 813 return ERR_PTR(-ENAMETOOLONG); 916 814 917 815 ud = to_userdata(&group->cg_item); 918 816 nt = userdata_to_target(ud); 919 - child_count = list_count_nodes(&nt->userdata_group.cg_children); 920 - if (child_count >= MAX_USERDATA_ITEMS) 817 + if (count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) 921 818 return ERR_PTR(-ENOSPC); 922 819 923 820 udm = kzalloc(sizeof(*udm), GFP_KERNEL); ··· 941 842 } 942 843 943 844 static struct configfs_attribute *userdata_attrs[] = { 845 + &sysdata_attr_cpu_nr_enabled, 944 846 NULL, 945 847 }; 946 848 ··· 1117 1017 init_target_config_group(nt, target_name); 1118 1018 } 1119 1019 1020 + /* 1021 + * prepare_extradata - append sysdata at extradata_complete in runtime 1022 + * @nt: target to send message to 1023 + */ 1024 + static int prepare_extradata(struct netconsole_target *nt) 1025 + { 1026 + int sysdata_len, extradata_len; 1027 + 1028 + /* userdata was appended when configfs write helper was called 1029 + * by update_userdata(). 1030 + */ 1031 + extradata_len = nt->userdata_length; 1032 + 1033 + if (!(nt->sysdata_fields & CPU_NR)) 1034 + goto out; 1035 + 1036 + /* Append cpu=%d at extradata_complete after userdata str */ 1037 + sysdata_len = scnprintf(&nt->extradata_complete[nt->userdata_length], 1038 + MAX_EXTRADATA_ENTRY_LEN, " cpu=%u\n", 1039 + raw_smp_processor_id()); 1040 + 1041 + extradata_len += sysdata_len; 1042 + 1043 + WARN_ON_ONCE(extradata_len > 1044 + MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS); 1045 + 1046 + out: 1047 + return extradata_len; 1048 + } 1049 + #else /* CONFIG_NETCONSOLE_DYNAMIC not set */ 1050 + static int prepare_extradata(struct netconsole_target *nt) 1051 + { 1052 + return 0; 1053 + } 1120 1054 #endif /* CONFIG_NETCONSOLE_DYNAMIC */ 1121 1055 1122 1056 /* Handle network interface device notifications */ ··· 1251 1117 int msg_len, 1252 1118 int release_len) 1253 1119 { 1254 - static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */ 1255 - const char *userdata = NULL; 1120 + const char *extradata = NULL; 1256 1121 const char *release; 1257 1122 1258 1123 #ifdef CONFIG_NETCONSOLE_DYNAMIC 1259 - userdata = nt->userdata_complete; 1124 + extradata = nt->extradata_complete; 1260 1125 #endif 1261 1126 1262 1127 if (release_len) { 1263 1128 release = init_utsname()->release; 1264 1129 1265 - scnprintf(buf, MAX_PRINT_CHUNK, "%s,%s", release, msg); 1130 + scnprintf(nt->buf, MAX_PRINT_CHUNK, "%s,%s", release, msg); 1266 1131 msg_len += release_len; 1267 1132 } else { 1268 - memcpy(buf, msg, msg_len); 1133 + memcpy(nt->buf, msg, msg_len); 1269 1134 } 1270 1135 1271 - if (userdata) 1272 - msg_len += scnprintf(&buf[msg_len], 1136 + if (extradata) 1137 + msg_len += scnprintf(&nt->buf[msg_len], 1273 1138 MAX_PRINT_CHUNK - msg_len, 1274 - "%s", userdata); 1139 + "%s", extradata); 1275 1140 1276 - send_udp(nt, buf, msg_len); 1141 + send_udp(nt, nt->buf, msg_len); 1277 1142 } 1278 1143 1279 1144 static void append_release(char *buf) ··· 1283 1150 scnprintf(buf, MAX_PRINT_CHUNK, "%s,", release); 1284 1151 } 1285 1152 1286 - static void send_fragmented_body(struct netconsole_target *nt, char *buf, 1153 + static void send_fragmented_body(struct netconsole_target *nt, 1287 1154 const char *msgbody, int header_len, 1288 - int msgbody_len) 1155 + int msgbody_len, int extradata_len) 1289 1156 { 1290 - const char *userdata = NULL; 1157 + int sent_extradata, preceding_bytes; 1158 + const char *extradata = NULL; 1291 1159 int body_len, offset = 0; 1292 - int userdata_len = 0; 1293 1160 1294 1161 #ifdef CONFIG_NETCONSOLE_DYNAMIC 1295 - userdata = nt->userdata_complete; 1296 - userdata_len = nt->userdata_length; 1162 + extradata = nt->extradata_complete; 1297 1163 #endif 1298 1164 1299 1165 /* body_len represents the number of bytes that will be sent. This is 1300 1166 * bigger than MAX_PRINT_CHUNK, thus, it will be split in multiple 1301 1167 * packets 1302 1168 */ 1303 - body_len = msgbody_len + userdata_len; 1169 + body_len = msgbody_len + extradata_len; 1304 1170 1305 1171 /* In each iteration of the while loop below, we send a packet 1306 1172 * containing the header and a portion of the body. The body is 1307 - * composed of two parts: msgbody and userdata. We keep track of how 1173 + * composed of two parts: msgbody and extradata. We keep track of how 1308 1174 * many bytes have been sent so far using the offset variable, which 1309 1175 * ranges from 0 to the total length of the body. 1310 1176 */ ··· 1313 1181 int this_offset = 0; 1314 1182 int this_chunk = 0; 1315 1183 1316 - this_header += scnprintf(buf + this_header, 1184 + this_header += scnprintf(nt->buf + this_header, 1317 1185 MAX_PRINT_CHUNK - this_header, 1318 1186 ",ncfrag=%d/%d;", offset, 1319 1187 body_len); ··· 1324 1192 MAX_PRINT_CHUNK - this_header); 1325 1193 if (WARN_ON_ONCE(this_chunk <= 0)) 1326 1194 return; 1327 - memcpy(buf + this_header, msgbody + offset, this_chunk); 1195 + memcpy(nt->buf + this_header, msgbody + offset, 1196 + this_chunk); 1328 1197 this_offset += this_chunk; 1329 1198 } 1330 1199 1331 1200 /* msgbody was finally written, either in the previous 1332 1201 * messages and/or in the current buf. Time to write 1333 - * the userdata. 1202 + * the extradata. 1334 1203 */ 1335 1204 msgbody_written |= offset + this_offset >= msgbody_len; 1336 1205 1337 - /* Msg body is fully written and there is pending userdata to 1338 - * write, append userdata in this chunk 1206 + /* Msg body is fully written and there is pending extradata to 1207 + * write, append extradata in this chunk 1339 1208 */ 1340 1209 if (msgbody_written && offset + this_offset < body_len) { 1341 1210 /* Track how much user data was already sent. First 1342 1211 * time here, sent_userdata is zero 1343 1212 */ 1344 - int sent_userdata = (offset + this_offset) - msgbody_len; 1213 + sent_extradata = (offset + this_offset) - msgbody_len; 1345 1214 /* offset of bytes used in current buf */ 1346 - int preceding_bytes = this_chunk + this_header; 1215 + preceding_bytes = this_chunk + this_header; 1347 1216 1348 - if (WARN_ON_ONCE(sent_userdata < 0)) 1217 + if (WARN_ON_ONCE(sent_extradata < 0)) 1349 1218 return; 1350 1219 1351 - this_chunk = min(userdata_len - sent_userdata, 1220 + this_chunk = min(extradata_len - sent_extradata, 1352 1221 MAX_PRINT_CHUNK - preceding_bytes); 1353 1222 if (WARN_ON_ONCE(this_chunk < 0)) 1354 1223 /* this_chunk could be zero if all the previous 1355 1224 * message used all the buffer. This is not a 1356 - * problem, userdata will be sent in the next 1225 + * problem, extradata will be sent in the next 1357 1226 * iteration 1358 1227 */ 1359 1228 return; 1360 1229 1361 - memcpy(buf + this_header + this_offset, 1362 - userdata + sent_userdata, 1230 + memcpy(nt->buf + this_header + this_offset, 1231 + extradata + sent_extradata, 1363 1232 this_chunk); 1364 1233 this_offset += this_chunk; 1365 1234 } 1366 1235 1367 - send_udp(nt, buf, this_header + this_offset); 1236 + send_udp(nt, nt->buf, this_header + this_offset); 1368 1237 offset += this_offset; 1369 1238 } 1370 1239 } ··· 1373 1240 static void send_msg_fragmented(struct netconsole_target *nt, 1374 1241 const char *msg, 1375 1242 int msg_len, 1376 - int release_len) 1243 + int release_len, 1244 + int extradata_len) 1377 1245 { 1378 - static char buf[MAX_PRINT_CHUNK]; /* protected by target_list_lock */ 1379 1246 int header_len, msgbody_len; 1380 1247 const char *msgbody; 1381 1248 ··· 1393 1260 * "ncfrag=<byte-offset>/<total-bytes>" 1394 1261 */ 1395 1262 if (release_len) 1396 - append_release(buf); 1263 + append_release(nt->buf); 1397 1264 1398 1265 /* Copy the header into the buffer */ 1399 - memcpy(buf + release_len, msg, header_len); 1266 + memcpy(nt->buf + release_len, msg, header_len); 1400 1267 header_len += release_len; 1401 1268 1402 1269 /* for now on, the header will be persisted, and the msgbody 1403 1270 * will be replaced 1404 1271 */ 1405 - send_fragmented_body(nt, buf, msgbody, header_len, msgbody_len); 1272 + send_fragmented_body(nt, msgbody, header_len, msgbody_len, 1273 + extradata_len); 1406 1274 } 1407 1275 1408 1276 /** ··· 1419 1285 static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, 1420 1286 int msg_len) 1421 1287 { 1422 - int userdata_len = 0; 1423 1288 int release_len = 0; 1289 + int extradata_len; 1424 1290 1425 - #ifdef CONFIG_NETCONSOLE_DYNAMIC 1426 - userdata_len = nt->userdata_length; 1427 - #endif 1291 + extradata_len = prepare_extradata(nt); 1428 1292 1429 1293 if (nt->release) 1430 1294 release_len = strlen(init_utsname()->release) + 1; 1431 1295 1432 - if (msg_len + release_len + userdata_len <= MAX_PRINT_CHUNK) 1296 + if (msg_len + release_len + extradata_len <= MAX_PRINT_CHUNK) 1433 1297 return send_msg_no_fragmentation(nt, msg, msg_len, release_len); 1434 1298 1435 - return send_msg_fragmented(nt, msg, msg_len, release_len); 1299 + return send_msg_fragmented(nt, msg, msg_len, release_len, 1300 + extradata_len); 1436 1301 } 1437 1302 1438 1303 static void write_ext_msg(struct console *con, const char *msg,
+1
tools/testing/selftests/drivers/net/Makefile
··· 9 9 netcons_basic.sh \ 10 10 netcons_fragmented_msg.sh \ 11 11 netcons_overflow.sh \ 12 + netcons_sysdata.sh \ 12 13 ping.py \ 13 14 queues.py \ 14 15 stats.py \
+17
tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh
··· 230 230 exit "${ksft_skip}" 231 231 fi 232 232 } 233 + 234 + function check_for_taskset() { 235 + if ! which taskset > /dev/null ; then 236 + echo "SKIP: taskset(1) is not available" >&2 237 + exit "${ksft_skip}" 238 + fi 239 + } 240 + 241 + # This is necessary if running multiple tests in a row 242 + function pkill_socat() { 243 + PROCESS_NAME="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}" 244 + # socat runs under timeout(1), kill it if it is still alive 245 + # do not fail if socat doesn't exist anymore 246 + set +e 247 + pkill -f "${PROCESS_NAME}" 248 + set -e 249 + }
+167
tools/testing/selftests/drivers/net/netcons_sysdata.sh
··· 1 + #!/usr/bin/env bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + # A test that makes sure that sysdata runtime CPU data is properly set 5 + # when a message is sent. 6 + # 7 + # There are 3 different tests, every time sent using a random CPU. 8 + # - Test #1 9 + # * Only enable cpu_nr sysdata feature. 10 + # - Test #2 11 + # * Keep cpu_nr sysdata feature enable and enable userdata. 12 + # - Test #3 13 + # * keep userdata enabled, and disable sysdata cpu_nr feature. 14 + # 15 + # Author: Breno Leitao <leitao@debian.org> 16 + 17 + set -euo pipefail 18 + 19 + SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") 20 + 21 + source "${SCRIPTDIR}"/lib/sh/lib_netcons.sh 22 + 23 + # Enable the sysdata cpu_nr feature 24 + function set_cpu_nr() { 25 + if [[ ! -f "${NETCONS_PATH}/userdata/cpu_nr_enabled" ]] 26 + then 27 + echo "Populate CPU configfs path not available in ${NETCONS_PATH}/userdata/cpu_nr_enabled" >&2 28 + exit "${ksft_skip}" 29 + fi 30 + 31 + echo 1 > "${NETCONS_PATH}/userdata/cpu_nr_enabled" 32 + } 33 + 34 + # Disable the sysdata cpu_nr feature 35 + function unset_cpu_nr() { 36 + echo 0 > "${NETCONS_PATH}/userdata/cpu_nr_enabled" 37 + } 38 + 39 + # Test if MSG content and `cpu=${CPU}` exists in OUTPUT_FILE 40 + function validate_sysdata_cpu_exists() { 41 + # OUTPUT_FILE will contain something like: 42 + # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM 43 + # userdatakey=userdatavalue 44 + # cpu=X 45 + 46 + if [ ! -f "$OUTPUT_FILE" ]; then 47 + echo "FAIL: File was not generated." >&2 48 + exit "${ksft_fail}" 49 + fi 50 + 51 + if ! grep -q "${MSG}" "${OUTPUT_FILE}"; then 52 + echo "FAIL: ${MSG} not found in ${OUTPUT_FILE}" >&2 53 + cat "${OUTPUT_FILE}" >&2 54 + exit "${ksft_fail}" 55 + fi 56 + 57 + # Check if cpu=XX exists in the file and matches the one used 58 + # in taskset(1) 59 + if ! grep -q "cpu=${CPU}\+" "${OUTPUT_FILE}"; then 60 + echo "FAIL: 'cpu=${CPU}' not found in ${OUTPUT_FILE}" >&2 61 + cat "${OUTPUT_FILE}" >&2 62 + exit "${ksft_fail}" 63 + fi 64 + 65 + rm "${OUTPUT_FILE}" 66 + pkill_socat 67 + } 68 + 69 + # Test if MSG content exists in OUTPUT_FILE but no `cpu=` string 70 + function validate_sysdata_no_cpu() { 71 + if [ ! -f "$OUTPUT_FILE" ]; then 72 + echo "FAIL: File was not generated." >&2 73 + exit "${ksft_fail}" 74 + fi 75 + 76 + if ! grep -q "${MSG}" "${OUTPUT_FILE}"; then 77 + echo "FAIL: ${MSG} not found in ${OUTPUT_FILE}" >&2 78 + cat "${OUTPUT_FILE}" >&2 79 + exit "${ksft_fail}" 80 + fi 81 + 82 + if grep -q "cpu=" "${OUTPUT_FILE}"; then 83 + echo "FAIL: 'cpu= found in ${OUTPUT_FILE}" >&2 84 + cat "${OUTPUT_FILE}" >&2 85 + exit "${ksft_fail}" 86 + fi 87 + 88 + rm "${OUTPUT_FILE}" 89 + } 90 + 91 + # Start socat, send the message and wait for the file to show up in the file 92 + # system 93 + function runtest { 94 + # Listen for netconsole port inside the namespace and destination 95 + # interface 96 + listen_port_and_save_to "${OUTPUT_FILE}" & 97 + # Wait for socat to start and listen to the port. 98 + wait_local_port_listen "${NAMESPACE}" "${PORT}" udp 99 + # Send the message 100 + taskset -c "${CPU}" echo "${MSG}: ${TARGET}" > /dev/kmsg 101 + # Wait until socat saves the file to disk 102 + busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" 103 + } 104 + 105 + # ========== # 106 + # Start here # 107 + # ========== # 108 + 109 + modprobe netdevsim 2> /dev/null || true 110 + modprobe netconsole 2> /dev/null || true 111 + 112 + # Check for basic system dependency and exit if not found 113 + check_for_dependencies 114 + # This test also depends on taskset(1). Check for it before starting the test 115 + check_for_taskset 116 + 117 + # Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5) 118 + echo "6 5" > /proc/sys/kernel/printk 119 + # Remove the namespace, interfaces and netconsole target on exit 120 + trap cleanup EXIT 121 + # Create one namespace and two interfaces 122 + set_network 123 + # Create a dynamic target for netconsole 124 + create_dynamic_target 125 + 126 + #==================================================== 127 + # TEST #1 128 + # Send message from a random CPU 129 + #==================================================== 130 + # Random CPU in the system 131 + CPU=$((RANDOM % $(nproc))) 132 + OUTPUT_FILE="/tmp/${TARGET}_1" 133 + MSG="Test #1 from CPU${CPU}" 134 + # Enable the auto population of cpu_nr 135 + set_cpu_nr 136 + runtest 137 + # Make sure the message was received in the dst part 138 + # and exit 139 + validate_sysdata_cpu_exists 140 + 141 + #==================================================== 142 + # TEST #2 143 + # This test now adds userdata together with sysdata 144 + # =================================================== 145 + # Get a new random CPU 146 + CPU=$((RANDOM % $(nproc))) 147 + OUTPUT_FILE="/tmp/${TARGET}_2" 148 + MSG="Test #2 from CPU${CPU}" 149 + set_user_data 150 + runtest 151 + validate_sysdata_cpu_exists 152 + 153 + # =================================================== 154 + # TEST #3 155 + # Unset cpu_nr, so, no CPU should be appended. 156 + # userdata is still set 157 + # =================================================== 158 + CPU=$((RANDOM % $(nproc))) 159 + OUTPUT_FILE="/tmp/${TARGET}_3" 160 + MSG="Test #3 from CPU${CPU}" 161 + # Enable the auto population of cpu_nr 162 + unset_cpu_nr 163 + runtest 164 + # At this time, cpu= shouldn't be present in the msg 165 + validate_sysdata_no_cpu 166 + 167 + exit "${ksft_pass}"