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 'selftests-drv-net-gro-enable-hw-gro-and-lro-testing'

Jakub Kicinski says:

====================
selftests: drv-net: gro: enable HW GRO and LRO testing

Add support for running our existing GRO test against HW GRO
and LRO implementation. The first 3 patches are just ksft lib
nice-to-haves, and patch 4 cleans up the existing gro Python.

Patches 5 and 6 are of most practical interest. The support
reconfiguring the NIC to disable SW GRO and enable HW GRO and LRO.
Additionally last patch breaks up the existing GRO cases to
track HW compliance at finer granularity.
====================

Link: https://patch.msgid.link/20260113000740.255360-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+439 -216
+252 -181
tools/testing/selftests/drivers/net/gro.c
··· 3 3 * This testsuite provides conformance testing for GRO coalescing. 4 4 * 5 5 * Test cases: 6 - * 1.data 6 + * 7 + * data_*: 7 8 * Data packets of the same size and same header setup with correct 8 9 * sequence numbers coalesce. The one exception being the last data 9 10 * packet coalesced: it can be smaller than the rest and coalesced 10 11 * as long as it is in the same flow. 11 - * 2.ack 12 + * - data_same: same size packets coalesce 13 + * - data_lrg_sml: large then small coalesces 14 + * - data_sml_lrg: small then large doesn't coalesce 15 + * 16 + * ack: 12 17 * Pure ACK does not coalesce. 13 - * 3.flags 14 - * Specific test cases: no packets with PSH, SYN, URG, RST set will 15 - * be coalesced. 16 - * 4.tcp 18 + * 19 + * flags_*: 20 + * No packets with PSH, SYN, URG, RST set will be coalesced. 21 + * - flags_psh, flags_syn, flags_rst, flags_urg 22 + * 23 + * tcp_*: 17 24 * Packets with incorrect checksum, non-consecutive seqno and 18 25 * different TCP header options shouldn't coalesce. Nit: given that 19 26 * some extension headers have paddings, such as timestamp, headers 20 - * that are padding differently would not be coalesced. 21 - * 5.ip: 22 - * Packets with different (ECN, TTL, TOS) header, ip options or 23 - * ip fragments (ipv6) shouldn't coalesce. 24 - * 6.large: 27 + * that are padded differently would not be coalesced. 28 + * - tcp_csum: incorrect checksum 29 + * - tcp_seq: non-consecutive sequence numbers 30 + * - tcp_ts: different timestamps 31 + * - tcp_opt: different TCP options 32 + * 33 + * ip_*: 34 + * Packets with different (ECN, TTL, TOS) header, IP options or 35 + * IP fragments shouldn't coalesce. 36 + * - ip_ecn, ip_tos: shared between IPv4/IPv6 37 + * - ip_ttl, ip_opt, ip_frag4: IPv4 only 38 + * - ip_id_df*: IPv4 IP ID field coalescing tests 39 + * - ip_frag6, ip_v6ext_*: IPv6 only 40 + * 41 + * large_*: 25 42 * Packets larger than GRO_MAX_SIZE packets shouldn't coalesce. 43 + * - large_max: exceeding max size 44 + * - large_rem: remainder handling 26 45 * 27 46 * MSS is defined as 4096 - header because if it is too small 28 47 * (i.e. 1500 MTU - header), it will result in many packets, ··· 98 79 #define ipv6_optlen(p) (((p)->hdrlen+1) << 3) /* calculate IPv6 extension header len */ 99 80 #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) 100 81 82 + enum flush_id_case { 83 + FLUSH_ID_DF1_INC, 84 + FLUSH_ID_DF1_FIXED, 85 + FLUSH_ID_DF0_INC, 86 + FLUSH_ID_DF0_FIXED, 87 + FLUSH_ID_DF1_INC_FIXED, 88 + FLUSH_ID_DF1_FIXED_INC, 89 + }; 90 + 101 91 static const char *addr6_src = "fdaa::2"; 102 92 static const char *addr6_dst = "fdaa::1"; 103 93 static const char *addr4_src = "192.168.1.200"; ··· 123 95 static int total_hdr_len = -1; 124 96 static int ethhdr_proto = -1; 125 97 static bool ipip; 126 - static const int num_flush_id_cases = 6; 127 98 128 99 static void vlog(const char *fmt, ...) 129 100 { ··· 154 127 /* Overridden later if exthdrs are used: */ 155 128 opt_ipproto_off = ipproto_off; 156 129 157 - if (strcmp(testname, "ip") == 0) { 158 - if (proto == PF_INET) 159 - optlen = sizeof(struct ip_timestamp); 160 - else { 161 - BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE); 162 - BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE); 163 - BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE); 130 + if (strcmp(testname, "ip_opt") == 0) { 131 + optlen = sizeof(struct ip_timestamp); 132 + } else if (strcmp(testname, "ip_frag6") == 0 || 133 + strcmp(testname, "ip_v6ext_same") == 0 || 134 + strcmp(testname, "ip_v6ext_diff") == 0) { 135 + BUILD_BUG_ON(sizeof(struct ip6_hbh) > MIN_EXTHDR_SIZE); 136 + BUILD_BUG_ON(sizeof(struct ip6_dest) > MIN_EXTHDR_SIZE); 137 + BUILD_BUG_ON(sizeof(struct ip6_frag) > MIN_EXTHDR_SIZE); 164 138 165 - /* same size for HBH and Fragment extension header types */ 166 - optlen = MIN_EXTHDR_SIZE; 167 - opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr) 168 - + offsetof(struct ip6_ext, ip6e_nxt); 169 - } 139 + /* same size for HBH and Fragment extension header types */ 140 + optlen = MIN_EXTHDR_SIZE; 141 + opt_ipproto_off = ETH_HLEN + sizeof(struct ipv6hdr) 142 + + offsetof(struct ip6_ext, ip6e_nxt); 170 143 } 171 144 172 145 /* this filter validates the following: ··· 675 648 iph->check = checksum_fold(iph, sizeof(struct iphdr), 0); 676 649 } 677 650 678 - static void send_flush_id_case(int fd, struct sockaddr_ll *daddr, int tcase) 651 + static void send_flush_id_case(int fd, struct sockaddr_ll *daddr, 652 + enum flush_id_case tcase) 679 653 { 680 654 static char buf1[MAX_HDR_LEN + PAYLOAD_LEN]; 681 655 static char buf2[MAX_HDR_LEN + PAYLOAD_LEN]; ··· 695 667 create_packet(buf3, PAYLOAD_LEN * 2, 0, PAYLOAD_LEN, 0); 696 668 697 669 switch (tcase) { 698 - case 0: /* DF=1, Incrementing - should coalesce */ 670 + case FLUSH_ID_DF1_INC: /* DF=1, Incrementing - should coalesce */ 699 671 iph1->frag_off |= htons(IP_DF); 700 672 iph1->id = htons(8); 701 673 ··· 703 675 iph2->id = htons(9); 704 676 break; 705 677 706 - case 1: /* DF=1, Fixed - should coalesce */ 678 + case FLUSH_ID_DF1_FIXED: /* DF=1, Fixed - should coalesce */ 707 679 iph1->frag_off |= htons(IP_DF); 708 680 iph1->id = htons(8); 709 681 ··· 711 683 iph2->id = htons(8); 712 684 break; 713 685 714 - case 2: /* DF=0, Incrementing - should coalesce */ 686 + case FLUSH_ID_DF0_INC: /* DF=0, Incrementing - should coalesce */ 715 687 iph1->frag_off &= ~htons(IP_DF); 716 688 iph1->id = htons(8); 717 689 ··· 719 691 iph2->id = htons(9); 720 692 break; 721 693 722 - case 3: /* DF=0, Fixed - should coalesce */ 694 + case FLUSH_ID_DF0_FIXED: /* DF=0, Fixed - should coalesce */ 723 695 iph1->frag_off &= ~htons(IP_DF); 724 696 iph1->id = htons(8); 725 697 ··· 727 699 iph2->id = htons(8); 728 700 break; 729 701 730 - case 4: /* DF=1, two packets incrementing, and one fixed - should 731 - * coalesce only the first two packets 732 - */ 702 + case FLUSH_ID_DF1_INC_FIXED: /* DF=1, two packets incrementing, and 703 + * one fixed - should coalesce only the 704 + * first two packets 705 + */ 733 706 iph1->frag_off |= htons(IP_DF); 734 707 iph1->id = htons(8); 735 708 ··· 742 713 send_three = true; 743 714 break; 744 715 745 - case 5: /* DF=1, two packets fixed, and one incrementing - should 746 - * coalesce only the first two packets 747 - */ 716 + case FLUSH_ID_DF1_FIXED_INC: /* DF=1, two packets fixed, and one 717 + * incrementing - should coalesce only 718 + * the first two packets 719 + */ 748 720 iph1->frag_off |= htons(IP_DF); 749 721 iph1->id = htons(8); 750 722 ··· 766 736 if (send_three) { 767 737 fix_ip4_checksum(iph3); 768 738 write_packet(fd, buf3, total_hdr_len + PAYLOAD_LEN, daddr); 769 - } 770 - } 771 - 772 - static void test_flush_id(int fd, struct sockaddr_ll *daddr, char *fin_pkt) 773 - { 774 - for (int i = 0; i < num_flush_id_cases; i++) { 775 - sleep(1); 776 - send_flush_id_case(fd, daddr, i); 777 - sleep(1); 778 - write_packet(fd, fin_pkt, total_hdr_len, daddr); 779 739 } 780 740 } 781 741 ··· 1050 1030 daddr.sll_halen = ETH_ALEN; 1051 1031 create_packet(fin_pkt, PAYLOAD_LEN * 2, 0, 0, 1); 1052 1032 1053 - if (strcmp(testname, "data") == 0) { 1033 + /* data sub-tests */ 1034 + if (strcmp(testname, "data_same") == 0) { 1054 1035 send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN); 1055 1036 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1056 - 1037 + } else if (strcmp(testname, "data_lrg_sml") == 0) { 1057 1038 send_data_pkts(txfd, &daddr, PAYLOAD_LEN, PAYLOAD_LEN / 2); 1058 1039 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1059 - 1040 + } else if (strcmp(testname, "data_sml_lrg") == 0) { 1060 1041 send_data_pkts(txfd, &daddr, PAYLOAD_LEN / 2, PAYLOAD_LEN); 1061 1042 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1043 + 1044 + /* ack test */ 1062 1045 } else if (strcmp(testname, "ack") == 0) { 1063 1046 send_ack(txfd, &daddr); 1064 1047 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1065 - } else if (strcmp(testname, "flags") == 0) { 1048 + 1049 + /* flags sub-tests */ 1050 + } else if (strcmp(testname, "flags_psh") == 0) { 1066 1051 send_flags(txfd, &daddr, 1, 0, 0, 0); 1067 1052 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1068 - 1053 + } else if (strcmp(testname, "flags_syn") == 0) { 1069 1054 send_flags(txfd, &daddr, 0, 1, 0, 0); 1070 1055 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1071 - 1056 + } else if (strcmp(testname, "flags_rst") == 0) { 1072 1057 send_flags(txfd, &daddr, 0, 0, 1, 0); 1073 1058 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1074 - 1059 + } else if (strcmp(testname, "flags_urg") == 0) { 1075 1060 send_flags(txfd, &daddr, 0, 0, 0, 1); 1076 1061 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1077 - } else if (strcmp(testname, "tcp") == 0) { 1062 + 1063 + /* tcp sub-tests */ 1064 + } else if (strcmp(testname, "tcp_csum") == 0) { 1078 1065 send_changed_checksum(txfd, &daddr); 1079 - /* Adding sleep before sending FIN so that it is not 1080 - * received prior to other packets. 1081 - */ 1082 1066 usleep(fin_delay_us); 1083 1067 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1084 - 1068 + } else if (strcmp(testname, "tcp_seq") == 0) { 1085 1069 send_changed_seq(txfd, &daddr); 1086 1070 usleep(fin_delay_us); 1087 1071 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1088 - 1072 + } else if (strcmp(testname, "tcp_ts") == 0) { 1089 1073 send_changed_ts(txfd, &daddr); 1090 1074 usleep(fin_delay_us); 1091 1075 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1092 - 1076 + } else if (strcmp(testname, "tcp_opt") == 0) { 1093 1077 send_diff_opt(txfd, &daddr); 1094 1078 usleep(fin_delay_us); 1095 1079 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1096 - } else if (strcmp(testname, "ip") == 0) { 1080 + 1081 + /* ip sub-tests - shared between IPv4 and IPv6 */ 1082 + } else if (strcmp(testname, "ip_ecn") == 0) { 1097 1083 send_changed_ECN(txfd, &daddr); 1098 1084 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1099 - 1085 + } else if (strcmp(testname, "ip_tos") == 0) { 1100 1086 send_changed_tos(txfd, &daddr); 1101 1087 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1102 - if (proto == PF_INET) { 1103 - /* Modified packets may be received out of order. 1104 - * Sleep function added to enforce test boundaries 1105 - * so that fin pkts are not received prior to other pkts. 1106 - */ 1107 - sleep(1); 1108 - send_changed_ttl(txfd, &daddr); 1109 - write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1110 1088 1111 - sleep(1); 1112 - send_ip_options(txfd, &daddr); 1113 - sleep(1); 1114 - write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1089 + /* ip sub-tests - IPv4 only */ 1090 + } else if (strcmp(testname, "ip_ttl") == 0) { 1091 + send_changed_ttl(txfd, &daddr); 1092 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1093 + } else if (strcmp(testname, "ip_opt") == 0) { 1094 + send_ip_options(txfd, &daddr); 1095 + usleep(fin_delay_us); 1096 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1097 + } else if (strcmp(testname, "ip_frag4") == 0) { 1098 + send_fragment4(txfd, &daddr); 1099 + usleep(fin_delay_us); 1100 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1101 + } else if (strcmp(testname, "ip_id_df1_inc") == 0) { 1102 + send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_INC); 1103 + usleep(fin_delay_us); 1104 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1105 + } else if (strcmp(testname, "ip_id_df1_fixed") == 0) { 1106 + send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_FIXED); 1107 + usleep(fin_delay_us); 1108 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1109 + } else if (strcmp(testname, "ip_id_df0_inc") == 0) { 1110 + send_flush_id_case(txfd, &daddr, FLUSH_ID_DF0_INC); 1111 + usleep(fin_delay_us); 1112 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1113 + } else if (strcmp(testname, "ip_id_df0_fixed") == 0) { 1114 + send_flush_id_case(txfd, &daddr, FLUSH_ID_DF0_FIXED); 1115 + usleep(fin_delay_us); 1116 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1117 + } else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) { 1118 + send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_INC_FIXED); 1119 + usleep(fin_delay_us); 1120 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1121 + } else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) { 1122 + send_flush_id_case(txfd, &daddr, FLUSH_ID_DF1_FIXED_INC); 1123 + usleep(fin_delay_us); 1124 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1115 1125 1116 - sleep(1); 1117 - send_fragment4(txfd, &daddr); 1118 - sleep(1); 1119 - write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1126 + /* ip sub-tests - IPv6 only */ 1127 + } else if (strcmp(testname, "ip_frag6") == 0) { 1128 + send_fragment6(txfd, &daddr); 1129 + usleep(fin_delay_us); 1130 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1131 + } else if (strcmp(testname, "ip_v6ext_same") == 0) { 1132 + send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1); 1133 + usleep(fin_delay_us); 1134 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1135 + } else if (strcmp(testname, "ip_v6ext_diff") == 0) { 1136 + send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2); 1137 + usleep(fin_delay_us); 1138 + write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1120 1139 1121 - test_flush_id(txfd, &daddr, fin_pkt); 1122 - } else if (proto == PF_INET6) { 1123 - sleep(1); 1124 - send_fragment6(txfd, &daddr); 1125 - sleep(1); 1126 - write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1127 - 1128 - sleep(1); 1129 - /* send IPv6 packets with ext header with same payload */ 1130 - send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_1); 1131 - sleep(1); 1132 - write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1133 - 1134 - sleep(1); 1135 - /* send IPv6 packets with ext header with different payload */ 1136 - send_ipv6_exthdr(txfd, &daddr, EXT_PAYLOAD_1, EXT_PAYLOAD_2); 1137 - sleep(1); 1138 - write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1139 - } 1140 - } else if (strcmp(testname, "large") == 0) { 1141 - /* 20 is the difference between min iphdr size 1142 - * and min ipv6hdr size. Like MAX_HDR_SIZE, 1143 - * MAX_PAYLOAD is defined with the larger header of the two. 1144 - */ 1140 + /* large sub-tests */ 1141 + } else if (strcmp(testname, "large_max") == 0) { 1145 1142 int offset = (proto == PF_INET && !ipip) ? 20 : 0; 1146 1143 int remainder = (MAX_PAYLOAD + offset) % MSS; 1147 1144 1148 1145 send_large(txfd, &daddr, remainder); 1149 1146 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1147 + } else if (strcmp(testname, "large_rem") == 0) { 1148 + int offset = (proto == PF_INET && !ipip) ? 20 : 0; 1149 + int remainder = (MAX_PAYLOAD + offset) % MSS; 1150 1150 1151 1151 send_large(txfd, &daddr, remainder + 1); 1152 1152 write_packet(txfd, fin_pkt, total_hdr_len, &daddr); 1153 1153 } else { 1154 - error(1, 0, "Unknown testcase"); 1154 + error(1, 0, "Unknown testcase: %s", testname); 1155 1155 } 1156 1156 1157 1157 if (close(txfd)) ··· 1195 1155 1196 1156 memset(correct_payload, 0, sizeof(correct_payload)); 1197 1157 1198 - if (strcmp(testname, "data") == 0) { 1158 + /* data sub-tests */ 1159 + if (strcmp(testname, "data_same") == 0) { 1199 1160 printf("pure data packet of same size: "); 1200 1161 correct_payload[0] = PAYLOAD_LEN * 2; 1201 1162 check_recv_pkts(rxfd, correct_payload, 1); 1202 - 1163 + } else if (strcmp(testname, "data_lrg_sml") == 0) { 1203 1164 printf("large data packets followed by a smaller one: "); 1204 1165 correct_payload[0] = PAYLOAD_LEN * 1.5; 1205 1166 check_recv_pkts(rxfd, correct_payload, 1); 1206 - 1167 + } else if (strcmp(testname, "data_sml_lrg") == 0) { 1207 1168 printf("small data packets followed by a larger one: "); 1208 1169 correct_payload[0] = PAYLOAD_LEN / 2; 1209 1170 correct_payload[1] = PAYLOAD_LEN; 1210 1171 check_recv_pkts(rxfd, correct_payload, 2); 1172 + 1173 + /* ack test */ 1211 1174 } else if (strcmp(testname, "ack") == 0) { 1212 1175 printf("duplicate ack and pure ack: "); 1213 1176 check_recv_pkts(rxfd, correct_payload, 3); 1214 - } else if (strcmp(testname, "flags") == 0) { 1177 + 1178 + /* flags sub-tests */ 1179 + } else if (strcmp(testname, "flags_psh") == 0) { 1215 1180 correct_payload[0] = PAYLOAD_LEN * 3; 1216 1181 correct_payload[1] = PAYLOAD_LEN * 2; 1217 - 1218 1182 printf("psh flag ends coalescing: "); 1219 1183 check_recv_pkts(rxfd, correct_payload, 2); 1220 - 1184 + } else if (strcmp(testname, "flags_syn") == 0) { 1221 1185 correct_payload[0] = PAYLOAD_LEN * 2; 1222 1186 correct_payload[1] = 0; 1223 1187 correct_payload[2] = PAYLOAD_LEN * 2; 1224 1188 printf("syn flag ends coalescing: "); 1225 1189 check_recv_pkts(rxfd, correct_payload, 3); 1226 - 1190 + } else if (strcmp(testname, "flags_rst") == 0) { 1191 + correct_payload[0] = PAYLOAD_LEN * 2; 1192 + correct_payload[1] = 0; 1193 + correct_payload[2] = PAYLOAD_LEN * 2; 1227 1194 printf("rst flag ends coalescing: "); 1228 1195 check_recv_pkts(rxfd, correct_payload, 3); 1229 - 1196 + } else if (strcmp(testname, "flags_urg") == 0) { 1197 + correct_payload[0] = PAYLOAD_LEN * 2; 1198 + correct_payload[1] = 0; 1199 + correct_payload[2] = PAYLOAD_LEN * 2; 1230 1200 printf("urg flag ends coalescing: "); 1231 1201 check_recv_pkts(rxfd, correct_payload, 3); 1232 - } else if (strcmp(testname, "tcp") == 0) { 1202 + 1203 + /* tcp sub-tests */ 1204 + } else if (strcmp(testname, "tcp_csum") == 0) { 1233 1205 correct_payload[0] = PAYLOAD_LEN; 1206 + correct_payload[1] = PAYLOAD_LEN; 1207 + printf("changed checksum does not coalesce: "); 1208 + check_recv_pkts(rxfd, correct_payload, 2); 1209 + } else if (strcmp(testname, "tcp_seq") == 0) { 1210 + correct_payload[0] = PAYLOAD_LEN; 1211 + correct_payload[1] = PAYLOAD_LEN; 1212 + printf("Wrong Seq number doesn't coalesce: "); 1213 + check_recv_pkts(rxfd, correct_payload, 2); 1214 + } else if (strcmp(testname, "tcp_ts") == 0) { 1215 + correct_payload[0] = PAYLOAD_LEN * 2; 1234 1216 correct_payload[1] = PAYLOAD_LEN; 1235 1217 correct_payload[2] = PAYLOAD_LEN; 1236 1218 correct_payload[3] = PAYLOAD_LEN; 1237 - 1238 - printf("changed checksum does not coalesce: "); 1239 - check_recv_pkts(rxfd, correct_payload, 2); 1240 - 1241 - printf("Wrong Seq number doesn't coalesce: "); 1242 - check_recv_pkts(rxfd, correct_payload, 2); 1243 - 1244 1219 printf("Different timestamp doesn't coalesce: "); 1245 - correct_payload[0] = PAYLOAD_LEN * 2; 1246 1220 check_recv_pkts(rxfd, correct_payload, 4); 1247 - 1248 - printf("Different options doesn't coalesce: "); 1221 + } else if (strcmp(testname, "tcp_opt") == 0) { 1249 1222 correct_payload[0] = PAYLOAD_LEN * 2; 1223 + correct_payload[1] = PAYLOAD_LEN; 1224 + printf("Different options doesn't coalesce: "); 1250 1225 check_recv_pkts(rxfd, correct_payload, 2); 1251 - } else if (strcmp(testname, "ip") == 0) { 1226 + 1227 + /* ip sub-tests - shared between IPv4 and IPv6 */ 1228 + } else if (strcmp(testname, "ip_ecn") == 0) { 1252 1229 correct_payload[0] = PAYLOAD_LEN; 1253 1230 correct_payload[1] = PAYLOAD_LEN; 1254 - 1255 1231 printf("different ECN doesn't coalesce: "); 1256 1232 check_recv_pkts(rxfd, correct_payload, 2); 1257 - 1233 + } else if (strcmp(testname, "ip_tos") == 0) { 1234 + correct_payload[0] = PAYLOAD_LEN; 1235 + correct_payload[1] = PAYLOAD_LEN; 1258 1236 printf("different tos doesn't coalesce: "); 1259 1237 check_recv_pkts(rxfd, correct_payload, 2); 1260 1238 1261 - if (proto == PF_INET) { 1262 - printf("different ttl doesn't coalesce: "); 1263 - check_recv_pkts(rxfd, correct_payload, 2); 1239 + /* ip sub-tests - IPv4 only */ 1240 + } else if (strcmp(testname, "ip_ttl") == 0) { 1241 + correct_payload[0] = PAYLOAD_LEN; 1242 + correct_payload[1] = PAYLOAD_LEN; 1243 + printf("different ttl doesn't coalesce: "); 1244 + check_recv_pkts(rxfd, correct_payload, 2); 1245 + } else if (strcmp(testname, "ip_opt") == 0) { 1246 + correct_payload[0] = PAYLOAD_LEN; 1247 + correct_payload[1] = PAYLOAD_LEN; 1248 + correct_payload[2] = PAYLOAD_LEN; 1249 + printf("ip options doesn't coalesce: "); 1250 + check_recv_pkts(rxfd, correct_payload, 3); 1251 + } else if (strcmp(testname, "ip_frag4") == 0) { 1252 + correct_payload[0] = PAYLOAD_LEN; 1253 + correct_payload[1] = PAYLOAD_LEN; 1254 + printf("fragmented ip4 doesn't coalesce: "); 1255 + check_recv_pkts(rxfd, correct_payload, 2); 1256 + } else if (strcmp(testname, "ip_id_df1_inc") == 0) { 1257 + printf("DF=1, Incrementing - should coalesce: "); 1258 + correct_payload[0] = PAYLOAD_LEN * 2; 1259 + check_recv_pkts(rxfd, correct_payload, 1); 1260 + } else if (strcmp(testname, "ip_id_df1_fixed") == 0) { 1261 + printf("DF=1, Fixed - should coalesce: "); 1262 + correct_payload[0] = PAYLOAD_LEN * 2; 1263 + check_recv_pkts(rxfd, correct_payload, 1); 1264 + } else if (strcmp(testname, "ip_id_df0_inc") == 0) { 1265 + printf("DF=0, Incrementing - should coalesce: "); 1266 + correct_payload[0] = PAYLOAD_LEN * 2; 1267 + check_recv_pkts(rxfd, correct_payload, 1); 1268 + } else if (strcmp(testname, "ip_id_df0_fixed") == 0) { 1269 + printf("DF=0, Fixed - should coalesce: "); 1270 + correct_payload[0] = PAYLOAD_LEN * 2; 1271 + check_recv_pkts(rxfd, correct_payload, 1); 1272 + } else if (strcmp(testname, "ip_id_df1_inc_fixed") == 0) { 1273 + printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: "); 1274 + correct_payload[0] = PAYLOAD_LEN * 2; 1275 + correct_payload[1] = PAYLOAD_LEN; 1276 + check_recv_pkts(rxfd, correct_payload, 2); 1277 + } else if (strcmp(testname, "ip_id_df1_fixed_inc") == 0) { 1278 + printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: "); 1279 + correct_payload[0] = PAYLOAD_LEN * 2; 1280 + correct_payload[1] = PAYLOAD_LEN; 1281 + check_recv_pkts(rxfd, correct_payload, 2); 1264 1282 1265 - printf("ip options doesn't coalesce: "); 1266 - correct_payload[2] = PAYLOAD_LEN; 1267 - check_recv_pkts(rxfd, correct_payload, 3); 1283 + /* ip sub-tests - IPv6 only */ 1284 + } else if (strcmp(testname, "ip_frag6") == 0) { 1285 + /* GRO doesn't check for ipv6 hop limit when flushing. 1286 + * Hence no corresponding test to the ipv4 case. 1287 + */ 1288 + printf("fragmented ip6 doesn't coalesce: "); 1289 + correct_payload[0] = PAYLOAD_LEN * 2; 1290 + correct_payload[1] = PAYLOAD_LEN; 1291 + correct_payload[2] = PAYLOAD_LEN; 1292 + check_recv_pkts(rxfd, correct_payload, 3); 1293 + } else if (strcmp(testname, "ip_v6ext_same") == 0) { 1294 + printf("ipv6 with ext header does coalesce: "); 1295 + correct_payload[0] = PAYLOAD_LEN * 2; 1296 + check_recv_pkts(rxfd, correct_payload, 1); 1297 + } else if (strcmp(testname, "ip_v6ext_diff") == 0) { 1298 + printf("ipv6 with ext header with different payloads doesn't coalesce: "); 1299 + correct_payload[0] = PAYLOAD_LEN; 1300 + correct_payload[1] = PAYLOAD_LEN; 1301 + check_recv_pkts(rxfd, correct_payload, 2); 1268 1302 1269 - printf("fragmented ip4 doesn't coalesce: "); 1270 - check_recv_pkts(rxfd, correct_payload, 2); 1271 - 1272 - /* is_atomic checks */ 1273 - printf("DF=1, Incrementing - should coalesce: "); 1274 - correct_payload[0] = PAYLOAD_LEN * 2; 1275 - check_recv_pkts(rxfd, correct_payload, 1); 1276 - 1277 - printf("DF=1, Fixed - should coalesce: "); 1278 - correct_payload[0] = PAYLOAD_LEN * 2; 1279 - check_recv_pkts(rxfd, correct_payload, 1); 1280 - 1281 - printf("DF=0, Incrementing - should coalesce: "); 1282 - correct_payload[0] = PAYLOAD_LEN * 2; 1283 - check_recv_pkts(rxfd, correct_payload, 1); 1284 - 1285 - printf("DF=0, Fixed - should coalesce: "); 1286 - correct_payload[0] = PAYLOAD_LEN * 2; 1287 - check_recv_pkts(rxfd, correct_payload, 1); 1288 - 1289 - printf("DF=1, 2 Incrementing and one fixed - should coalesce only first 2 packets: "); 1290 - correct_payload[0] = PAYLOAD_LEN * 2; 1291 - correct_payload[1] = PAYLOAD_LEN; 1292 - check_recv_pkts(rxfd, correct_payload, 2); 1293 - 1294 - printf("DF=1, 2 Fixed and one incrementing - should coalesce only first 2 packets: "); 1295 - correct_payload[0] = PAYLOAD_LEN * 2; 1296 - correct_payload[1] = PAYLOAD_LEN; 1297 - check_recv_pkts(rxfd, correct_payload, 2); 1298 - } else if (proto == PF_INET6) { 1299 - /* GRO doesn't check for ipv6 hop limit when flushing. 1300 - * Hence no corresponding test to the ipv4 case. 1301 - */ 1302 - printf("fragmented ip6 doesn't coalesce: "); 1303 - correct_payload[0] = PAYLOAD_LEN * 2; 1304 - correct_payload[1] = PAYLOAD_LEN; 1305 - correct_payload[2] = PAYLOAD_LEN; 1306 - check_recv_pkts(rxfd, correct_payload, 3); 1307 - 1308 - printf("ipv6 with ext header does coalesce: "); 1309 - correct_payload[0] = PAYLOAD_LEN * 2; 1310 - check_recv_pkts(rxfd, correct_payload, 1); 1311 - 1312 - printf("ipv6 with ext header with different payloads doesn't coalesce: "); 1313 - correct_payload[0] = PAYLOAD_LEN; 1314 - correct_payload[1] = PAYLOAD_LEN; 1315 - check_recv_pkts(rxfd, correct_payload, 2); 1316 - } 1317 - } else if (strcmp(testname, "large") == 0) { 1303 + /* large sub-tests */ 1304 + } else if (strcmp(testname, "large_max") == 0) { 1318 1305 int offset = (proto == PF_INET && !ipip) ? 20 : 0; 1319 1306 int remainder = (MAX_PAYLOAD + offset) % MSS; 1320 1307 ··· 1349 1282 correct_payload[1] = remainder; 1350 1283 printf("Shouldn't coalesce if exceed IP max pkt size: "); 1351 1284 check_recv_pkts(rxfd, correct_payload, 2); 1285 + } else if (strcmp(testname, "large_rem") == 0) { 1286 + int offset = (proto == PF_INET && !ipip) ? 20 : 0; 1287 + int remainder = (MAX_PAYLOAD + offset) % MSS; 1352 1288 1353 1289 /* last segment sent individually, doesn't start new segment */ 1354 - correct_payload[0] = correct_payload[0] - remainder; 1290 + correct_payload[0] = (MAX_PAYLOAD + offset) - remainder; 1355 1291 correct_payload[1] = remainder + 1; 1356 1292 correct_payload[2] = remainder + 1; 1293 + printf("last segment sent individually: "); 1357 1294 check_recv_pkts(rxfd, correct_payload, 3); 1358 1295 } else { 1359 - error(1, 0, "Test case error, should never trigger"); 1296 + error(1, 0, "Test case error: unknown testname %s", testname); 1360 1297 } 1361 1298 1362 1299 if (close(rxfd))
+140 -23
tools/testing/selftests/drivers/net/gro.py
··· 9 9 coalescing behavior. 10 10 11 11 Test cases: 12 - - data: Data packets with same size/headers and correct seq numbers coalesce 12 + - data_same: Same size data packets coalesce 13 + - data_lrg_sml: Large packet followed by smaller one coalesces 14 + - data_sml_lrg: Small packet followed by larger one doesn't coalesce 13 15 - ack: Pure ACK packets do not coalesce 14 - - flags: Packets with PSH, SYN, URG, RST flags do not coalesce 15 - - tcp: Packets with incorrect checksum, non-consecutive seqno don't coalesce 16 - - ip: Packets with different ECN, TTL, TOS, or IP options don't coalesce 17 - - large: Packets larger than GRO_MAX_SIZE don't coalesce 16 + - flags_psh: Packets with PSH flag don't coalesce 17 + - flags_syn: Packets with SYN flag don't coalesce 18 + - flags_rst: Packets with RST flag don't coalesce 19 + - flags_urg: Packets with URG flag don't coalesce 20 + - tcp_csum: Packets with incorrect checksum don't coalesce 21 + - tcp_seq: Packets with non-consecutive seqno don't coalesce 22 + - tcp_ts: Packets with different timestamp options don't coalesce 23 + - tcp_opt: Packets with different TCP options don't coalesce 24 + - ip_ecn: Packets with different ECN don't coalesce 25 + - ip_tos: Packets with different TOS don't coalesce 26 + - ip_ttl: (IPv4) Packets with different TTL don't coalesce 27 + - ip_opt: (IPv4) Packets with IP options don't coalesce 28 + - ip_frag4: (IPv4) IPv4 fragments don't coalesce 29 + - ip_id_df*: (IPv4) IP ID field coalescing tests 30 + - ip_frag6: (IPv6) IPv6 fragments don't coalesce 31 + - ip_v6ext_same: (IPv6) IPv6 ext header with same payload coalesces 32 + - ip_v6ext_diff: (IPv6) IPv6 ext header with different payload doesn't coalesce 33 + - large_max: Packets exceeding GRO_MAX_SIZE don't coalesce 34 + - large_rem: Large packet remainder handling 18 35 """ 19 36 20 37 import os 21 38 from lib.py import ksft_run, ksft_exit, ksft_pr 22 39 from lib.py import NetDrvEpEnv, KsftXfailEx 23 - from lib.py import cmd, defer, bkg, ip 40 + from lib.py import bkg, cmd, defer, ethtool, ip 24 41 from lib.py import ksft_variants 25 42 26 43 ··· 87 70 defer(ip, f"link set dev {dev['ifname']} mtu {dev['mtu']}", host=host) 88 71 89 72 90 - def _setup(cfg, test_name): 73 + def _set_ethtool_feat(dev, current, feats, host=None): 74 + s2n = {True: "on", False: "off"} 75 + 76 + new = ["-K", dev] 77 + old = ["-K", dev] 78 + no_change = True 79 + for name, state in feats.items(): 80 + new += [name, s2n[state]] 81 + old += [name, s2n[current[name]["active"]]] 82 + 83 + if current[name]["active"] != state: 84 + no_change = False 85 + if current[name]["fixed"]: 86 + raise KsftXfailEx(f"Device does not support {name}") 87 + if no_change: 88 + return 89 + 90 + eth_cmd = ethtool(" ".join(new), host=host) 91 + defer(ethtool, " ".join(old), host=host) 92 + 93 + # If ethtool printed something kernel must have modified some features 94 + if eth_cmd.stdout: 95 + ksft_pr(eth_cmd) 96 + 97 + 98 + def _setup(cfg, mode, test_name): 91 99 """ Setup hardware loopback mode for GRO testing. """ 92 100 93 101 if not hasattr(cfg, "bin_remote"): 94 102 cfg.bin_local = cfg.test_dir / "gro" 95 103 cfg.bin_remote = cfg.remote.deploy(cfg.bin_local) 96 104 97 - # "large" test needs at least 4k MTU 98 - if test_name == "large": 105 + if not hasattr(cfg, "feat"): 106 + cfg.feat = ethtool(f"-k {cfg.ifname}", json=True)[0] 107 + cfg.remote_feat = ethtool(f"-k {cfg.remote_ifname}", 108 + host=cfg.remote, json=True)[0] 109 + 110 + # "large_*" tests need at least 4k MTU 111 + if test_name.startswith("large_"): 99 112 _set_mtu_restore(cfg.dev, 4096, None) 100 113 _set_mtu_restore(cfg.remote_dev, 4096, cfg.remote) 101 114 102 - flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout" 103 - irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs" 115 + if mode == "sw": 116 + flush_path = f"/sys/class/net/{cfg.ifname}/gro_flush_timeout" 117 + irq_path = f"/sys/class/net/{cfg.ifname}/napi_defer_hard_irqs" 104 118 105 - _write_defer_restore(cfg, flush_path, "200000", defer_undo=True) 106 - _write_defer_restore(cfg, irq_path, "10", defer_undo=True) 119 + _write_defer_restore(cfg, flush_path, "200000", defer_undo=True) 120 + _write_defer_restore(cfg, irq_path, "10", defer_undo=True) 121 + 122 + _set_ethtool_feat(cfg.ifname, cfg.feat, 123 + {"generic-receive-offload": True, 124 + "rx-gro-hw": False, 125 + "large-receive-offload": False}) 126 + elif mode == "hw": 127 + _set_ethtool_feat(cfg.ifname, cfg.feat, 128 + {"generic-receive-offload": False, 129 + "rx-gro-hw": True, 130 + "large-receive-offload": False}) 131 + 132 + # Some NICs treat HW GRO as a GRO sub-feature so disabling GRO 133 + # will also clear HW GRO. Use a hack of installing XDP generic 134 + # to skip SW GRO, even when enabled. 135 + feat = ethtool(f"-k {cfg.ifname}", json=True)[0] 136 + if not feat["rx-gro-hw"]["active"]: 137 + ksft_pr("Driver clears HW GRO and SW GRO is cleared, using generic XDP workaround") 138 + prog = cfg.net_lib_dir / "xdp_dummy.bpf.o" 139 + ip(f"link set dev {cfg.ifname} xdpgeneric obj {prog} sec xdp") 140 + defer(ip, f"link set dev {cfg.ifname} xdpgeneric off") 141 + 142 + # Attaching XDP may change features, fetch the latest state 143 + feat = ethtool(f"-k {cfg.ifname}", json=True)[0] 144 + 145 + _set_ethtool_feat(cfg.ifname, feat, 146 + {"generic-receive-offload": True, 147 + "rx-gro-hw": True, 148 + "large-receive-offload": False}) 149 + elif mode == "lro": 150 + # netdevsim advertises LRO for feature inheritance testing with 151 + # bonding/team tests but it doesn't actually perform the offload 152 + cfg.require_nsim(nsim_test=False) 153 + 154 + _set_ethtool_feat(cfg.ifname, cfg.feat, 155 + {"generic-receive-offload": False, 156 + "rx-gro-hw": False, 157 + "large-receive-offload": True}) 107 158 108 159 try: 109 160 # Disable TSO for local tests 110 161 cfg.require_nsim() # will raise KsftXfailEx if not running on nsim 111 162 112 - cmd(f"ethtool -K {cfg.ifname} gro on tso off") 113 - cmd(f"ethtool -K {cfg.remote_ifname} gro on tso off", host=cfg.remote) 163 + _set_ethtool_feat(cfg.remote_ifname, cfg.remote_feat, 164 + {"tcp-segmentation-offload": False}, 165 + host=cfg.remote) 114 166 except KsftXfailEx: 115 167 pass 168 + 116 169 117 170 def _gro_variants(): 118 171 """Generator that yields all combinations of protocol and test types.""" 119 172 120 - for protocol in ["ipv4", "ipv6", "ipip"]: 121 - for test_name in ["data", "ack", "flags", "tcp", "ip", "large"]: 122 - yield protocol, test_name 173 + # Tests that work for all protocols 174 + common_tests = [ 175 + "data_same", "data_lrg_sml", "data_sml_lrg", 176 + "ack", 177 + "flags_psh", "flags_syn", "flags_rst", "flags_urg", 178 + "tcp_csum", "tcp_seq", "tcp_ts", "tcp_opt", 179 + "ip_ecn", "ip_tos", 180 + "large_max", "large_rem", 181 + ] 182 + 183 + # Tests specific to IPv4 184 + ipv4_tests = [ 185 + "ip_ttl", "ip_opt", "ip_frag4", 186 + "ip_id_df1_inc", "ip_id_df1_fixed", 187 + "ip_id_df0_inc", "ip_id_df0_fixed", 188 + "ip_id_df1_inc_fixed", "ip_id_df1_fixed_inc", 189 + ] 190 + 191 + # Tests specific to IPv6 192 + ipv6_tests = [ 193 + "ip_frag6", "ip_v6ext_same", "ip_v6ext_diff", 194 + ] 195 + 196 + for mode in ["sw", "hw", "lro"]: 197 + for protocol in ["ipv4", "ipv6", "ipip"]: 198 + for test_name in common_tests: 199 + yield mode, protocol, test_name 200 + 201 + if protocol in ["ipv4", "ipip"]: 202 + for test_name in ipv4_tests: 203 + yield mode, protocol, test_name 204 + elif protocol == "ipv6": 205 + for test_name in ipv6_tests: 206 + yield mode, protocol, test_name 123 207 124 208 125 209 @ksft_variants(_gro_variants()) 126 - def test(cfg, protocol, test_name): 210 + def test(cfg, mode, protocol, test_name): 127 211 """Run a single GRO test with retries.""" 128 212 129 213 ipver = "6" if protocol[-1] == "6" else "4" 130 214 cfg.require_ipver(ipver) 131 215 132 - _setup(cfg, test_name) 216 + _setup(cfg, mode, test_name) 133 217 134 218 base_cmd_args = [ 135 219 f"--{protocol}", ··· 260 142 if rx_proc.ret == 0: 261 143 return 262 144 263 - ksft_pr(rx_proc.stdout.strip().replace('\n', '\n# ')) 264 - ksft_pr(rx_proc.stderr.strip().replace('\n', '\n# ')) 145 + ksft_pr(rx_proc) 265 146 266 - if test_name == "large" and os.environ.get("KSFT_MACHINE_SLOW"): 147 + if test_name.startswith("large_") and os.environ.get("KSFT_MACHINE_SLOW"): 267 148 ksft_pr(f"Ignoring {protocol}/{test_name} failure due to slow environment") 268 149 return 269 150
+5 -2
tools/testing/selftests/drivers/net/lib/py/env.py
··· 248 248 if not self.addr_v[ipver] or not self.remote_addr_v[ipver]: 249 249 raise KsftSkipEx(f"Test requires IPv{ipver} connectivity") 250 250 251 - def require_nsim(self): 252 - if self._ns is None: 251 + def require_nsim(self, nsim_test=True): 252 + """Require or exclude netdevsim for this test""" 253 + if nsim_test and self._ns is None: 253 254 raise KsftXfailEx("Test only works on netdevsim") 255 + if nsim_test is False and self._ns is not None: 256 + raise KsftXfailEx("Test does not work on netdevsim") 254 257 255 258 def _require_cmd(self, comm, key, host=None): 256 259 cached = self._required_cmd.get(comm, {})
+19 -10
tools/testing/selftests/net/lib/py/ksft.py
··· 32 32 33 33 34 34 def ksft_pr(*objs, **kwargs): 35 + """ 36 + Print logs to stdout. 37 + 38 + Behaves like print() but log lines will be prefixed 39 + with # to prevent breaking the TAP output formatting. 40 + 41 + Extra arguments (on top of what print() supports): 42 + line_pfx - add extra string before each line 43 + """ 44 + sep = kwargs.pop("sep", " ") 45 + pfx = kwargs.pop("line_pfx", "") 46 + pfx = "#" + (" " + pfx if pfx else "") 35 47 kwargs["flush"] = True 36 - print("#", *objs, **kwargs) 48 + 49 + text = sep.join(str(obj) for obj in objs) 50 + prefixed = f"\n{pfx} ".join(text.split('\n')) 51 + print(pfx, prefixed, **kwargs) 37 52 38 53 39 54 def _fail(*args): ··· 185 170 entry.exec_only() 186 171 except Exception: 187 172 ksft_pr(f"Exception while handling defer / cleanup (callback {i} of {qlen_start})!") 188 - tb = traceback.format_exc() 189 - for line in tb.strip().split('\n'): 190 - ksft_pr("Defer Exception|", line) 173 + ksft_pr(traceback.format_exc(), line_pfx="Defer Exception|") 191 174 KSFT_RESULT = False 192 175 193 176 ··· 344 331 cnt_key = 'xfail' 345 332 except BaseException as e: 346 333 stop |= isinstance(e, KeyboardInterrupt) 347 - tb = traceback.format_exc() 348 - for line in tb.strip().split('\n'): 349 - ksft_pr("Exception|", line) 334 + ksft_pr(traceback.format_exc(), line_pfx="Exception|") 350 335 if stop: 351 336 ksft_pr(f"Stopping tests due to {type(e).__name__}.") 352 337 KSFT_RESULT = False ··· 354 343 try: 355 344 ksft_flush_defer() 356 345 except BaseException as e: 357 - tb = traceback.format_exc() 358 - for line in tb.strip().split('\n'): 359 - ksft_pr("Exception|", line) 346 + ksft_pr(traceback.format_exc(), line_pfx="Exception|") 360 347 if isinstance(e, KeyboardInterrupt): 361 348 ksft_pr() 362 349 ksft_pr("WARN: defer() interrupted, cleanup may be incomplete.")
+23
tools/testing/selftests/net/lib/py/utils.py
··· 41 41 self.ret = None 42 42 self.ksft_term_fd = None 43 43 44 + self.host = host 44 45 self.comm = comm 46 + 45 47 if host: 46 48 self.proc = host.cmd(comm) 47 49 else: ··· 100 98 stderr = stderr[:-1] 101 99 raise CmdExitFailure("Command failed: %s\nSTDOUT: %s\nSTDERR: %s" % 102 100 (self.proc.args, stdout, stderr), self) 101 + 102 + def __repr__(self): 103 + def str_fmt(name, s): 104 + name += ': ' 105 + return (name + s.strip().replace('\n', '\n' + ' ' * len(name))) 106 + 107 + ret = "CMD" 108 + if self.host: 109 + ret += "[remote]" 110 + if self.ret is None: 111 + ret += f" (unterminated): {self.comm}\n" 112 + elif self.ret == 0: 113 + ret += f" (success): {self.comm}\n" 114 + else: 115 + ret += f": {self.comm}\n" 116 + ret += f" EXIT: {self.ret}\n" 117 + if self.stdout: 118 + ret += str_fmt(" STDOUT", self.stdout) + "\n" 119 + if self.stderr: 120 + ret += str_fmt(" STDERR", self.stderr) + "\n" 121 + return ret.strip() 103 122 104 123 105 124 class bkg(cmd):