lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 13 Jun 2022 13:29:59 +0200
From:   Magnus Karlsson <magnus.karlsson@...il.com>
To:     Maciej Fijalkowski <maciej.fijalkowski@...el.com>
Cc:     bpf <bpf@...r.kernel.org>, Alexei Starovoitov <ast@...nel.org>,
        Daniel Borkmann <daniel@...earbox.net>,
        Network Development <netdev@...r.kernel.org>,
        "Karlsson, Magnus" <magnus.karlsson@...el.com>,
        Björn Töpel <bjorn@...nel.org>
Subject: Re: [PATCH bpf-next 07/10] selftests: xsk: add support for executing
 tests on physical device

On Fri, Jun 10, 2022 at 5:36 PM Maciej Fijalkowski
<maciej.fijalkowski@...el.com> wrote:
>
> 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. Xdpxceiver
> 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.

Good one! Nice to see that we can test real NICs with this support.

Acked-by: Magnus Karlsson <magnus.karlsson@...el.com>

> Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@...el.com>
> ---
>  tools/testing/selftests/bpf/test_xsk.sh  |  52 +++++--
>  tools/testing/selftests/bpf/xdpxceiver.c | 189 ++++++++++++++---------
>  tools/testing/selftests/bpf/xdpxceiver.h |   1 +
>  3 files changed, 156 insertions(+), 86 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/test_xsk.sh b/tools/testing/selftests/bpf/test_xsk.sh
> index 567500299231..19b24cce5414 100755
> --- a/tools/testing/selftests/bpf/test_xsk.sh
> +++ b/tools/testing/selftests/bpf/test_xsk.sh
> @@ -73,14 +73,20 @@
>  #
>  # Run and dump packet contents:
>  #   sudo ./test_xsk.sh -D
> +#
> +# Run test suite for physical device in loopback mode
> +#   sudo ./test_xsk.sh -i IFACE
>
>  . xsk_prereqs.sh
>
> -while getopts "vD" flag
> +ETH=""
> +
> +while getopts "vDi:" flag
>  do
>         case "${flag}" in
>                 v) verbose=1;;
>                 D) dump_pkts=1;;
> +               i) ETH=${OPTARG};;
>         esac
>  done
>
> @@ -132,18 +138,25 @@ setup_vethPairs() {
>         ip link set ${VETH0} up
>  }
>
> -validate_root_exec
> -validate_veth_support ${VETH0}
> -validate_ip_utility
> -setup_vethPairs
> -
> -retval=$?
> -if [ $retval -ne 0 ]; then
> -       test_status $retval "${TEST_NAME}"
> -       cleanup_exit ${VETH0} ${VETH1} ${NS1}
> -       exit $retval
> +if [ ! -z $ETH ]; then
> +       VETH0=${ETH}
> +       VETH1=${ETH}
> +       NS1=""
> +else
> +       validate_root_exec
> +       validate_veth_support ${VETH0}
> +       validate_ip_utility
> +       setup_vethPairs
> +
> +       retval=$?
> +       if [ $retval -ne 0 ]; then
> +               test_status $retval "${TEST_NAME}"
> +               cleanup_exit ${VETH0} ${VETH1} ${NS1}
> +               exit $retval
> +       fi
>  fi
>
> +
>  if [[ $verbose -eq 1 ]]; then
>         ARGS+="-v "
>  fi
> @@ -152,26 +165,33 @@ if [[ $dump_pkts -eq 1 ]]; then
>         ARGS="-D "
>  fi
>
> +retval=$?
>  test_status $retval "${TEST_NAME}"
>
>  ## START TESTS
>
>  statusList=()
>
> -TEST_NAME="XSK_SELFTESTS_SOFTIRQ"
> +TEST_NAME="XSK_SELFTESTS_${VETH0}_SOFTIRQ"
>
>  execxdpxceiver
>
> -cleanup_exit ${VETH0} ${VETH1} ${NS1}
> -TEST_NAME="XSK_SELFTESTS_BUSY_POLL"
> +if [ -z $ETH ]; then
> +       cleanup_exit ${VETH0} ${VETH1} ${NS1}
> +fi
> +TEST_NAME="XSK_SELFTESTS_${VETH0}_BUSY_POLL"
>  busy_poll=1
>
> -setup_vethPairs
> +if [ -z $ETH ]; then
> +       setup_vethPairs
> +fi
>  execxdpxceiver
>
>  ## END TESTS
>
> -cleanup_exit ${VETH0} ${VETH1} ${NS1}
> +if [ -z $ETH ]; then
> +       cleanup_exit ${VETH0} ${VETH1} ${NS1}
> +fi
>
>  failures=0
>  echo -e "\nSummary:"
> diff --git a/tools/testing/selftests/bpf/xdpxceiver.c b/tools/testing/selftests/bpf/xdpxceiver.c
> index ad6c92c31026..316f1dd338fc 100644
> --- a/tools/testing/selftests/bpf/xdpxceiver.c
> +++ b/tools/testing/selftests/bpf/xdpxceiver.c
> @@ -296,8 +296,8 @@ static void enable_busy_poll(struct xsk_socket_info *xsk)
>                 exit_with_error(errno);
>  }
>
> -static int xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
> -                               struct ifobject *ifobject, bool shared)
> +static int __xsk_configure_socket(struct xsk_socket_info *xsk, struct xsk_umem_info *umem,
> +                                 struct ifobject *ifobject, bool shared)
>  {
>         struct xsk_socket_config cfg = {};
>         struct xsk_ring_cons *rxr;
> @@ -443,6 +443,9 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
>                 memset(ifobj->umem, 0, sizeof(*ifobj->umem));
>                 ifobj->umem->num_frames = DEFAULT_UMEM_BUFFERS;
>                 ifobj->umem->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
> +               if (ifobj->shared_umem && ifobj->rx_on)
> +                       ifobj->umem->base_addr = DEFAULT_UMEM_BUFFERS *
> +                               XSK_UMEM__DEFAULT_FRAME_SIZE;
>
>                 for (j = 0; j < MAX_SOCKETS; j++) {
>                         memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j]));
> @@ -1101,19 +1104,85 @@ static int validate_tx_invalid_descs(struct ifobject *ifobject)
>         return TEST_PASS;
>  }
>
> +static void xsk_configure_socket(struct test_spec *test, struct ifobject *ifobject,
> +                                struct xsk_umem_info *umem, bool tx)
> +{
> +       int i, ret;
> +
> +       for (i = 0; i < test->nb_sockets; i++) {
> +               bool shared = (ifobject->shared_umem && tx) ? true : !!i;
> +               u32 ctr = 0;
> +
> +               while (ctr++ < SOCK_RECONF_CTR) {
> +                       ret = __xsk_configure_socket(&ifobject->xsk_arr[i], umem,
> +                                                    ifobject, shared);
> +                       if (!ret)
> +                               break;
> +
> +                       /* Retry if it fails as xsk_socket__create() is asynchronous */
> +                       if (ctr >= SOCK_RECONF_CTR)
> +                               exit_with_error(-ret);
> +                       usleep(USLEEP_MAX);
> +               }
> +               if (ifobject->busy_poll)
> +                       enable_busy_poll(&ifobject->xsk_arr[i]);
> +       }
> +}
> +
> +static void thread_common_ops_tx(struct test_spec *test, struct ifobject *ifobject)
> +{
> +       xsk_configure_socket(test, ifobject, test->ifobj_rx->umem, true);
> +       ifobject->xsk = &ifobject->xsk_arr[0];
> +       ifobject->xsk_map_fd = test->ifobj_rx->xsk_map_fd;
> +       memcpy(ifobject->umem, test->ifobj_rx->umem, sizeof(struct xsk_umem_info));
> +}
> +
> +static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream)
> +{
> +       u32 idx = 0, i, buffers_to_fill;
> +       int ret;
> +
> +       if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS)
> +               buffers_to_fill = umem->num_frames;
> +       else
> +               buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS;
> +
> +       ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx);
> +       if (ret != buffers_to_fill)
> +               exit_with_error(ENOSPC);
> +       for (i = 0; i < buffers_to_fill; i++) {
> +               u64 addr;
> +
> +               if (pkt_stream->use_addr_for_fill) {
> +                       struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i);
> +
> +                       if (!pkt)
> +                               break;
> +                       addr = pkt->addr;
> +               } else {
> +                       addr = i * umem->frame_size;
> +               }
> +
> +               *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
> +       }
> +       xsk_ring_prod__submit(&umem->fq, buffers_to_fill);
> +}
> +
>  static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
>  {
>         u64 umem_sz = ifobject->umem->num_frames * ifobject->umem->frame_size;
>         int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
>         int ret, ifindex;
>         void *bufs;
> -       u32 i;
>
>         ifobject->ns_fd = switch_namespace(ifobject->nsname);
>
>         if (ifobject->umem->unaligned_mode)
>                 mmap_flags |= MAP_HUGETLB;
>
> +       if (ifobject->shared_umem)
> +               umem_sz *= 2;
> +
>         bufs = mmap(NULL, umem_sz, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
>         if (bufs == MAP_FAILED)
>                 exit_with_error(errno);
> @@ -1122,24 +1191,9 @@ static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
>         if (ret)
>                 exit_with_error(-ret);
>
> -       for (i = 0; i < test->nb_sockets; i++) {
> -               u32 ctr = 0;
> -
> -               while (ctr++ < SOCK_RECONF_CTR) {
> -                       ret = xsk_configure_socket(&ifobject->xsk_arr[i], ifobject->umem,
> -                                                  ifobject, !!i);
> -                       if (!ret)
> -                               break;
> -
> -                       /* Retry if it fails as xsk_socket__create() is asynchronous */
> -                       if (ctr >= SOCK_RECONF_CTR)
> -                               exit_with_error(-ret);
> -                       usleep(USLEEP_MAX);
> -               }
> +       xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream);
>
> -               if (ifobject->busy_poll)
> -                       enable_busy_poll(&ifobject->xsk_arr[i]);
> -       }
> +       xsk_configure_socket(test, ifobject, ifobject->umem, false);
>
>         ifobject->xsk = &ifobject->xsk_arr[0];
>
> @@ -1159,22 +1213,18 @@ static void thread_common_ops(struct test_spec *test, struct ifobject *ifobject)
>                 exit_with_error(-ret);
>  }
>
> -static void testapp_cleanup_xsk_res(struct ifobject *ifobj)
> -{
> -       print_verbose("Destroying socket\n");
> -       xsk_socket__delete(ifobj->xsk->xsk);
> -       munmap(ifobj->umem->buffer, ifobj->umem->num_frames * ifobj->umem->frame_size);
> -       xsk_umem__delete(ifobj->umem->umem);
> -}
> -
>  static void *worker_testapp_validate_tx(void *arg)
>  {
>         struct test_spec *test = (struct test_spec *)arg;
>         struct ifobject *ifobject = test->ifobj_tx;
>         int err;
>
> -       if (test->current_step == 1)
> -               thread_common_ops(test, ifobject);
> +       if (test->current_step == 1) {
> +               if (!ifobject->shared_umem)
> +                       thread_common_ops(test, ifobject);
> +               else
> +                       thread_common_ops_tx(test, ifobject);
> +       }
>
>         print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts,
>                       ifobject->ifname);
> @@ -1185,53 +1235,23 @@ static void *worker_testapp_validate_tx(void *arg)
>         if (err)
>                 report_failure(test);
>
> -       if (test->total_steps == test->current_step || err)
> -               testapp_cleanup_xsk_res(ifobject);
>         pthread_exit(NULL);
>  }
>
> -static void xsk_populate_fill_ring(struct xsk_umem_info *umem, struct pkt_stream *pkt_stream)
> -{
> -       u32 idx = 0, i, buffers_to_fill;
> -       int ret;
> -
> -       if (umem->num_frames < XSK_RING_PROD__DEFAULT_NUM_DESCS)
> -               buffers_to_fill = umem->num_frames;
> -       else
> -               buffers_to_fill = XSK_RING_PROD__DEFAULT_NUM_DESCS;
> -
> -       ret = xsk_ring_prod__reserve(&umem->fq, buffers_to_fill, &idx);
> -       if (ret != buffers_to_fill)
> -               exit_with_error(ENOSPC);
> -       for (i = 0; i < buffers_to_fill; i++) {
> -               u64 addr;
> -
> -               if (pkt_stream->use_addr_for_fill) {
> -                       struct pkt *pkt = pkt_stream_get_pkt(pkt_stream, i);
> -
> -                       if (!pkt)
> -                               break;
> -                       addr = pkt->addr;
> -               } else {
> -                       addr = i * umem->frame_size;
> -               }
> -
> -               *xsk_ring_prod__fill_addr(&umem->fq, idx++) = addr;
> -       }
> -       xsk_ring_prod__submit(&umem->fq, buffers_to_fill);
> -}
> -
>  static void *worker_testapp_validate_rx(void *arg)
>  {
>         struct test_spec *test = (struct test_spec *)arg;
>         struct ifobject *ifobject = test->ifobj_rx;
>         struct pollfd fds = { };
> +       int id = 0;
>         int err;
>
> -       if (test->current_step == 1)
> +       if (test->current_step == 1) {
>                 thread_common_ops(test, ifobject);
> -
> -       xsk_populate_fill_ring(ifobject->umem, ifobject->pkt_stream);
> +       } else {
> +               bpf_map_delete_elem(ifobject->xsk_map_fd, &id);
> +               xsk_socket__update_xskmap(ifobject->xsk->xsk, ifobject->xsk_map_fd);
> +       }
>
>         fds.fd = xsk_socket__fd(ifobject->xsk->xsk);
>         fds.events = POLLIN;
> @@ -1249,11 +1269,20 @@ static void *worker_testapp_validate_rx(void *arg)
>                 pthread_mutex_unlock(&pacing_mutex);
>         }
>
> -       if (test->total_steps == test->current_step || err)
> -               testapp_cleanup_xsk_res(ifobject);
>         pthread_exit(NULL);
>  }
>
> +static void testapp_clean_xsk_umem(struct ifobject *ifobj)
> +{
> +       u64 umem_sz = ifobj->umem->num_frames * ifobj->umem->frame_size;
> +
> +       if (ifobj->shared_umem)
> +               umem_sz *= 2;
> +
> +       xsk_umem__delete(ifobj->umem->umem);
> +       munmap(ifobj->umem->buffer, umem_sz);
> +}
> +
>  static int testapp_validate_traffic(struct test_spec *test)
>  {
>         struct ifobject *ifobj_tx = test->ifobj_tx;
> @@ -1280,6 +1309,14 @@ static int testapp_validate_traffic(struct test_spec *test)
>         pthread_join(t1, NULL);
>         pthread_join(t0, NULL);
>
> +       if (test->total_steps == test->current_step || test->fail) {
> +               xsk_socket__delete(ifobj_tx->xsk->xsk);
> +               xsk_socket__delete(ifobj_rx->xsk->xsk);
> +               testapp_clean_xsk_umem(ifobj_rx);
> +               if (!ifobj_tx->shared_umem)
> +                       testapp_clean_xsk_umem(ifobj_tx);
> +       }
> +
>         return !!test->fail;
>  }
>
> @@ -1359,9 +1396,9 @@ static void testapp_headroom(struct test_spec *test)
>  static void testapp_stats_rx_dropped(struct test_spec *test)
>  {
>         test_spec_set_name(test, "STAT_RX_DROPPED");
> +       pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0);
>         test->ifobj_rx->umem->frame_headroom = test->ifobj_rx->umem->frame_size -
>                 XDP_PACKET_HEADROOM - MIN_PKT_SIZE * 3;
> -       pkt_stream_replace_half(test, MIN_PKT_SIZE * 4, 0);
>         pkt_stream_receive_half(test);
>         test->ifobj_rx->validation_func = validate_rx_dropped;
>         testapp_validate_traffic(test);
> @@ -1484,6 +1521,11 @@ static void testapp_invalid_desc(struct test_spec *test)
>                 pkts[7].valid = false;
>         }
>
> +       if (test->ifobj_tx->shared_umem) {
> +               pkts[4].addr += UMEM_SIZE;
> +               pkts[5].addr += UMEM_SIZE;
> +       }
> +
>         pkt_stream_generate_custom(test, pkts, ARRAY_SIZE(pkts));
>         testapp_validate_traffic(test);
>         pkt_stream_restore_default(test);
> @@ -1624,7 +1666,6 @@ static void ifobject_delete(struct ifobject *ifobj)
>  {
>         if (ifobj->ns_fd != -1)
>                 close(ifobj->ns_fd);
> -       free(ifobj->umem);
>         free(ifobj->xsk_arr);
>         free(ifobj);
>  }
> @@ -1665,6 +1706,7 @@ int main(int argc, char **argv)
>         int modes = TEST_MODE_SKB + 1;
>         u32 i, j, failed_tests = 0;
>         struct test_spec test;
> +       bool shared_umem;
>
>         /* Use libbpf 1.0 API mode */
>         libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
> @@ -1679,6 +1721,10 @@ int main(int argc, char **argv)
>         setlocale(LC_ALL, "");
>
>         parse_command_line(ifobj_tx, ifobj_rx, argc, argv);
> +       shared_umem = !strcmp(ifobj_tx->ifname, ifobj_rx->ifname);
> +
> +       ifobj_tx->shared_umem = shared_umem;
> +       ifobj_rx->shared_umem = shared_umem;
>
>         if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) {
>                 usage(basename(argv[0]));
> @@ -1715,6 +1761,9 @@ int main(int argc, char **argv)
>
>         pkt_stream_delete(tx_pkt_stream_default);
>         pkt_stream_delete(rx_pkt_stream_default);
> +       free(ifobj_rx->umem);
> +       if (!ifobj_tx->shared_umem)
> +               free(ifobj_tx->umem);
>         ifobject_delete(ifobj_tx);
>         ifobject_delete(ifobj_rx);
>
> diff --git a/tools/testing/selftests/bpf/xdpxceiver.h b/tools/testing/selftests/bpf/xdpxceiver.h
> index ccfc829b2e5e..b7aa6c7cf2be 100644
> --- a/tools/testing/selftests/bpf/xdpxceiver.h
> +++ b/tools/testing/selftests/bpf/xdpxceiver.h
> @@ -149,6 +149,7 @@ struct ifobject {
>         bool busy_poll;
>         bool use_fill_ring;
>         bool release_rx;
> +       bool shared_umem;
>         u8 dst_mac[ETH_ALEN];
>         u8 src_mac[ETH_ALEN];
>  };
> --
> 2.27.0
>

Powered by blists - more mailing lists