Path: blob/master/tools/testing/vsock/vsock_test_zerocopy.c
26282 views
// SPDX-License-Identifier: GPL-2.0-only1/* MSG_ZEROCOPY feature tests for vsock2*3* Copyright (C) 2023 SberDevices.4*5* Author: Arseniy Krasnov <[email protected]>6*/78#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <sys/mman.h>12#include <unistd.h>13#include <poll.h>14#include <linux/errqueue.h>15#include <linux/kernel.h>16#include <errno.h>1718#include "control.h"19#include "vsock_test_zerocopy.h"20#include "msg_zerocopy_common.h"2122#ifndef PAGE_SIZE23#define PAGE_SIZE 409624#endif2526#define VSOCK_TEST_DATA_MAX_IOV 32728struct vsock_test_data {29/* This test case if for SOCK_STREAM only. */30bool stream_only;31/* Data must be zerocopied. This field is checked against32* field 'ee_code' of the 'struct sock_extended_err', which33* contains bit to detect that zerocopy transmission was34* fallbacked to copy mode.35*/36bool zerocopied;37/* Enable SO_ZEROCOPY option on the socket. Without enabled38* SO_ZEROCOPY, every MSG_ZEROCOPY transmission will behave39* like without MSG_ZEROCOPY flag.40*/41bool so_zerocopy;42/* 'errno' after 'sendmsg()' call. */43int sendmsg_errno;44/* Number of valid elements in 'vecs'. */45int vecs_cnt;46struct iovec vecs[VSOCK_TEST_DATA_MAX_IOV];47};4849static struct vsock_test_data test_data_array[] = {50/* Last element has non-page aligned size. */51{52.zerocopied = true,53.so_zerocopy = true,54.sendmsg_errno = 0,55.vecs_cnt = 3,56{57{ NULL, PAGE_SIZE },58{ NULL, PAGE_SIZE },59{ NULL, 200 }60}61},62/* All elements have page aligned base and size. */63{64.zerocopied = true,65.so_zerocopy = true,66.sendmsg_errno = 0,67.vecs_cnt = 3,68{69{ NULL, PAGE_SIZE },70{ NULL, PAGE_SIZE * 2 },71{ NULL, PAGE_SIZE * 3 }72}73},74/* All elements have page aligned base and size. But75* data length is bigger than 64Kb.76*/77{78.zerocopied = true,79.so_zerocopy = true,80.sendmsg_errno = 0,81.vecs_cnt = 3,82{83{ NULL, PAGE_SIZE * 16 },84{ NULL, PAGE_SIZE * 16 },85{ NULL, PAGE_SIZE * 16 }86}87},88/* Middle element has both non-page aligned base and size. */89{90.zerocopied = true,91.so_zerocopy = true,92.sendmsg_errno = 0,93.vecs_cnt = 3,94{95{ NULL, PAGE_SIZE },96{ (void *)1, 100 },97{ NULL, PAGE_SIZE }98}99},100/* Middle element is unmapped. */101{102.zerocopied = false,103.so_zerocopy = true,104.sendmsg_errno = ENOMEM,105.vecs_cnt = 3,106{107{ NULL, PAGE_SIZE },108{ MAP_FAILED, PAGE_SIZE },109{ NULL, PAGE_SIZE }110}111},112/* Valid data, but SO_ZEROCOPY is off. This113* will trigger fallback to copy.114*/115{116.zerocopied = false,117.so_zerocopy = false,118.sendmsg_errno = 0,119.vecs_cnt = 1,120{121{ NULL, PAGE_SIZE }122}123},124/* Valid data, but message is bigger than peer's125* buffer, so this will trigger fallback to copy.126* This test is for SOCK_STREAM only, because127* for SOCK_SEQPACKET, 'sendmsg()' returns EMSGSIZE.128*/129{130.stream_only = true,131.zerocopied = false,132.so_zerocopy = true,133.sendmsg_errno = 0,134.vecs_cnt = 1,135{136{ NULL, 100 * PAGE_SIZE }137}138},139};140141#define POLL_TIMEOUT_MS 100142143static void test_client(const struct test_opts *opts,144const struct vsock_test_data *test_data,145bool sock_seqpacket)146{147struct pollfd fds = { 0 };148struct msghdr msg = { 0 };149ssize_t sendmsg_res;150struct iovec *iovec;151int fd;152153if (sock_seqpacket)154fd = vsock_seqpacket_connect(opts->peer_cid, opts->peer_port);155else156fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);157158if (fd < 0) {159perror("connect");160exit(EXIT_FAILURE);161}162163if (test_data->so_zerocopy)164enable_so_zerocopy_check(fd);165166iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);167168msg.msg_iov = iovec;169msg.msg_iovlen = test_data->vecs_cnt;170171errno = 0;172173sendmsg_res = sendmsg(fd, &msg, MSG_ZEROCOPY);174if (errno != test_data->sendmsg_errno) {175fprintf(stderr, "expected 'errno' == %i, got %i\n",176test_data->sendmsg_errno, errno);177exit(EXIT_FAILURE);178}179180if (!errno) {181if (sendmsg_res != iovec_bytes(iovec, test_data->vecs_cnt)) {182fprintf(stderr, "expected 'sendmsg()' == %li, got %li\n",183iovec_bytes(iovec, test_data->vecs_cnt),184sendmsg_res);185exit(EXIT_FAILURE);186}187}188189fds.fd = fd;190fds.events = 0;191192if (poll(&fds, 1, POLL_TIMEOUT_MS) < 0) {193perror("poll");194exit(EXIT_FAILURE);195}196197if (fds.revents & POLLERR) {198vsock_recv_completion(fd, &test_data->zerocopied);199} else if (test_data->so_zerocopy && !test_data->sendmsg_errno) {200/* If we don't have data in the error queue, but201* SO_ZEROCOPY was enabled and 'sendmsg()' was202* successful - this is an error.203*/204fprintf(stderr, "POLLERR expected\n");205exit(EXIT_FAILURE);206}207208if (!test_data->sendmsg_errno)209control_writeulong(iovec_hash_djb2(iovec, test_data->vecs_cnt));210else211control_writeulong(0);212213control_writeln("DONE");214free_test_iovec(test_data->vecs, iovec, test_data->vecs_cnt);215close(fd);216}217218void test_stream_msgzcopy_client(const struct test_opts *opts)219{220int i;221222for (i = 0; i < ARRAY_SIZE(test_data_array); i++)223test_client(opts, &test_data_array[i], false);224}225226void test_seqpacket_msgzcopy_client(const struct test_opts *opts)227{228int i;229230for (i = 0; i < ARRAY_SIZE(test_data_array); i++) {231if (test_data_array[i].stream_only)232continue;233234test_client(opts, &test_data_array[i], true);235}236}237238static void test_server(const struct test_opts *opts,239const struct vsock_test_data *test_data,240bool sock_seqpacket)241{242unsigned long remote_hash;243unsigned long local_hash;244ssize_t total_bytes_rec;245unsigned char *data;246size_t data_len;247int fd;248249if (sock_seqpacket)250fd = vsock_seqpacket_accept(VMADDR_CID_ANY, opts->peer_port, NULL);251else252fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);253254if (fd < 0) {255perror("accept");256exit(EXIT_FAILURE);257}258259data_len = iovec_bytes(test_data->vecs, test_data->vecs_cnt);260261data = malloc(data_len);262if (!data) {263perror("malloc");264exit(EXIT_FAILURE);265}266267total_bytes_rec = 0;268269while (total_bytes_rec != data_len) {270ssize_t bytes_rec;271272bytes_rec = read(fd, data + total_bytes_rec,273data_len - total_bytes_rec);274if (bytes_rec <= 0)275break;276277total_bytes_rec += bytes_rec;278}279280if (test_data->sendmsg_errno == 0)281local_hash = hash_djb2(data, data_len);282else283local_hash = 0;284285free(data);286287/* Waiting for some result. */288remote_hash = control_readulong();289if (remote_hash != local_hash) {290fprintf(stderr, "hash mismatch\n");291exit(EXIT_FAILURE);292}293294control_expectln("DONE");295close(fd);296}297298void test_stream_msgzcopy_server(const struct test_opts *opts)299{300int i;301302for (i = 0; i < ARRAY_SIZE(test_data_array); i++)303test_server(opts, &test_data_array[i], false);304}305306void test_seqpacket_msgzcopy_server(const struct test_opts *opts)307{308int i;309310for (i = 0; i < ARRAY_SIZE(test_data_array); i++) {311if (test_data_array[i].stream_only)312continue;313314test_server(opts, &test_data_array[i], true);315}316}317318void test_stream_msgzcopy_empty_errq_client(const struct test_opts *opts)319{320struct msghdr msg = { 0 };321char cmsg_data[128];322ssize_t res;323int fd;324325fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);326if (fd < 0) {327perror("connect");328exit(EXIT_FAILURE);329}330331msg.msg_control = cmsg_data;332msg.msg_controllen = sizeof(cmsg_data);333334res = recvmsg(fd, &msg, MSG_ERRQUEUE);335if (res != -1) {336fprintf(stderr, "expected 'recvmsg(2)' failure, got %zi\n",337res);338exit(EXIT_FAILURE);339}340341control_writeln("DONE");342close(fd);343}344345void test_stream_msgzcopy_empty_errq_server(const struct test_opts *opts)346{347int fd;348349fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);350if (fd < 0) {351perror("accept");352exit(EXIT_FAILURE);353}354355control_expectln("DONE");356close(fd);357}358359360