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.

selftests/xsk: Add support for executing tests on physical device

Currently, architecture of xdpxceiver is designed strictly for
conducting veth based tests. Veth pair is created together with a
network namespace and one of the veth interfaces is moved to the
mentioned netns. Then, separate threads for Tx and Rx are spawned which
will utilize described setup.

Infrastructure described in the paragraph above can not be used for
testing AF_XDP support on physical devices. That testing will be
conducted on a single network interface and same queue. Xskxceiver
needs to be extended to distinguish between veth tests and physical
interface tests.

Since same iface/queue id pair will be used by both Tx/Rx threads for
physical device testing, Tx thread, which happen to run after the Rx
thread, is going to create XSK socket with shared umem flag. In order to
track this setting throughout the lifetime of spawned threads, introduce
'shared_umem' boolean variable to struct ifobject and set it to true
when xdpxceiver is run against physical device. In such case, UMEM size
needs to be doubled, so half of it will be used by Rx thread and other
half by Tx thread. For two step based test types, value of XSKMAP
element under key 0 has to be updated as there is now another socket for
the second step. Also, to avoid race conditions when destroying XSK
resources, move this activity to the main thread after spawned Rx and Tx
threads have finished its job. This way it is possible to gracefully
remove shared umem without introducing synchronization mechanisms.

To run xsk selftests suite on physical device, append "-i $IFACE" when
invoking test_xsk.sh. For veth based tests, simply skip it. When "-i
$IFACE" is in place, under the hood test_xsk.sh will use $IFACE for both
interfaces supplied to xdpxceiver, which in turn will interpret that
this execution of test suite is for a physical device.

Note that currently this makes it possible only to test SKB and DRV mode
(in case underlying device has native XDP support). ZC testing support
is added in a later patch.

Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Magnus Karlsson <magnus.karlsson@intel.com>
Link: https://lore.kernel.org/bpf/20220901114813.16275-5-maciej.fijalkowski@intel.com

authored by

Maciej Fijalkowski and committed by
Daniel Borkmann
a693ff3e 24037ba7

+169 -86
+35 -15
tools/testing/selftests/bpf/test_xsk.sh
··· 73 73 # 74 74 # Run and dump packet contents: 75 75 # sudo ./test_xsk.sh -D 76 + # 77 + # Run test suite for physical device in loopback mode 78 + # sudo ./test_xsk.sh -i IFACE 76 79 77 80 . xsk_prereqs.sh 78 81 79 - while getopts "vD" flag 82 + ETH="" 83 + 84 + while getopts "vDi:" flag 80 85 do 81 86 case "${flag}" in 82 87 v) verbose=1;; 83 88 D) dump_pkts=1;; 89 + i) ETH=${OPTARG};; 84 90 esac 85 91 done 86 92 ··· 138 132 ip link set ${VETH0} up 139 133 } 140 134 141 - validate_root_exec 142 - validate_veth_support ${VETH0} 143 - validate_ip_utility 144 - setup_vethPairs 135 + if [ ! -z $ETH ]; then 136 + VETH0=${ETH} 137 + VETH1=${ETH} 138 + NS1="" 139 + else 140 + validate_root_exec 141 + validate_veth_support ${VETH0} 142 + validate_ip_utility 143 + setup_vethPairs 145 144 146 - retval=$? 147 - if [ $retval -ne 0 ]; then 148 - test_status $retval "${TEST_NAME}" 149 - cleanup_exit ${VETH0} ${VETH1} ${NS1} 150 - exit $retval 145 + retval=$? 146 + if [ $retval -ne 0 ]; then 147 + test_status $retval "${TEST_NAME}" 148 + cleanup_exit ${VETH0} ${VETH1} ${NS1} 149 + exit $retval 150 + fi 151 151 fi 152 + 152 153 153 154 if [[ $verbose -eq 1 ]]; then 154 155 ARGS+="-v " ··· 165 152 ARGS="-D " 166 153 fi 167 154 155 + retval=$? 168 156 test_status $retval "${TEST_NAME}" 169 157 170 158 ## START TESTS 171 159 172 160 statusList=() 173 161 174 - TEST_NAME="XSK_SELFTESTS_SOFTIRQ" 162 + TEST_NAME="XSK_SELFTESTS_${VETH0}_SOFTIRQ" 175 163 176 164 exec_xskxceiver 177 165 178 - cleanup_exit ${VETH0} ${VETH1} ${NS1} 179 - TEST_NAME="XSK_SELFTESTS_BUSY_POLL" 166 + if [ -z $ETH ]; then 167 + cleanup_exit ${VETH0} ${VETH1} ${NS1} 168 + fi 169 + TEST_NAME="XSK_SELFTESTS_${VETH0}_BUSY_POLL" 180 170 busy_poll=1 181 171 182 - setup_vethPairs 172 + if [ -z $ETH ]; then 173 + setup_vethPairs 174 + fi 183 175 exec_xskxceiver 184 176 185 177 ## END TESTS 186 178 187 - cleanup_exit ${VETH0} ${VETH1} ${NS1} 179 + if [ -z $ETH ]; then 180 + cleanup_exit ${VETH0} ${VETH1} ${NS1} 181 + fi 188 182 189 183 failures=0 190 184 echo -e "\nSummary:"
+133 -71
tools/testing/selftests/bpf/xskxceiver.c
··· 301 301 exit_with_error(errno); 302 302 } 303 303 304 - static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem, 305 - struct ifobject *ifobject, bool shared) 304 + static int __xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem, 305 + struct ifobject *ifobject, bool shared) 306 306 { 307 307 struct xsk_socket_config cfg = {}; 308 308 struct xsk_ring_cons *rxr; ··· 448 448 memset(ifobj->umem, 0, sizeof(*ifobj->umem)); 449 449 ifobj->umem->num_frames = DEFAULT_UMEM_BUFFERS; 450 450 ifobj->umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; 451 + if (ifobj->shared_umem && ifobj->rx_on) 452 + ifobj->umem->base_addr = DEFAULT_UMEM_BUFFERS * 453 + XSK_UMEM__DEFAULT_FRAME_SIZE; 451 454 452 455 for (j = 0; j < MAX_SOCKETS; j++) { 453 456 memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j])); ··· 1149 1146 return TEST_PASS; 1150 1147 } 1151 1148 1149 + static void xsk_configure_socket(struct test_spec *test, struct ifobject *ifobject, 1150 + struct xsk_umem_info *umem, bool tx) 1151 + { 1152 + int i, ret; 1153 + 1154 + for (i = 0; i < test->nb_sockets; i++) { 1155 + bool shared = (ifobject->shared_umem && tx) ? true : !!i; 1156 + u32 ctr = 0; 1157 + 1158 + while (ctr++ < SOCK_RECONF_CTR) { 1159 + ret = __xsk_configure_socket(&ifobject->xsk_arr[i], umem, 1160 + ifobject, shared); 1161 + if (!ret) 1162 + break; 1163 + 1164 + /* Retry if it fails as xsk_socket__create() is asynchronous */ 1165 + if (ctr >= SOCK_RECONF_CTR) 1166 + exit_with_error(-ret); 1167 + usleep(USLEEP_MAX); 1168 + } 1169 + if (ifobject->busy_poll) 1170 + enable_busy_poll(&ifobject->xsk_arr[i]); 1171 + } 1172 + } 1173 + 1174 + static void thread_common_ops_tx(struct test_spec *test, struct ifobject *ifobject) 1175 + { 1176 + xsk_configure_socket(test, ifobject, test->ifobj_rx->umem, true); 1177 + ifobject->xsk = &ifobject->xsk_arr[0]; 1178 + ifobject->xsk_map_fd = test->ifobj_rx->xsk_map_fd; 1179 + memcpy(ifobject->umem, test->ifobj_rx->umem, sizeof(struct xsk_umem_info)); 1180 + } 1181 + 1182 + static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream) 1183 + { 1184 + u32 idx = 0, i, buffers_to_fill; 1185 + int ret; 1186 + 1187 + if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS) 1188 + buffers_to_fill = umem->num_frames; 1189 + else 1190 + buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS; 1191 + 1192 + ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx); 1193 + if (ret != buffers_to_fill) 1194 + exit_with_error(ENOSPC); 1195 + for (i = 0; i < buffers_to_fill; i++) { 1196 + u64 addr; 1197 + 1198 + if (pkt_stream->use_addr_for_fill) { 1199 + struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i); 1200 + 1201 + if (!pkt) 1202 + break; 1203 + addr = pkt->addr; 1204 + } else { 1205 + addr = i * umem->frame_size; 1206 + } 1207 + 1208 + *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr; 1209 + } 1210 + xsk_ring_prod__submit(&umem->fq, buffers_to_fill); 1211 + } 1212 + 1152 1213 static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject) 1153 1214 { 1154 1215 u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size; ··· 1220 1153 LIBBPF_OPTS(bpf_xdp_query_opts, opts); 1221 1154 int ret, ifindex; 1222 1155 void *bufs; 1223 - u32 i; 1224 1156 1225 1157 ifobject->ns_fd = switch_namespace(ifobject->nsname); 1226 1158 1227 1159 if (ifobject->umem->unaligned_mode) 1228 1160 mmap_flags |= MAP_HUGETLB; 1161 + 1162 + if (ifobject->shared_umem) 1163 + umem_sz *= 2; 1229 1164 1230 1165 bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0); 1231 1166 if (bufs == MAP_FAILED) ··· 1237 1168 if (ret) 1238 1169 exit_with_error(-ret); 1239 1170 1240 - for (i = 0; i < test->nb_sockets; i++) { 1241 - u32 ctr = 0; 1171 + xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream); 1242 1172 1243 - while (ctr++ < SOCK_RECONF_CTR) { 1244 - ret = xsk_configure_socket(&ifobject->xsk_arr[i], ifobject->umem, 1245 - ifobject, !!i); 1246 - if (!ret) 1247 - break; 1248 - 1249 - /* Retry if it fails as xsk_socket__create() is asynchronous */ 1250 - if (ctr >= SOCK_RECONF_CTR) 1251 - exit_with_error(-ret); 1252 - usleep(USLEEP_MAX); 1253 - } 1254 - 1255 - if (ifobject->busy_poll) 1256 - enable_busy_poll(&ifobject->xsk_arr[i]); 1257 - } 1173 + xsk_configure_socket(test, ifobject, ifobject->umem, false); 1258 1174 1259 1175 ifobject->xsk = &ifobject->xsk_arr[0]; 1260 1176 ··· 1275 1221 exit_with_error(-ret); 1276 1222 } 1277 1223 1278 - static void testapp_cleanup_xsk_res(struct ifobject *ifobj) 1279 - { 1280 - print_verbose("Destroying socket\n"); 1281 - xsk_socket__delete(ifobj->xsk->xsk); 1282 - munmap(ifobj->umem->buffer, ifobj->umem->num_frames * ifobj->umem->frame_size); 1283 - xsk_umem__delete(ifobj->umem->umem); 1284 - } 1285 - 1286 1224 static void *worker_testapp_validate_tx(void *arg) 1287 1225 { 1288 1226 struct test_spec *test = (struct test_spec *)arg; 1289 1227 struct ifobject *ifobject = test->ifobj_tx; 1290 1228 int err; 1291 1229 1292 - if (test->current_step == 1) 1293 - thread_common_ops(test, ifobject); 1230 + if (test->current_step == 1) { 1231 + if (!ifobject->shared_umem) 1232 + thread_common_ops(test, ifobject); 1233 + else 1234 + thread_common_ops_tx(test, ifobject); 1235 + } 1294 1236 1295 1237 print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts, 1296 1238 ifobject->ifname); ··· 1297 1247 if (err) 1298 1248 report_failure(test); 1299 1249 1300 - if (test->total_steps == test->current_step || err) 1301 - testapp_cleanup_xsk_res(ifobject); 1302 1250 pthread_exit(NULL); 1303 - } 1304 - 1305 - static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream) 1306 - { 1307 - u32 idx = 0, i, buffers_to_fill; 1308 - int ret; 1309 - 1310 - if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS) 1311 - buffers_to_fill = umem->num_frames; 1312 - else 1313 - buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS; 1314 - 1315 - ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx); 1316 - if (ret != buffers_to_fill) 1317 - exit_with_error(ENOSPC); 1318 - for (i = 0; i < buffers_to_fill; i++) { 1319 - u64 addr; 1320 - 1321 - if (pkt_stream->use_addr_for_fill) { 1322 - struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i); 1323 - 1324 - if (!pkt) 1325 - break; 1326 - addr = pkt->addr; 1327 - } else { 1328 - addr = i * umem->frame_size; 1329 - } 1330 - 1331 - *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr; 1332 - } 1333 - xsk_ring_prod__submit(&umem->fq, buffers_to_fill); 1334 1251 } 1335 1252 1336 1253 static void *worker_testapp_validate_rx(void *arg) ··· 1305 1288 struct test_spec *test = (struct test_spec *)arg; 1306 1289 struct ifobject *ifobject = test->ifobj_rx; 1307 1290 struct pollfd fds = { }; 1291 + int id = 0; 1308 1292 int err; 1309 1293 1310 - if (test->current_step == 1) 1294 + if (test->current_step == 1) { 1311 1295 thread_common_ops(test, ifobject); 1312 - 1313 - xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream); 1296 + } else { 1297 + bpf_map_delete_elem(ifobject->xsk_map_fd, &id); 1298 + xsk_socket__update_xskmap(ifobject->xsk->xsk, ifobject->xsk_map_fd); 1299 + } 1314 1300 1315 1301 fds.fd = xsk_socket__fd(ifobject->xsk->xsk); 1316 1302 fds.events = POLLIN; ··· 1331 1311 pthread_mutex_unlock(&pacing_mutex); 1332 1312 } 1333 1313 1334 - if (test->total_steps == test->current_step || err) 1335 - testapp_cleanup_xsk_res(ifobject); 1336 1314 pthread_exit(NULL); 1315 + } 1316 + 1317 + static void testapp_clean_xsk_umem(struct ifobject *ifobj) 1318 + { 1319 + u64 umem_sz = ifobj->umem->num_frames * ifobj->umem->frame_size; 1320 + 1321 + if (ifobj->shared_umem) 1322 + umem_sz *= 2; 1323 + 1324 + xsk_umem__delete(ifobj->umem->umem); 1325 + munmap(ifobj->umem->buffer, umem_sz); 1337 1326 } 1338 1327 1339 1328 static int testapp_validate_traffic_single_thread(struct test_spec *test, struct ifobject *ifobj, 1340 1329 enum test_type type) 1341 1330 { 1331 + bool old_shared_umem = ifobj->shared_umem; 1342 1332 pthread_t t0; 1343 1333 1344 1334 if (pthread_barrier_init(&barr, NULL, 2)) 1345 1335 exit_with_error(errno); 1346 1336 1347 1337 test->current_step++; 1348 - if (type == TEST_TYPE_POLL_RXQ_TMOUT) 1338 + if (type == TEST_TYPE_POLL_RXQ_TMOUT) 1349 1339 pkt_stream_reset(ifobj->pkt_stream); 1350 1340 pkts_in_flight = 0; 1351 1341 1352 - /*Spawn thread */ 1342 + test->ifobj_rx->shared_umem = false; 1343 + test->ifobj_tx->shared_umem = false; 1344 + 1345 + /* Spawn thread */ 1353 1346 pthread_create(&t0, NULL, ifobj->func_ptr, test); 1354 1347 1355 1348 if (type != TEST_TYPE_POLL_TXQ_TMOUT) ··· 1372 1339 exit_with_error(errno); 1373 1340 1374 1341 pthread_join(t0, NULL); 1342 + 1343 + if (test->total_steps == test->current_step || test->fail) { 1344 + xsk_socket__delete(ifobj->xsk->xsk); 1345 + testapp_clean_xsk_umem(ifobj); 1346 + } 1347 + 1348 + test->ifobj_rx->shared_umem = old_shared_umem; 1349 + test->ifobj_tx->shared_umem = old_shared_umem; 1375 1350 1376 1351 return !!test->fail; 1377 1352 } ··· 1409 1368 1410 1369 pthread_join(t1, NULL); 1411 1370 pthread_join(t0, NULL); 1371 + 1372 + if (test->total_steps == test->current_step || test->fail) { 1373 + xsk_socket__delete(ifobj_tx->xsk->xsk); 1374 + xsk_socket__delete(ifobj_rx->xsk->xsk); 1375 + testapp_clean_xsk_umem(ifobj_rx); 1376 + if (!ifobj_tx->shared_umem) 1377 + testapp_clean_xsk_umem(ifobj_tx); 1378 + } 1412 1379 1413 1380 return !!test->fail; 1414 1381 } ··· 1497 1448 static void testapp_stats_rx_dropped(struct test_spec *test) 1498 1449 { 1499 1450 test_spec_set_name(test, "STAT_RX_DROPPED"); 1451 + pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0); 1500 1452 test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size - 1501 1453 XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 3; 1502 - pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0); 1503 1454 pkt_stream_receive_half(test); 1504 1455 test->ifobj_rx->validation_func = validate_rx_dropped; 1505 1456 testapp_validate_traffic(test); ··· 1620 1571 if (test->ifobj_tx->umem->frame_size == XSK_UMEM__DEFAULT_FRAME_SIZE / 2) { 1621 1572 /* Crossing a 2K frame size boundrary not allowed */ 1622 1573 pkts[7].valid = false; 1574 + } 1575 + 1576 + if (test->ifobj_tx->shared_umem) { 1577 + pkts[4].addr += UMEM_SIZE; 1578 + pkts[5].addr += UMEM_SIZE; 1623 1579 } 1624 1580 1625 1581 pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts)); ··· 1823 1769 int modes = TEST_MODE_SKB + 1; 1824 1770 u32 i, j, failed_tests = 0; 1825 1771 struct test_spec test; 1772 + bool shared_umem; 1826 1773 1827 1774 /* Use libbpf 1.0 API mode */ 1828 1775 libbpf_set_strict_mode(LIBBPF_STRICT_ALL); ··· 1838 1783 setlocale(LC_ALL, ""); 1839 1784 1840 1785 parse_command_line(ifobj_tx, ifobj_rx, argc, argv); 1786 + shared_umem = !strcmp(ifobj_tx->ifname, ifobj_rx->ifname); 1787 + 1788 + ifobj_tx->shared_umem = shared_umem; 1789 + ifobj_rx->shared_umem = shared_umem; 1841 1790 1842 1791 if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) { 1843 1792 usage(basename(argv[0])); ··· 1878 1819 1879 1820 pkt_stream_delete(tx_pkt_stream_default); 1880 1821 pkt_stream_delete(rx_pkt_stream_default); 1822 + free(ifobj_rx->umem); 1823 + if (!ifobj_tx->shared_umem) 1824 + free(ifobj_tx->umem); 1881 1825 ifobject_delete(ifobj_tx); 1882 1826 ifobject_delete(ifobj_rx); 1883 1827
+1
tools/testing/selftests/bpf/xskxceiver.h
··· 153 153 bool busy_poll; 154 154 bool use_fill_ring; 155 155 bool release_rx; 156 + bool shared_umem; 156 157 u8 dst_mac[ETH_ALEN]; 157 158 u8 src_mac[ETH_ALEN]; 158 159 };