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.

netconsole: Split userdata and sysdata

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@gmail.com>
Reviewed-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20251119-netconsole_dynamic_extradata-v3-2-497ac3191707@meta.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Gustavo Luiz Duarte and committed by
Jakub Kicinski
9dc10f50 7279b718

+98 -117
+98 -117
drivers/net/netconsole.c
··· 50 50 /* The number 3 comes from userdata entry format characters (' ', '=', '\n') */ 51 51 #define MAX_EXTRADATA_NAME_LEN (MAX_EXTRADATA_ENTRY_LEN - \ 52 52 MAX_EXTRADATA_VALUE_LEN - 3) 53 - #define MAX_EXTRADATA_ITEMS 16 53 + #define MAX_USERDATA_ITEMS 16 54 54 #define MAX_PRINT_CHUNK 1000 55 55 56 56 static char config[MAX_PARAM_LENGTH]; ··· 115 115 SYSDATA_RELEASE = BIT(2), 116 116 /* Include a per-target message ID as part of sysdata */ 117 117 SYSDATA_MSGID = BIT(3), 118 + /* Sentinel: highest bit position */ 119 + MAX_SYSDATA_ITEMS = 4, 118 120 }; 119 121 120 122 /** ··· 124 122 * @list: Links this target into the target_list. 125 123 * @group: Links us into the configfs subsystem hierarchy. 126 124 * @userdata_group: Links to the userdata configfs hierarchy 127 - * @extradata_complete: Cached, formatted string of append 128 - * @userdata_length: String length of usedata in extradata_complete. 125 + * @userdata: Cached, formatted string of append 126 + * @userdata_length: String length of userdata. 127 + * @sysdata: Cached, formatted string of append 129 128 * @sysdata_fields: Sysdata features enabled. 130 129 * @msgcounter: Message sent counter. 131 130 * @stats: Packet send stats for the target. Used for debugging. ··· 155 152 #ifdef CONFIG_NETCONSOLE_DYNAMIC 156 153 struct config_group group; 157 154 struct config_group userdata_group; 158 - char extradata_complete[MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS]; 155 + char userdata[MAX_EXTRADATA_ENTRY_LEN * MAX_USERDATA_ITEMS]; 159 156 size_t userdata_length; 157 + char sysdata[MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS]; 158 + 160 159 /* bit-wise with sysdata_feature bits */ 161 160 u32 sysdata_fields; 162 161 /* protected by target_list_lock */ ··· 807 802 return ret; 808 803 } 809 804 810 - /* Count number of entries we have in extradata. 811 - * This is important because the extradata_complete only supports 812 - * MAX_EXTRADATA_ITEMS entries. Before enabling any new {user,sys}data 813 - * feature, number of entries needs to checked for available space. 805 + /* Count number of entries we have in userdata. 806 + * This is important because userdata only supports MAX_USERDATA_ITEMS 807 + * entries. Before enabling any new userdata feature, number of entries needs 808 + * to checked for available space. 814 809 */ 815 - static size_t count_extradata_entries(struct netconsole_target *nt) 810 + static size_t count_userdata_entries(struct netconsole_target *nt) 816 811 { 817 - size_t entries; 818 - 819 - /* Userdata entries */ 820 - entries = list_count_nodes(&nt->userdata_group.cg_children); 821 - /* Plus sysdata entries */ 822 - if (nt->sysdata_fields & SYSDATA_CPU_NR) 823 - entries += 1; 824 - if (nt->sysdata_fields & SYSDATA_TASKNAME) 825 - entries += 1; 826 - if (nt->sysdata_fields & SYSDATA_RELEASE) 827 - entries += 1; 828 - if (nt->sysdata_fields & SYSDATA_MSGID) 829 - entries += 1; 830 - 831 - return entries; 812 + return list_count_nodes(&nt->userdata_group.cg_children); 832 813 } 833 814 834 815 static ssize_t remote_mac_store(struct config_item *item, const char *buf, ··· 885 894 886 895 /* Clear the current string in case the last userdatum was deleted */ 887 896 nt->userdata_length = 0; 888 - nt->extradata_complete[0] = 0; 897 + nt->userdata[0] = 0; 889 898 890 899 list_for_each(entry, &nt->userdata_group.cg_children) { 891 900 struct userdatum *udm_item; 892 901 struct config_item *item; 893 902 894 - if (child_count >= MAX_EXTRADATA_ITEMS) { 903 + if (child_count >= MAX_USERDATA_ITEMS) { 895 904 spin_unlock_irqrestore(&target_list_lock, flags); 896 905 WARN_ON_ONCE(1); 897 906 return; ··· 905 914 if (strnlen(udm_item->value, MAX_EXTRADATA_VALUE_LEN) == 0) 906 915 continue; 907 916 908 - /* This doesn't overflow extradata_complete since it will write 909 - * one entry length (1/MAX_EXTRADATA_ITEMS long), entry count is 917 + /* This doesn't overflow userdata since it will write 918 + * one entry length (1/MAX_USERDATA_ITEMS long), entry count is 910 919 * checked to not exceed MAX items with child_count above 911 920 */ 912 - nt->userdata_length += scnprintf(&nt->extradata_complete[nt->userdata_length], 921 + nt->userdata_length += scnprintf(&nt->userdata[nt->userdata_length], 913 922 MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n", 914 923 item->ci_name, udm_item->value); 915 924 } ··· 953 962 enum sysdata_feature feature) 954 963 { 955 964 nt->sysdata_fields &= ~feature; 956 - nt->extradata_complete[nt->userdata_length] = 0; 965 + nt->sysdata[0] = 0; 957 966 } 958 967 959 968 static ssize_t sysdata_msgid_enabled_store(struct config_item *item, ··· 973 982 if (msgid_enabled == curr) 974 983 goto unlock_ok; 975 984 976 - if (msgid_enabled && 977 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 978 - ret = -ENOSPC; 979 - goto unlock; 980 - } 981 - 982 985 if (msgid_enabled) 983 986 nt->sysdata_fields |= SYSDATA_MSGID; 984 987 else ··· 980 995 981 996 unlock_ok: 982 997 ret = strnlen(buf, count); 983 - unlock: 984 998 mutex_unlock(&dynamic_netconsole_mutex); 985 999 mutex_unlock(&netconsole_subsys.su_mutex); 986 1000 return ret; ··· 1002 1018 if (release_enabled == curr) 1003 1019 goto unlock_ok; 1004 1020 1005 - if (release_enabled && 1006 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1007 - ret = -ENOSPC; 1008 - goto unlock; 1009 - } 1010 - 1011 1021 if (release_enabled) 1012 1022 nt->sysdata_fields |= SYSDATA_RELEASE; 1013 1023 else ··· 1009 1031 1010 1032 unlock_ok: 1011 1033 ret = strnlen(buf, count); 1012 - unlock: 1013 1034 mutex_unlock(&dynamic_netconsole_mutex); 1014 1035 mutex_unlock(&netconsole_subsys.su_mutex); 1015 1036 return ret; ··· 1031 1054 if (taskname_enabled == curr) 1032 1055 goto unlock_ok; 1033 1056 1034 - if (taskname_enabled && 1035 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1036 - ret = -ENOSPC; 1037 - goto unlock; 1038 - } 1039 - 1040 1057 if (taskname_enabled) 1041 1058 nt->sysdata_fields |= SYSDATA_TASKNAME; 1042 1059 else ··· 1038 1067 1039 1068 unlock_ok: 1040 1069 ret = strnlen(buf, count); 1041 - unlock: 1042 1070 mutex_unlock(&dynamic_netconsole_mutex); 1043 1071 mutex_unlock(&netconsole_subsys.su_mutex); 1044 1072 return ret; ··· 1062 1092 /* no change requested */ 1063 1093 goto unlock_ok; 1064 1094 1065 - if (cpu_nr_enabled && 1066 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1067 - /* user wants the new feature, but there is no space in the 1068 - * buffer. 1069 - */ 1070 - ret = -ENOSPC; 1071 - goto unlock; 1072 - } 1073 - 1074 1095 if (cpu_nr_enabled) 1075 1096 nt->sysdata_fields |= SYSDATA_CPU_NR; 1076 1097 else 1077 - /* This is special because extradata_complete might have 1078 - * remaining data from previous sysdata, and it needs to be 1079 - * cleaned. 1098 + /* This is special because sysdata might have remaining data 1099 + * from previous sysdata, and it needs to be cleaned. 1080 1100 */ 1081 1101 disable_sysdata_feature(nt, SYSDATA_CPU_NR); 1082 1102 1083 1103 unlock_ok: 1084 1104 ret = strnlen(buf, count); 1085 - unlock: 1086 1105 mutex_unlock(&dynamic_netconsole_mutex); 1087 1106 mutex_unlock(&netconsole_subsys.su_mutex); 1088 1107 return ret; ··· 1115 1156 1116 1157 ud = to_userdata(&group->cg_item); 1117 1158 nt = userdata_to_target(ud); 1118 - if (count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) 1159 + if (count_userdata_entries(nt) >= MAX_USERDATA_ITEMS) 1119 1160 return ERR_PTR(-ENOSPC); 1120 1161 1121 1162 udm = kzalloc(sizeof(*udm), GFP_KERNEL); ··· 1322 1363 1323 1364 static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset) 1324 1365 { 1325 - /* Append cpu=%d at extradata_complete after userdata str */ 1326 - return scnprintf(&nt->extradata_complete[offset], 1366 + return scnprintf(&nt->sysdata[offset], 1327 1367 MAX_EXTRADATA_ENTRY_LEN, " cpu=%u\n", 1328 1368 raw_smp_processor_id()); 1329 1369 } 1330 1370 1331 1371 static int sysdata_append_taskname(struct netconsole_target *nt, int offset) 1332 1372 { 1333 - return scnprintf(&nt->extradata_complete[offset], 1373 + return scnprintf(&nt->sysdata[offset], 1334 1374 MAX_EXTRADATA_ENTRY_LEN, " taskname=%s\n", 1335 1375 current->comm); 1336 1376 } 1337 1377 1338 1378 static int sysdata_append_release(struct netconsole_target *nt, int offset) 1339 1379 { 1340 - return scnprintf(&nt->extradata_complete[offset], 1380 + return scnprintf(&nt->sysdata[offset], 1341 1381 MAX_EXTRADATA_ENTRY_LEN, " release=%s\n", 1342 1382 init_utsname()->release); 1343 1383 } ··· 1344 1386 static int sysdata_append_msgid(struct netconsole_target *nt, int offset) 1345 1387 { 1346 1388 wrapping_assign_add(nt->msgcounter, 1); 1347 - return scnprintf(&nt->extradata_complete[offset], 1389 + return scnprintf(&nt->sysdata[offset], 1348 1390 MAX_EXTRADATA_ENTRY_LEN, " msgid=%u\n", 1349 1391 nt->msgcounter); 1350 1392 } 1351 1393 1352 1394 /* 1353 - * prepare_extradata - append sysdata at extradata_complete in runtime 1395 + * prepare_sysdata - append sysdata in runtime 1354 1396 * @nt: target to send message to 1355 1397 */ 1356 - static int prepare_extradata(struct netconsole_target *nt) 1398 + static int prepare_sysdata(struct netconsole_target *nt) 1357 1399 { 1358 - int extradata_len; 1359 - 1360 - /* userdata was appended when configfs write helper was called 1361 - * by update_userdata(). 1362 - */ 1363 - extradata_len = nt->userdata_length; 1400 + int sysdata_len = 0; 1364 1401 1365 1402 if (!nt->sysdata_fields) 1366 1403 goto out; 1367 1404 1368 1405 if (nt->sysdata_fields & SYSDATA_CPU_NR) 1369 - extradata_len += sysdata_append_cpu_nr(nt, extradata_len); 1406 + sysdata_len += sysdata_append_cpu_nr(nt, sysdata_len); 1370 1407 if (nt->sysdata_fields & SYSDATA_TASKNAME) 1371 - extradata_len += sysdata_append_taskname(nt, extradata_len); 1408 + sysdata_len += sysdata_append_taskname(nt, sysdata_len); 1372 1409 if (nt->sysdata_fields & SYSDATA_RELEASE) 1373 - extradata_len += sysdata_append_release(nt, extradata_len); 1410 + sysdata_len += sysdata_append_release(nt, sysdata_len); 1374 1411 if (nt->sysdata_fields & SYSDATA_MSGID) 1375 - extradata_len += sysdata_append_msgid(nt, extradata_len); 1412 + sysdata_len += sysdata_append_msgid(nt, sysdata_len); 1376 1413 1377 - WARN_ON_ONCE(extradata_len > 1378 - MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS); 1414 + WARN_ON_ONCE(sysdata_len > 1415 + MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS); 1379 1416 1380 1417 out: 1381 - return extradata_len; 1382 - } 1383 - #else /* CONFIG_NETCONSOLE_DYNAMIC not set */ 1384 - static int prepare_extradata(struct netconsole_target *nt) 1385 - { 1386 - return 0; 1418 + return sysdata_len; 1387 1419 } 1388 1420 #endif /* CONFIG_NETCONSOLE_DYNAMIC */ 1389 1421 ··· 1475 1527 int msg_len, 1476 1528 int release_len) 1477 1529 { 1478 - const char *extradata = NULL; 1530 + const char *userdata = NULL; 1531 + const char *sysdata = NULL; 1479 1532 const char *release; 1480 1533 1481 1534 #ifdef CONFIG_NETCONSOLE_DYNAMIC 1482 - extradata = nt->extradata_complete; 1535 + userdata = nt->userdata; 1536 + sysdata = nt->sysdata; 1483 1537 #endif 1484 1538 1485 1539 if (release_len) { ··· 1493 1543 memcpy(nt->buf, msg, msg_len); 1494 1544 } 1495 1545 1496 - if (extradata) 1546 + if (userdata) 1497 1547 msg_len += scnprintf(&nt->buf[msg_len], 1498 - MAX_PRINT_CHUNK - msg_len, 1499 - "%s", extradata); 1548 + MAX_PRINT_CHUNK - msg_len, "%s", 1549 + userdata); 1550 + 1551 + if (sysdata) 1552 + msg_len += scnprintf(&nt->buf[msg_len], 1553 + MAX_PRINT_CHUNK - msg_len, "%s", 1554 + sysdata); 1500 1555 1501 1556 send_udp(nt, nt->buf, msg_len); 1502 1557 } ··· 1516 1561 1517 1562 static void send_fragmented_body(struct netconsole_target *nt, 1518 1563 const char *msgbody_ptr, int header_len, 1519 - int msgbody_len, int extradata_len) 1564 + int msgbody_len, int sysdata_len) 1520 1565 { 1521 - const char *extradata_ptr = NULL; 1566 + const char *userdata_ptr = NULL; 1567 + const char *sysdata_ptr = NULL; 1522 1568 int data_len, data_sent = 0; 1523 - int extradata_offset = 0; 1569 + int userdata_offset = 0; 1570 + int sysdata_offset = 0; 1524 1571 int msgbody_offset = 0; 1572 + int userdata_len = 0; 1525 1573 1526 1574 #ifdef CONFIG_NETCONSOLE_DYNAMIC 1527 - extradata_ptr = nt->extradata_complete; 1575 + userdata_ptr = nt->userdata; 1576 + sysdata_ptr = nt->sysdata; 1577 + userdata_len = nt->userdata_length; 1528 1578 #endif 1529 - if (WARN_ON_ONCE(!extradata_ptr && extradata_len != 0)) 1579 + if (WARN_ON_ONCE(!userdata_ptr && userdata_len != 0)) 1580 + return; 1581 + 1582 + if (WARN_ON_ONCE(!sysdata_ptr && sysdata_len != 0)) 1530 1583 return; 1531 1584 1532 1585 /* data_len represents the number of bytes that will be sent. This is 1533 1586 * bigger than MAX_PRINT_CHUNK, thus, it will be split in multiple 1534 1587 * packets 1535 1588 */ 1536 - data_len = msgbody_len + extradata_len; 1589 + data_len = msgbody_len + userdata_len + sysdata_len; 1537 1590 1538 1591 /* In each iteration of the while loop below, we send a packet 1539 1592 * containing the header and a portion of the data. The data is 1540 - * composed of two parts: msgbody and extradata. We keep track of how 1541 - * many bytes have been sent so far using the data_sent variable, which 1542 - * ranges from 0 to the total bytes to be sent. 1593 + * composed of three parts: msgbody, userdata, and sysdata. 1594 + * We keep track of how many bytes have been sent from each part using 1595 + * the *_offset variables. 1596 + * We keep track of how many bytes have been sent overall using the 1597 + * data_sent variable, which ranges from 0 to the total bytes to be 1598 + * sent. 1543 1599 */ 1544 1600 while (data_sent < data_len) { 1545 - int extradata_left = extradata_len - extradata_offset; 1601 + int userdata_left = userdata_len - userdata_offset; 1602 + int sysdata_left = sysdata_len - sysdata_offset; 1546 1603 int msgbody_left = msgbody_len - msgbody_offset; 1547 1604 int buf_offset = 0; 1548 1605 int this_chunk = 0; ··· 1575 1608 buf_offset += this_chunk; 1576 1609 data_sent += this_chunk; 1577 1610 1578 - /* after msgbody, append extradata */ 1579 - if (extradata_ptr && extradata_left) { 1580 - this_chunk = min(extradata_left, 1611 + /* after msgbody, append userdata */ 1612 + if (userdata_ptr && userdata_left) { 1613 + this_chunk = min(userdata_left, 1581 1614 MAX_PRINT_CHUNK - buf_offset); 1582 1615 memcpy(nt->buf + buf_offset, 1583 - extradata_ptr + extradata_offset, this_chunk); 1584 - extradata_offset += this_chunk; 1616 + userdata_ptr + userdata_offset, this_chunk); 1617 + userdata_offset += this_chunk; 1618 + buf_offset += this_chunk; 1619 + data_sent += this_chunk; 1620 + } 1621 + 1622 + /* after userdata, append sysdata */ 1623 + if (sysdata_ptr && sysdata_left) { 1624 + this_chunk = min(sysdata_left, 1625 + MAX_PRINT_CHUNK - buf_offset); 1626 + memcpy(nt->buf + buf_offset, 1627 + sysdata_ptr + sysdata_offset, this_chunk); 1628 + sysdata_offset += this_chunk; 1585 1629 buf_offset += this_chunk; 1586 1630 data_sent += this_chunk; 1587 1631 } ··· 1609 1631 const char *msg, 1610 1632 int msg_len, 1611 1633 int release_len, 1612 - int extradata_len) 1634 + int sysdata_len) 1613 1635 { 1614 1636 int header_len, msgbody_len; 1615 1637 const char *msgbody; ··· 1638 1660 * will be replaced 1639 1661 */ 1640 1662 send_fragmented_body(nt, msgbody, header_len, msgbody_len, 1641 - extradata_len); 1663 + sysdata_len); 1642 1664 } 1643 1665 1644 1666 /** ··· 1654 1676 static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, 1655 1677 int msg_len) 1656 1678 { 1679 + int userdata_len = 0; 1657 1680 int release_len = 0; 1658 - int extradata_len; 1681 + int sysdata_len = 0; 1659 1682 1660 - extradata_len = prepare_extradata(nt); 1661 - 1683 + #ifdef CONFIG_NETCONSOLE_DYNAMIC 1684 + sysdata_len = prepare_sysdata(nt); 1685 + userdata_len = nt->userdata_length; 1686 + #endif 1662 1687 if (nt->release) 1663 1688 release_len = strlen(init_utsname()->release) + 1; 1664 1689 1665 - if (msg_len + release_len + extradata_len <= MAX_PRINT_CHUNK) 1690 + if (msg_len + release_len + sysdata_len + userdata_len <= MAX_PRINT_CHUNK) 1666 1691 return send_msg_no_fragmentation(nt, msg, msg_len, release_len); 1667 1692 1668 1693 return send_msg_fragmented(nt, msg, msg_len, release_len, 1669 - extradata_len); 1694 + sysdata_len); 1670 1695 } 1671 1696 1672 1697 static void write_ext_msg(struct console *con, const char *msg,