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

Merge branch 'netconsole-allow-userdata-buffer-to-grow-dynamically'

Gustavo Luiz Duarte says:

====================
netconsole: Allow userdata buffer to grow dynamically

The current netconsole implementation allocates a static buffer for
extradata (userdata + sysdata) with a fixed size of
MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS bytes for every target,
regardless of whether userspace actually uses this feature. This forces
us to keep MAX_EXTRADATA_ITEMS small (16), which is restrictive for
users who need to attach more metadata to their log messages.

This patch series enables dynamic allocation of the userdata buffer,
allowing it to grow on-demand based on actual usage. The series:

1. Refactors send_fragmented_body() to simplify handling of separated
userdata and sysdata (patch 1/4)
2. Splits userdata and sysdata into separate buffers (patch 2/4)
3. Implements dynamic allocation for the userdata buffer (patch 3/4)
4. Increases MAX_USERDATA_ITEMS from 16 to 256 now that we can do so
without memory waste (patch 4/4)

Benefits:
- No memory waste when userdata is not used
- Targets that use userdata only consume what they need
- Users can attach significantly more metadata without impacting systems
that don't use this feature
====================

Link: https://patch.msgid.link/20251119-netconsole_dynamic_extradata-v3-0-497ac3191707@meta.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+197 -195
+196 -194
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 256 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; 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, ··· 875 884 return sysfs_emit(buf, "%s\n", &(to_userdatum(item)->value[0])); 876 885 } 877 886 878 - static void update_userdata(struct netconsole_target *nt) 887 + /* Navigate configfs and calculate the lentgh of the formatted string 888 + * representing userdata. 889 + * Must be called holding netconsole_subsys.su_mutex 890 + */ 891 + static int calc_userdata_len(struct netconsole_target *nt) 879 892 { 893 + struct userdatum *udm_item; 894 + struct config_item *item; 880 895 struct list_head *entry; 881 - int child_count = 0; 882 - unsigned long flags; 883 - 884 - spin_lock_irqsave(&target_list_lock, flags); 885 - 886 - /* Clear the current string in case the last userdatum was deleted */ 887 - nt->userdata_length = 0; 888 - nt->extradata_complete[0] = 0; 896 + int len = 0; 889 897 890 898 list_for_each(entry, &nt->userdata_group.cg_children) { 891 - struct userdatum *udm_item; 892 - struct config_item *item; 893 - 894 - if (child_count >= MAX_EXTRADATA_ITEMS) { 895 - spin_unlock_irqrestore(&target_list_lock, flags); 896 - WARN_ON_ONCE(1); 897 - return; 898 - } 899 - child_count++; 900 - 901 899 item = container_of(entry, struct config_item, ci_entry); 902 900 udm_item = to_userdatum(item); 903 - 904 901 /* Skip userdata with no value set */ 905 - if (strnlen(udm_item->value, MAX_EXTRADATA_VALUE_LEN) == 0) 906 - continue; 907 - 908 - /* This doesn't overflow extradata_complete since it will write 909 - * one entry length (1/MAX_EXTRADATA_ITEMS long), entry count is 910 - * checked to not exceed MAX items with child_count above 911 - */ 912 - nt->userdata_length += scnprintf(&nt->extradata_complete[nt->userdata_length], 913 - MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n", 914 - item->ci_name, udm_item->value); 902 + if (udm_item->value[0]) { 903 + len += snprintf(NULL, 0, " %s=%s\n", item->ci_name, 904 + udm_item->value); 905 + } 915 906 } 907 + return len; 908 + } 909 + 910 + static int update_userdata(struct netconsole_target *nt) 911 + { 912 + struct userdatum *udm_item; 913 + struct config_item *item; 914 + struct list_head *entry; 915 + char *old_buf = NULL; 916 + char *new_buf = NULL; 917 + unsigned long flags; 918 + int offset = 0; 919 + int len; 920 + 921 + /* Calculate required buffer size */ 922 + len = calc_userdata_len(nt); 923 + 924 + if (WARN_ON_ONCE(len > MAX_EXTRADATA_ENTRY_LEN * MAX_USERDATA_ITEMS)) 925 + return -ENOSPC; 926 + 927 + /* Allocate new buffer */ 928 + if (len) { 929 + new_buf = kmalloc(len + 1, GFP_KERNEL); 930 + if (!new_buf) 931 + return -ENOMEM; 932 + } 933 + 934 + /* Write userdata to new buffer */ 935 + list_for_each(entry, &nt->userdata_group.cg_children) { 936 + item = container_of(entry, struct config_item, ci_entry); 937 + udm_item = to_userdatum(item); 938 + /* Skip userdata with no value set */ 939 + if (udm_item->value[0]) { 940 + offset += scnprintf(&new_buf[offset], len + 1 - offset, 941 + " %s=%s\n", item->ci_name, 942 + udm_item->value); 943 + } 944 + } 945 + 946 + WARN_ON_ONCE(offset != len); 947 + 948 + /* Switch to new buffer and free old buffer */ 949 + spin_lock_irqsave(&target_list_lock, flags); 950 + old_buf = nt->userdata; 951 + nt->userdata = new_buf; 952 + nt->userdata_length = offset; 916 953 spin_unlock_irqrestore(&target_list_lock, flags); 954 + 955 + kfree(old_buf); 956 + 957 + return 0; 917 958 } 918 959 919 960 static ssize_t userdatum_value_store(struct config_item *item, const char *buf, ··· 969 946 970 947 ud = to_userdata(item->ci_parent); 971 948 nt = userdata_to_target(ud); 972 - update_userdata(nt); 949 + ret = update_userdata(nt); 950 + if (ret < 0) 951 + goto out_unlock; 973 952 ret = count; 974 953 out_unlock: 975 954 mutex_unlock(&dynamic_netconsole_mutex); ··· 987 962 enum sysdata_feature feature) 988 963 { 989 964 nt->sysdata_fields &= ~feature; 990 - nt->extradata_complete[nt->userdata_length] = 0; 965 + nt->sysdata[0] = 0; 991 966 } 992 967 993 968 static ssize_t sysdata_msgid_enabled_store(struct config_item *item, ··· 1007 982 if (msgid_enabled == curr) 1008 983 goto unlock_ok; 1009 984 1010 - if (msgid_enabled && 1011 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1012 - ret = -ENOSPC; 1013 - goto unlock; 1014 - } 1015 - 1016 985 if (msgid_enabled) 1017 986 nt->sysdata_fields |= SYSDATA_MSGID; 1018 987 else ··· 1014 995 1015 996 unlock_ok: 1016 997 ret = strnlen(buf, count); 1017 - unlock: 1018 998 mutex_unlock(&dynamic_netconsole_mutex); 1019 999 mutex_unlock(&netconsole_subsys.su_mutex); 1020 1000 return ret; ··· 1036 1018 if (release_enabled == curr) 1037 1019 goto unlock_ok; 1038 1020 1039 - if (release_enabled && 1040 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1041 - ret = -ENOSPC; 1042 - goto unlock; 1043 - } 1044 - 1045 1021 if (release_enabled) 1046 1022 nt->sysdata_fields |= SYSDATA_RELEASE; 1047 1023 else ··· 1043 1031 1044 1032 unlock_ok: 1045 1033 ret = strnlen(buf, count); 1046 - unlock: 1047 1034 mutex_unlock(&dynamic_netconsole_mutex); 1048 1035 mutex_unlock(&netconsole_subsys.su_mutex); 1049 1036 return ret; ··· 1065 1054 if (taskname_enabled == curr) 1066 1055 goto unlock_ok; 1067 1056 1068 - if (taskname_enabled && 1069 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1070 - ret = -ENOSPC; 1071 - goto unlock; 1072 - } 1073 - 1074 1057 if (taskname_enabled) 1075 1058 nt->sysdata_fields |= SYSDATA_TASKNAME; 1076 1059 else ··· 1072 1067 1073 1068 unlock_ok: 1074 1069 ret = strnlen(buf, count); 1075 - unlock: 1076 1070 mutex_unlock(&dynamic_netconsole_mutex); 1077 1071 mutex_unlock(&netconsole_subsys.su_mutex); 1078 1072 return ret; ··· 1096 1092 /* no change requested */ 1097 1093 goto unlock_ok; 1098 1094 1099 - if (cpu_nr_enabled && 1100 - count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) { 1101 - /* user wants the new feature, but there is no space in the 1102 - * buffer. 1103 - */ 1104 - ret = -ENOSPC; 1105 - goto unlock; 1106 - } 1107 - 1108 1095 if (cpu_nr_enabled) 1109 1096 nt->sysdata_fields |= SYSDATA_CPU_NR; 1110 1097 else 1111 - /* This is special because extradata_complete might have 1112 - * remaining data from previous sysdata, and it needs to be 1113 - * cleaned. 1098 + /* This is special because sysdata might have remaining data 1099 + * from previous sysdata, and it needs to be cleaned. 1114 1100 */ 1115 1101 disable_sysdata_feature(nt, SYSDATA_CPU_NR); 1116 1102 1117 1103 unlock_ok: 1118 1104 ret = strnlen(buf, count); 1119 - unlock: 1120 1105 mutex_unlock(&dynamic_netconsole_mutex); 1121 1106 mutex_unlock(&netconsole_subsys.su_mutex); 1122 1107 return ret; ··· 1149 1156 1150 1157 ud = to_userdata(&group->cg_item); 1151 1158 nt = userdata_to_target(ud); 1152 - if (count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) 1159 + if (count_userdata_entries(nt) >= MAX_USERDATA_ITEMS) 1153 1160 return ERR_PTR(-ENOSPC); 1154 1161 1155 1162 udm = kzalloc(sizeof(*udm), GFP_KERNEL); ··· 1227 1234 1228 1235 static void netconsole_target_release(struct config_item *item) 1229 1236 { 1230 - kfree(to_target(item)); 1237 + struct netconsole_target *nt = to_target(item); 1238 + 1239 + kfree(nt->userdata); 1240 + kfree(nt); 1231 1241 } 1232 1242 1233 1243 static struct configfs_item_operations netconsole_target_item_ops = { ··· 1359 1363 1360 1364 static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset) 1361 1365 { 1362 - /* Append cpu=%d at extradata_complete after userdata str */ 1363 - return scnprintf(&nt->extradata_complete[offset], 1366 + return scnprintf(&nt->sysdata[offset], 1364 1367 MAX_EXTRADATA_ENTRY_LEN, " cpu=%u\n", 1365 1368 raw_smp_processor_id()); 1366 1369 } 1367 1370 1368 1371 static int sysdata_append_taskname(struct netconsole_target *nt, int offset) 1369 1372 { 1370 - return scnprintf(&nt->extradata_complete[offset], 1373 + return scnprintf(&nt->sysdata[offset], 1371 1374 MAX_EXTRADATA_ENTRY_LEN, " taskname=%s\n", 1372 1375 current->comm); 1373 1376 } 1374 1377 1375 1378 static int sysdata_append_release(struct netconsole_target *nt, int offset) 1376 1379 { 1377 - return scnprintf(&nt->extradata_complete[offset], 1380 + return scnprintf(&nt->sysdata[offset], 1378 1381 MAX_EXTRADATA_ENTRY_LEN, " release=%s\n", 1379 1382 init_utsname()->release); 1380 1383 } ··· 1381 1386 static int sysdata_append_msgid(struct netconsole_target *nt, int offset) 1382 1387 { 1383 1388 wrapping_assign_add(nt->msgcounter, 1); 1384 - return scnprintf(&nt->extradata_complete[offset], 1389 + return scnprintf(&nt->sysdata[offset], 1385 1390 MAX_EXTRADATA_ENTRY_LEN, " msgid=%u\n", 1386 1391 nt->msgcounter); 1387 1392 } 1388 1393 1389 1394 /* 1390 - * prepare_extradata - append sysdata at extradata_complete in runtime 1395 + * prepare_sysdata - append sysdata in runtime 1391 1396 * @nt: target to send message to 1392 1397 */ 1393 - static int prepare_extradata(struct netconsole_target *nt) 1398 + static int prepare_sysdata(struct netconsole_target *nt) 1394 1399 { 1395 - int extradata_len; 1396 - 1397 - /* userdata was appended when configfs write helper was called 1398 - * by update_userdata(). 1399 - */ 1400 - extradata_len = nt->userdata_length; 1400 + int sysdata_len = 0; 1401 1401 1402 1402 if (!nt->sysdata_fields) 1403 1403 goto out; 1404 1404 1405 1405 if (nt->sysdata_fields & SYSDATA_CPU_NR) 1406 - extradata_len += sysdata_append_cpu_nr(nt, extradata_len); 1406 + sysdata_len += sysdata_append_cpu_nr(nt, sysdata_len); 1407 1407 if (nt->sysdata_fields & SYSDATA_TASKNAME) 1408 - extradata_len += sysdata_append_taskname(nt, extradata_len); 1408 + sysdata_len += sysdata_append_taskname(nt, sysdata_len); 1409 1409 if (nt->sysdata_fields & SYSDATA_RELEASE) 1410 - extradata_len += sysdata_append_release(nt, extradata_len); 1410 + sysdata_len += sysdata_append_release(nt, sysdata_len); 1411 1411 if (nt->sysdata_fields & SYSDATA_MSGID) 1412 - extradata_len += sysdata_append_msgid(nt, extradata_len); 1412 + sysdata_len += sysdata_append_msgid(nt, sysdata_len); 1413 1413 1414 - WARN_ON_ONCE(extradata_len > 1415 - MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS); 1414 + WARN_ON_ONCE(sysdata_len > 1415 + MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS); 1416 1416 1417 1417 out: 1418 - return extradata_len; 1419 - } 1420 - #else /* CONFIG_NETCONSOLE_DYNAMIC not set */ 1421 - static int prepare_extradata(struct netconsole_target *nt) 1422 - { 1423 - return 0; 1418 + return sysdata_len; 1424 1419 } 1425 1420 #endif /* CONFIG_NETCONSOLE_DYNAMIC */ 1426 1421 ··· 1512 1527 int msg_len, 1513 1528 int release_len) 1514 1529 { 1515 - const char *extradata = NULL; 1530 + const char *userdata = NULL; 1531 + const char *sysdata = NULL; 1516 1532 const char *release; 1517 1533 1518 1534 #ifdef CONFIG_NETCONSOLE_DYNAMIC 1519 - extradata = nt->extradata_complete; 1535 + userdata = nt->userdata; 1536 + sysdata = nt->sysdata; 1520 1537 #endif 1521 1538 1522 1539 if (release_len) { ··· 1530 1543 memcpy(nt->buf, msg, msg_len); 1531 1544 } 1532 1545 1533 - if (extradata) 1546 + if (userdata) 1534 1547 msg_len += scnprintf(&nt->buf[msg_len], 1535 - MAX_PRINT_CHUNK - msg_len, 1536 - "%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); 1537 1555 1538 1556 send_udp(nt, nt->buf, msg_len); 1539 1557 } ··· 1552 1560 } 1553 1561 1554 1562 static void send_fragmented_body(struct netconsole_target *nt, 1555 - const char *msgbody, int header_len, 1556 - int msgbody_len, int extradata_len) 1563 + const char *msgbody_ptr, int header_len, 1564 + int msgbody_len, int sysdata_len) 1557 1565 { 1558 - int sent_extradata, preceding_bytes; 1559 - const char *extradata = NULL; 1560 - int body_len, offset = 0; 1566 + const char *userdata_ptr = NULL; 1567 + const char *sysdata_ptr = NULL; 1568 + int data_len, data_sent = 0; 1569 + int userdata_offset = 0; 1570 + int sysdata_offset = 0; 1571 + int msgbody_offset = 0; 1572 + int userdata_len = 0; 1561 1573 1562 1574 #ifdef CONFIG_NETCONSOLE_DYNAMIC 1563 - extradata = nt->extradata_complete; 1575 + userdata_ptr = nt->userdata; 1576 + sysdata_ptr = nt->sysdata; 1577 + userdata_len = nt->userdata_length; 1564 1578 #endif 1579 + if (WARN_ON_ONCE(!userdata_ptr && userdata_len != 0)) 1580 + return; 1565 1581 1566 - /* body_len represents the number of bytes that will be sent. This is 1582 + if (WARN_ON_ONCE(!sysdata_ptr && sysdata_len != 0)) 1583 + return; 1584 + 1585 + /* data_len represents the number of bytes that will be sent. This is 1567 1586 * bigger than MAX_PRINT_CHUNK, thus, it will be split in multiple 1568 1587 * packets 1569 1588 */ 1570 - body_len = msgbody_len + extradata_len; 1589 + data_len = msgbody_len + userdata_len + sysdata_len; 1571 1590 1572 1591 /* In each iteration of the while loop below, we send a packet 1573 - * containing the header and a portion of the body. The body is 1574 - * composed of two parts: msgbody and extradata. We keep track of how 1575 - * many bytes have been sent so far using the offset variable, which 1576 - * ranges from 0 to the total length of the body. 1592 + * containing the header and a portion of the data. The data is 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. 1577 1599 */ 1578 - while (offset < body_len) { 1579 - int this_header = header_len; 1580 - bool msgbody_written = false; 1581 - int this_offset = 0; 1600 + while (data_sent < data_len) { 1601 + int userdata_left = userdata_len - userdata_offset; 1602 + int sysdata_left = sysdata_len - sysdata_offset; 1603 + int msgbody_left = msgbody_len - msgbody_offset; 1604 + int buf_offset = 0; 1582 1605 int this_chunk = 0; 1583 1606 1584 - this_header += scnprintf(nt->buf + this_header, 1585 - MAX_PRINT_CHUNK - this_header, 1586 - ",ncfrag=%d/%d;", offset, 1587 - body_len); 1607 + /* header is already populated in nt->buf, just append to it */ 1608 + buf_offset = header_len; 1588 1609 1589 - /* Not all msgbody data has been written yet */ 1590 - if (offset < msgbody_len) { 1591 - this_chunk = min(msgbody_len - offset, 1592 - MAX_PRINT_CHUNK - this_header); 1593 - if (WARN_ON_ONCE(this_chunk <= 0)) 1594 - return; 1595 - memcpy(nt->buf + this_header, msgbody + offset, 1596 - this_chunk); 1597 - this_offset += this_chunk; 1610 + buf_offset += scnprintf(nt->buf + buf_offset, 1611 + MAX_PRINT_CHUNK - buf_offset, 1612 + ",ncfrag=%d/%d;", data_sent, 1613 + data_len); 1614 + 1615 + /* append msgbody first */ 1616 + this_chunk = min(msgbody_left, MAX_PRINT_CHUNK - buf_offset); 1617 + memcpy(nt->buf + buf_offset, msgbody_ptr + msgbody_offset, 1618 + this_chunk); 1619 + msgbody_offset += this_chunk; 1620 + buf_offset += this_chunk; 1621 + data_sent += this_chunk; 1622 + 1623 + /* after msgbody, append userdata */ 1624 + if (userdata_ptr && userdata_left) { 1625 + this_chunk = min(userdata_left, 1626 + MAX_PRINT_CHUNK - buf_offset); 1627 + memcpy(nt->buf + buf_offset, 1628 + userdata_ptr + userdata_offset, this_chunk); 1629 + userdata_offset += this_chunk; 1630 + buf_offset += this_chunk; 1631 + data_sent += this_chunk; 1598 1632 } 1599 1633 1600 - /* msgbody was finally written, either in the previous 1601 - * messages and/or in the current buf. Time to write 1602 - * the extradata. 1603 - */ 1604 - msgbody_written |= offset + this_offset >= msgbody_len; 1605 - 1606 - /* Msg body is fully written and there is pending extradata to 1607 - * write, append extradata in this chunk 1608 - */ 1609 - if (msgbody_written && offset + this_offset < body_len) { 1610 - /* Track how much user data was already sent. First 1611 - * time here, sent_userdata is zero 1612 - */ 1613 - sent_extradata = (offset + this_offset) - msgbody_len; 1614 - /* offset of bytes used in current buf */ 1615 - preceding_bytes = this_chunk + this_header; 1616 - 1617 - if (WARN_ON_ONCE(sent_extradata < 0)) 1618 - return; 1619 - 1620 - this_chunk = min(extradata_len - sent_extradata, 1621 - MAX_PRINT_CHUNK - preceding_bytes); 1622 - if (WARN_ON_ONCE(this_chunk < 0)) 1623 - /* this_chunk could be zero if all the previous 1624 - * message used all the buffer. This is not a 1625 - * problem, extradata will be sent in the next 1626 - * iteration 1627 - */ 1628 - return; 1629 - 1630 - memcpy(nt->buf + this_header + this_offset, 1631 - extradata + sent_extradata, 1632 - this_chunk); 1633 - this_offset += this_chunk; 1634 + /* after userdata, append sysdata */ 1635 + if (sysdata_ptr && sysdata_left) { 1636 + this_chunk = min(sysdata_left, 1637 + MAX_PRINT_CHUNK - buf_offset); 1638 + memcpy(nt->buf + buf_offset, 1639 + sysdata_ptr + sysdata_offset, this_chunk); 1640 + sysdata_offset += this_chunk; 1641 + buf_offset += this_chunk; 1642 + data_sent += this_chunk; 1634 1643 } 1635 1644 1636 - send_udp(nt, nt->buf, this_header + this_offset); 1637 - offset += this_offset; 1645 + /* if all is good, send the packet out */ 1646 + if (WARN_ON_ONCE(data_sent > data_len)) 1647 + return; 1648 + 1649 + send_udp(nt, nt->buf, buf_offset); 1638 1650 } 1639 1651 } 1640 1652 ··· 1646 1650 const char *msg, 1647 1651 int msg_len, 1648 1652 int release_len, 1649 - int extradata_len) 1653 + int sysdata_len) 1650 1654 { 1651 1655 int header_len, msgbody_len; 1652 1656 const char *msgbody; ··· 1675 1679 * will be replaced 1676 1680 */ 1677 1681 send_fragmented_body(nt, msgbody, header_len, msgbody_len, 1678 - extradata_len); 1682 + sysdata_len); 1679 1683 } 1680 1684 1681 1685 /** ··· 1691 1695 static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg, 1692 1696 int msg_len) 1693 1697 { 1698 + int userdata_len = 0; 1694 1699 int release_len = 0; 1695 - int extradata_len; 1700 + int sysdata_len = 0; 1696 1701 1697 - extradata_len = prepare_extradata(nt); 1698 - 1702 + #ifdef CONFIG_NETCONSOLE_DYNAMIC 1703 + sysdata_len = prepare_sysdata(nt); 1704 + userdata_len = nt->userdata_length; 1705 + #endif 1699 1706 if (nt->release) 1700 1707 release_len = strlen(init_utsname()->release) + 1; 1701 1708 1702 - if (msg_len + release_len + extradata_len <= MAX_PRINT_CHUNK) 1709 + if (msg_len + release_len + sysdata_len + userdata_len <= MAX_PRINT_CHUNK) 1703 1710 return send_msg_no_fragmentation(nt, msg, msg_len, release_len); 1704 1711 1705 1712 return send_msg_fragmented(nt, msg, msg_len, release_len, 1706 - extradata_len); 1713 + sysdata_len); 1707 1714 } 1708 1715 1709 1716 static void write_ext_msg(struct console *con, const char *msg, ··· 1911 1912 static void free_param_target(struct netconsole_target *nt) 1912 1913 { 1913 1914 netpoll_cleanup(&nt->np); 1915 + #ifdef CONFIG_NETCONSOLE_DYNAMIC 1916 + kfree(nt->userdata); 1917 + #endif 1914 1918 kfree(nt); 1915 1919 } 1916 1920
+1 -1
tools/testing/selftests/drivers/net/netcons_overflow.sh
··· 15 15 16 16 source "${SCRIPTDIR}"/lib/sh/lib_netcons.sh 17 17 # This is coming from netconsole code. Check for it in drivers/net/netconsole.c 18 - MAX_USERDATA_ITEMS=16 18 + MAX_USERDATA_ITEMS=256 19 19 20 20 # Function to create userdata entries 21 21 function create_userdata_max_entries() {