mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
Merge branch 'vsock-test-fix-wrong-setsockopt-parameters'
Konstantin Shkolnyy says: ==================== vsock/test: fix wrong setsockopt() parameters Parameters were created using wrong C types, which caused them to be of wrong size on some architectures, causing problems. The problem with SO_RCVLOWAT was found on s390 (big endian), while x86-64 didn't show it. After the fix, all tests pass on s390. Then Stefano Garzarella pointed out that SO_VM_SOCKETS_* calls might have a similar problem, which turned out to be true, hence, the second patch. Changes for v8: - Fix whitespace warnings from "checkpatch.pl --strict" - Add maintainers to Cc: Changes for v7: - Rebase on top of https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git - Add the "net" tags to the subjects Changes for v6: - rework the patch #3 to avoid creating a new file for new functions, and exclude vsock_perf from calling the new functions. - add "Reviewed-by:" to the patch #2. Changes for v5: - in the patch #2 replace the introduced uint64_t with unsigned long long to match documentation - add a patch #3 that verifies every setsockopt() call. Changes for v4: - add "Reviewed-by:" to the first patch, and add a second patch fixing SO_VM_SOCKETS_* calls, which depends on the first one (hence, it's now a patch series.) Changes for v3: - fix the same problem in vsock_perf and update commit message Changes for v2: - add "Fixes:" lines to the commit message ==================== Link: https://patch.msgid.link/20241203150656.287028-1-kshk@linux.ibm.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
0e21a47c83
@ -27,6 +27,7 @@
|
||||
|
||||
#include "timeout.h"
|
||||
#include "control.h"
|
||||
#include "util.h"
|
||||
|
||||
static int control_fd = -1;
|
||||
|
||||
@ -50,7 +51,6 @@ void control_init(const char *control_host,
|
||||
|
||||
for (ai = result; ai; ai = ai->ai_next) {
|
||||
int fd;
|
||||
int val = 1;
|
||||
|
||||
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (fd < 0)
|
||||
@ -65,11 +65,8 @@ void control_init(const char *control_host,
|
||||
break;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&val, sizeof(val)) < 0) {
|
||||
perror("setsockopt");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_int_check(fd, SOL_SOCKET, SO_REUSEADDR, 1,
|
||||
"setsockopt SO_REUSEADDR");
|
||||
|
||||
if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
|
||||
goto next;
|
||||
|
@ -14,16 +14,6 @@
|
||||
|
||||
#include "msg_zerocopy_common.h"
|
||||
|
||||
void enable_so_zerocopy(int fd)
|
||||
{
|
||||
int val = 1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
|
||||
perror("setsockopt");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void vsock_recv_completion(int fd, const bool *zerocopied)
|
||||
{
|
||||
struct sock_extended_err *serr;
|
||||
|
@ -12,7 +12,6 @@
|
||||
#define VSOCK_RECVERR 1
|
||||
#endif
|
||||
|
||||
void enable_so_zerocopy(int fd);
|
||||
void vsock_recv_completion(int fd, const bool *zerocopied);
|
||||
|
||||
#endif /* MSG_ZEROCOPY_COMMON_H */
|
||||
|
@ -651,3 +651,145 @@ void free_test_iovec(const struct iovec *test_iovec,
|
||||
|
||||
free(iovec);
|
||||
}
|
||||
|
||||
/* Set "unsigned long long" socket option and check that it's indeed set */
|
||||
void setsockopt_ull_check(int fd, int level, int optname,
|
||||
unsigned long long val, char const *errmsg)
|
||||
{
|
||||
unsigned long long chkval;
|
||||
socklen_t chklen;
|
||||
int err;
|
||||
|
||||
err = setsockopt(fd, level, optname, &val, sizeof(val));
|
||||
if (err) {
|
||||
fprintf(stderr, "setsockopt err: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chkval = ~val; /* just make storage != val */
|
||||
chklen = sizeof(chkval);
|
||||
|
||||
err = getsockopt(fd, level, optname, &chkval, &chklen);
|
||||
if (err) {
|
||||
fprintf(stderr, "getsockopt err: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (chklen != sizeof(chkval)) {
|
||||
fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
|
||||
chklen);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (chkval != val) {
|
||||
fprintf(stderr, "value mismatch: set %llu got %llu\n", val,
|
||||
chkval);
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
fprintf(stderr, "%s val %llu\n", errmsg, val);
|
||||
exit(EXIT_FAILURE);
|
||||
;
|
||||
}
|
||||
|
||||
/* Set "int" socket option and check that it's indeed set */
|
||||
void setsockopt_int_check(int fd, int level, int optname, int val,
|
||||
char const *errmsg)
|
||||
{
|
||||
int chkval;
|
||||
socklen_t chklen;
|
||||
int err;
|
||||
|
||||
err = setsockopt(fd, level, optname, &val, sizeof(val));
|
||||
if (err) {
|
||||
fprintf(stderr, "setsockopt err: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chkval = ~val; /* just make storage != val */
|
||||
chklen = sizeof(chkval);
|
||||
|
||||
err = getsockopt(fd, level, optname, &chkval, &chklen);
|
||||
if (err) {
|
||||
fprintf(stderr, "getsockopt err: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (chklen != sizeof(chkval)) {
|
||||
fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
|
||||
chklen);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (chkval != val) {
|
||||
fprintf(stderr, "value mismatch: set %d got %d\n", val, chkval);
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
fprintf(stderr, "%s val %d\n", errmsg, val);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void mem_invert(unsigned char *mem, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
mem[i] = ~mem[i];
|
||||
}
|
||||
|
||||
/* Set "timeval" socket option and check that it's indeed set */
|
||||
void setsockopt_timeval_check(int fd, int level, int optname,
|
||||
struct timeval val, char const *errmsg)
|
||||
{
|
||||
struct timeval chkval;
|
||||
socklen_t chklen;
|
||||
int err;
|
||||
|
||||
err = setsockopt(fd, level, optname, &val, sizeof(val));
|
||||
if (err) {
|
||||
fprintf(stderr, "setsockopt err: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* just make storage != val */
|
||||
chkval = val;
|
||||
mem_invert((unsigned char *)&chkval, sizeof(chkval));
|
||||
chklen = sizeof(chkval);
|
||||
|
||||
err = getsockopt(fd, level, optname, &chkval, &chklen);
|
||||
if (err) {
|
||||
fprintf(stderr, "getsockopt err: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (chklen != sizeof(chkval)) {
|
||||
fprintf(stderr, "size mismatch: set %zu got %d\n", sizeof(val),
|
||||
chklen);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (memcmp(&chkval, &val, sizeof(val)) != 0) {
|
||||
fprintf(stderr, "value mismatch: set %ld:%ld got %ld:%ld\n",
|
||||
val.tv_sec, val.tv_usec, chkval.tv_sec, chkval.tv_usec);
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
fail:
|
||||
fprintf(stderr, "%s val %ld:%ld\n", errmsg, val.tv_sec, val.tv_usec);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void enable_so_zerocopy_check(int fd)
|
||||
{
|
||||
setsockopt_int_check(fd, SOL_SOCKET, SO_ZEROCOPY, 1,
|
||||
"setsockopt SO_ZEROCOPY");
|
||||
}
|
||||
|
@ -68,4 +68,11 @@ unsigned long iovec_hash_djb2(const struct iovec *iov, size_t iovnum);
|
||||
struct iovec *alloc_test_iovec(const struct iovec *test_iovec, int iovnum);
|
||||
void free_test_iovec(const struct iovec *test_iovec,
|
||||
struct iovec *iovec, int iovnum);
|
||||
void setsockopt_ull_check(int fd, int level, int optname,
|
||||
unsigned long long val, char const *errmsg);
|
||||
void setsockopt_int_check(int fd, int level, int optname, int val,
|
||||
char const *errmsg);
|
||||
void setsockopt_timeval_check(int fd, int level, int optname,
|
||||
struct timeval val, char const *errmsg);
|
||||
void enable_so_zerocopy_check(int fd);
|
||||
#endif /* UTIL_H */
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
static unsigned int port = DEFAULT_PORT;
|
||||
static unsigned long buf_size_bytes = DEFAULT_BUF_SIZE_BYTES;
|
||||
static unsigned long vsock_buf_bytes = DEFAULT_VSOCK_BUF_BYTES;
|
||||
static unsigned long long vsock_buf_bytes = DEFAULT_VSOCK_BUF_BYTES;
|
||||
static bool zerocopy;
|
||||
|
||||
static void error(const char *s)
|
||||
@ -133,7 +133,7 @@ static float get_gbps(unsigned long bits, time_t ns_delta)
|
||||
((float)ns_delta / NSEC_PER_SEC);
|
||||
}
|
||||
|
||||
static void run_receiver(unsigned long rcvlowat_bytes)
|
||||
static void run_receiver(int rcvlowat_bytes)
|
||||
{
|
||||
unsigned int read_cnt;
|
||||
time_t rx_begin_ns;
|
||||
@ -162,8 +162,8 @@ static void run_receiver(unsigned long rcvlowat_bytes)
|
||||
printf("Run as receiver\n");
|
||||
printf("Listen port %u\n", port);
|
||||
printf("RX buffer %lu bytes\n", buf_size_bytes);
|
||||
printf("vsock buffer %lu bytes\n", vsock_buf_bytes);
|
||||
printf("SO_RCVLOWAT %lu bytes\n", rcvlowat_bytes);
|
||||
printf("vsock buffer %llu bytes\n", vsock_buf_bytes);
|
||||
printf("SO_RCVLOWAT %d bytes\n", rcvlowat_bytes);
|
||||
|
||||
fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
|
||||
@ -251,6 +251,16 @@ static void run_receiver(unsigned long rcvlowat_bytes)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void enable_so_zerocopy(int fd)
|
||||
{
|
||||
int val = 1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
|
||||
perror("setsockopt");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_sender(int peer_cid, unsigned long to_send_bytes)
|
||||
{
|
||||
time_t tx_begin_ns;
|
||||
@ -439,7 +449,7 @@ static long strtolx(const char *arg)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned long to_send_bytes = DEFAULT_TO_SEND_BYTES;
|
||||
unsigned long rcvlowat_bytes = DEFAULT_RCVLOWAT_BYTES;
|
||||
int rcvlowat_bytes = DEFAULT_RCVLOWAT_BYTES;
|
||||
int peer_cid = -1;
|
||||
bool sender = false;
|
||||
|
||||
|
@ -429,7 +429,7 @@ static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
|
||||
|
||||
static void test_seqpacket_msg_bounds_server(const struct test_opts *opts)
|
||||
{
|
||||
unsigned long sock_buf_size;
|
||||
unsigned long long sock_buf_size;
|
||||
unsigned long remote_hash;
|
||||
unsigned long curr_hash;
|
||||
int fd;
|
||||
@ -444,17 +444,13 @@ static void test_seqpacket_msg_bounds_server(const struct test_opts *opts)
|
||||
|
||||
sock_buf_size = SOCK_BUF_SIZE;
|
||||
|
||||
if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
|
||||
&sock_buf_size, sizeof(sock_buf_size))) {
|
||||
perror("setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,
|
||||
sock_buf_size,
|
||||
"setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)");
|
||||
|
||||
if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
|
||||
&sock_buf_size, sizeof(sock_buf_size))) {
|
||||
perror("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
|
||||
sock_buf_size,
|
||||
"setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
|
||||
|
||||
/* Ready to receive data. */
|
||||
control_writeln("SRVREADY");
|
||||
@ -586,10 +582,8 @@ static void test_seqpacket_timeout_client(const struct test_opts *opts)
|
||||
tv.tv_sec = RCVTIMEO_TIMEOUT_SEC;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv)) == -1) {
|
||||
perror("setsockopt(SO_RCVTIMEO)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_timeval_check(fd, SOL_SOCKET, SO_RCVTIMEO, tv,
|
||||
"setsockopt(SO_RCVTIMEO)");
|
||||
|
||||
read_enter_ns = current_nsec();
|
||||
|
||||
@ -634,7 +628,8 @@ static void test_seqpacket_timeout_server(const struct test_opts *opts)
|
||||
|
||||
static void test_seqpacket_bigmsg_client(const struct test_opts *opts)
|
||||
{
|
||||
unsigned long sock_buf_size;
|
||||
unsigned long long sock_buf_size;
|
||||
size_t buf_size;
|
||||
socklen_t len;
|
||||
void *data;
|
||||
int fd;
|
||||
@ -655,13 +650,20 @@ static void test_seqpacket_bigmsg_client(const struct test_opts *opts)
|
||||
|
||||
sock_buf_size++;
|
||||
|
||||
data = malloc(sock_buf_size);
|
||||
/* size_t can be < unsigned long long */
|
||||
buf_size = (size_t)sock_buf_size;
|
||||
if (buf_size != sock_buf_size) {
|
||||
fprintf(stderr, "Returned BUFFER_SIZE too large\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
data = malloc(buf_size);
|
||||
if (!data) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
send_buf(fd, data, sock_buf_size, 0, -EMSGSIZE);
|
||||
send_buf(fd, data, buf_size, 0, -EMSGSIZE);
|
||||
|
||||
control_writeln("CLISENT");
|
||||
|
||||
@ -835,7 +837,7 @@ static void test_stream_poll_rcvlowat_server(const struct test_opts *opts)
|
||||
|
||||
static void test_stream_poll_rcvlowat_client(const struct test_opts *opts)
|
||||
{
|
||||
unsigned long lowat_val = RCVLOWAT_BUF_SIZE;
|
||||
int lowat_val = RCVLOWAT_BUF_SIZE;
|
||||
char buf[RCVLOWAT_BUF_SIZE];
|
||||
struct pollfd fds;
|
||||
short poll_flags;
|
||||
@ -847,11 +849,8 @@ static void test_stream_poll_rcvlowat_client(const struct test_opts *opts)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
|
||||
&lowat_val, sizeof(lowat_val))) {
|
||||
perror("setsockopt(SO_RCVLOWAT)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_int_check(fd, SOL_SOCKET, SO_RCVLOWAT,
|
||||
lowat_val, "setsockopt(SO_RCVLOWAT)");
|
||||
|
||||
control_expectln("SRVSENT");
|
||||
|
||||
@ -1357,9 +1356,10 @@ static void test_stream_rcvlowat_def_cred_upd_client(const struct test_opts *opt
|
||||
static void test_stream_credit_update_test(const struct test_opts *opts,
|
||||
bool low_rx_bytes_test)
|
||||
{
|
||||
size_t recv_buf_size;
|
||||
int recv_buf_size;
|
||||
struct pollfd fds;
|
||||
size_t buf_size;
|
||||
unsigned long long sock_buf_size;
|
||||
void *buf;
|
||||
int fd;
|
||||
|
||||
@ -1371,11 +1371,12 @@ static void test_stream_credit_update_test(const struct test_opts *opts,
|
||||
|
||||
buf_size = RCVLOWAT_CREDIT_UPD_BUF_SIZE;
|
||||
|
||||
if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
|
||||
&buf_size, sizeof(buf_size))) {
|
||||
perror("setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* size_t can be < unsigned long long */
|
||||
sock_buf_size = buf_size;
|
||||
|
||||
setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
|
||||
sock_buf_size,
|
||||
"setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)");
|
||||
|
||||
if (low_rx_bytes_test) {
|
||||
/* Set new SO_RCVLOWAT here. This enables sending credit
|
||||
@ -1384,11 +1385,8 @@ static void test_stream_credit_update_test(const struct test_opts *opts,
|
||||
*/
|
||||
recv_buf_size = 1 + VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
|
||||
&recv_buf_size, sizeof(recv_buf_size))) {
|
||||
perror("setsockopt(SO_RCVLOWAT)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_int_check(fd, SOL_SOCKET, SO_RCVLOWAT,
|
||||
recv_buf_size, "setsockopt(SO_RCVLOWAT)");
|
||||
}
|
||||
|
||||
/* Send one dummy byte here, because 'setsockopt()' above also
|
||||
@ -1430,11 +1428,8 @@ static void test_stream_credit_update_test(const struct test_opts *opts,
|
||||
recv_buf_size++;
|
||||
|
||||
/* Updating SO_RCVLOWAT will send credit update. */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT,
|
||||
&recv_buf_size, sizeof(recv_buf_size))) {
|
||||
perror("setsockopt(SO_RCVLOWAT)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
setsockopt_int_check(fd, SOL_SOCKET, SO_RCVLOWAT,
|
||||
recv_buf_size, "setsockopt(SO_RCVLOWAT)");
|
||||
}
|
||||
|
||||
fds.fd = fd;
|
||||
|
@ -162,7 +162,7 @@ static void test_client(const struct test_opts *opts,
|
||||
}
|
||||
|
||||
if (test_data->so_zerocopy)
|
||||
enable_so_zerocopy(fd);
|
||||
enable_so_zerocopy_check(fd);
|
||||
|
||||
iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);
|
||||
|
||||
|
@ -73,7 +73,7 @@ static void vsock_io_uring_client(const struct test_opts *opts,
|
||||
}
|
||||
|
||||
if (msg_zerocopy)
|
||||
enable_so_zerocopy(fd);
|
||||
enable_so_zerocopy_check(fd);
|
||||
|
||||
iovec = alloc_test_iovec(test_data->vecs, test_data->vecs_cnt);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user