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 'vsock-introduce-siocinq-ioctl-support'

Xuewei Niu says:

====================
vsock: Introduce SIOCINQ ioctl support

Introduce SIOCINQ ioctl support for vsock, indicating the length of unread
bytes.

Similar with SIOCOUTQ ioctl, the information is transport-dependent.

The first patch adds SIOCINQ ioctl support in AF_VSOCK.

Thanks to @dexuan, the second patch is to fix the issue where hyper-v
`hvs_stream_has_data()` doesn't return the readable bytes.

The third patch wraps the ioctl into `ioctl_int()`, which implements a
retry mechanism to prevent immediate failure.

The last one adds two test cases to check the functionality. The changes
have been tested, and the results are as expected.
====================

Link: https://patch.msgid.link/20250708-siocinq-v6-0-3775f9a9e359@antgroup.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+145 -20
+22
net/vmw_vsock/af_vsock.c
··· 1389 1389 vsk = vsock_sk(sk); 1390 1390 1391 1391 switch (cmd) { 1392 + case SIOCINQ: { 1393 + ssize_t n_bytes; 1394 + 1395 + if (!vsk->transport) { 1396 + ret = -EOPNOTSUPP; 1397 + break; 1398 + } 1399 + 1400 + if (sock_type_connectible(sk->sk_type) && 1401 + sk->sk_state == TCP_LISTEN) { 1402 + ret = -EINVAL; 1403 + break; 1404 + } 1405 + 1406 + n_bytes = vsock_stream_has_data(vsk); 1407 + if (n_bytes < 0) { 1408 + ret = n_bytes; 1409 + break; 1410 + } 1411 + ret = put_user(n_bytes, arg); 1412 + break; 1413 + } 1392 1414 case SIOCOUTQ: { 1393 1415 ssize_t n_bytes; 1394 1416
+14 -3
net/vmw_vsock/hyperv_transport.c
··· 694 694 static s64 hvs_stream_has_data(struct vsock_sock *vsk) 695 695 { 696 696 struct hvsock *hvs = vsk->trans; 697 + bool need_refill; 697 698 s64 ret; 698 699 699 700 if (hvs->recv_data_len > 0) 700 - return 1; 701 + return hvs->recv_data_len; 701 702 702 703 switch (hvs_channel_readable_payload(hvs->chan)) { 703 704 case 1: 704 - ret = 1; 705 - break; 705 + need_refill = !hvs->recv_desc; 706 + if (!need_refill) 707 + return -EIO; 708 + 709 + hvs->recv_desc = hv_pkt_iter_first(hvs->chan); 710 + if (!hvs->recv_desc) 711 + return -ENOBUFS; 712 + 713 + ret = hvs_update_recv_data(hvs); 714 + if (ret) 715 + return ret; 716 + return hvs->recv_data_len; 706 717 case 0: 707 718 vsk->peer_shutdown |= SEND_SHUTDOWN; 708 719 ret = 0;
+29 -17
tools/testing/vsock/util.c
··· 17 17 #include <unistd.h> 18 18 #include <assert.h> 19 19 #include <sys/epoll.h> 20 + #include <sys/ioctl.h> 20 21 #include <sys/mman.h> 21 22 #include <linux/sockios.h> 22 23 ··· 102 101 close(epollfd); 103 102 } 104 103 104 + /* Wait until ioctl gives an expected int value. 105 + * Return false if the op is not supported. 106 + */ 107 + bool vsock_ioctl_int(int fd, unsigned long op, int expected) 108 + { 109 + int actual, ret; 110 + char name[32]; 111 + 112 + snprintf(name, sizeof(name), "ioctl(%lu)", op); 113 + 114 + timeout_begin(TIMEOUT); 115 + do { 116 + ret = ioctl(fd, op, &actual); 117 + if (ret < 0) { 118 + if (errno == EOPNOTSUPP) 119 + break; 120 + 121 + perror(name); 122 + exit(EXIT_FAILURE); 123 + } 124 + timeout_check(name); 125 + } while (actual != expected); 126 + timeout_end(); 127 + 128 + return ret >= 0; 129 + } 130 + 105 131 /* Wait until transport reports no data left to be sent. 106 132 * Return false if transport does not implement the unsent_bytes() callback. 107 133 */ 108 134 bool vsock_wait_sent(int fd) 109 135 { 110 - int ret, sock_bytes_unsent; 111 - 112 - timeout_begin(TIMEOUT); 113 - do { 114 - ret = ioctl(fd, SIOCOUTQ, &sock_bytes_unsent); 115 - if (ret < 0) { 116 - if (errno == EOPNOTSUPP) 117 - break; 118 - 119 - perror("ioctl(SIOCOUTQ)"); 120 - exit(EXIT_FAILURE); 121 - } 122 - timeout_check("SIOCOUTQ"); 123 - } while (sock_bytes_unsent != 0); 124 - timeout_end(); 125 - 126 - return !ret; 136 + return vsock_ioctl_int(fd, SIOCOUTQ, 0); 127 137 } 128 138 129 139 /* Create socket <type>, bind to <cid, port>.
+1
tools/testing/vsock/util.h
··· 87 87 int vsock_seqpacket_accept(unsigned int cid, unsigned int port, 88 88 struct sockaddr_vm *clientaddrp); 89 89 void vsock_wait_remote_close(int fd); 90 + bool vsock_ioctl_int(int fd, unsigned long op, int expected); 90 91 bool vsock_wait_sent(int fd); 91 92 void send_buf(int fd, const void *buf, size_t len, int flags, 92 93 ssize_t expected_ret);
+79
tools/testing/vsock/vsock_test.c
··· 24 24 #include <linux/time64.h> 25 25 #include <pthread.h> 26 26 #include <fcntl.h> 27 + #include <linux/sockios.h> 27 28 28 29 #include "vsock_test_zerocopy.h" 29 30 #include "timeout.h" ··· 1308 1307 close(fd); 1309 1308 } 1310 1309 1310 + static void test_unread_bytes_server(const struct test_opts *opts, int type) 1311 + { 1312 + unsigned char buf[MSG_BUF_IOCTL_LEN]; 1313 + int client_fd; 1314 + 1315 + client_fd = vsock_accept(VMADDR_CID_ANY, opts->peer_port, NULL, type); 1316 + if (client_fd < 0) { 1317 + perror("accept"); 1318 + exit(EXIT_FAILURE); 1319 + } 1320 + 1321 + for (int i = 0; i < sizeof(buf); i++) 1322 + buf[i] = rand() & 0xFF; 1323 + 1324 + send_buf(client_fd, buf, sizeof(buf), 0, sizeof(buf)); 1325 + control_writeln("SENT"); 1326 + 1327 + close(client_fd); 1328 + } 1329 + 1330 + static void test_unread_bytes_client(const struct test_opts *opts, int type) 1331 + { 1332 + unsigned char buf[MSG_BUF_IOCTL_LEN]; 1333 + int fd; 1334 + 1335 + fd = vsock_connect(opts->peer_cid, opts->peer_port, type); 1336 + if (fd < 0) { 1337 + perror("connect"); 1338 + exit(EXIT_FAILURE); 1339 + } 1340 + 1341 + control_expectln("SENT"); 1342 + /* The data has arrived but has not been read. The expected is 1343 + * MSG_BUF_IOCTL_LEN. 1344 + */ 1345 + if (!vsock_ioctl_int(fd, SIOCINQ, MSG_BUF_IOCTL_LEN)) { 1346 + fprintf(stderr, "Test skipped, SIOCINQ not supported.\n"); 1347 + goto out; 1348 + } 1349 + 1350 + recv_buf(fd, buf, sizeof(buf), 0, sizeof(buf)); 1351 + /* All data has been consumed, so the expected is 0. */ 1352 + vsock_ioctl_int(fd, SIOCINQ, 0); 1353 + 1354 + out: 1355 + close(fd); 1356 + } 1357 + 1311 1358 static void test_stream_unsent_bytes_client(const struct test_opts *opts) 1312 1359 { 1313 1360 test_unsent_bytes_client(opts, SOCK_STREAM); ··· 1374 1325 static void test_seqpacket_unsent_bytes_server(const struct test_opts *opts) 1375 1326 { 1376 1327 test_unsent_bytes_server(opts, SOCK_SEQPACKET); 1328 + } 1329 + 1330 + static void test_stream_unread_bytes_client(const struct test_opts *opts) 1331 + { 1332 + test_unread_bytes_client(opts, SOCK_STREAM); 1333 + } 1334 + 1335 + static void test_stream_unread_bytes_server(const struct test_opts *opts) 1336 + { 1337 + test_unread_bytes_server(opts, SOCK_STREAM); 1338 + } 1339 + 1340 + static void test_seqpacket_unread_bytes_client(const struct test_opts *opts) 1341 + { 1342 + test_unread_bytes_client(opts, SOCK_SEQPACKET); 1343 + } 1344 + 1345 + static void test_seqpacket_unread_bytes_server(const struct test_opts *opts) 1346 + { 1347 + test_unread_bytes_server(opts, SOCK_SEQPACKET); 1377 1348 } 1378 1349 1379 1350 #define RCVLOWAT_CREDIT_UPD_BUF_SIZE (1024 * 128) ··· 2344 2275 .name = "SOCK_STREAM transport change null-ptr-deref", 2345 2276 .run_client = test_stream_transport_change_client, 2346 2277 .run_server = test_stream_transport_change_server, 2278 + }, 2279 + { 2280 + .name = "SOCK_STREAM ioctl(SIOCINQ) functionality", 2281 + .run_client = test_stream_unread_bytes_client, 2282 + .run_server = test_stream_unread_bytes_server, 2283 + }, 2284 + { 2285 + .name = "SOCK_SEQPACKET ioctl(SIOCINQ) functionality", 2286 + .run_client = test_seqpacket_unread_bytes_client, 2287 + .run_server = test_seqpacket_unread_bytes_server, 2347 2288 }, 2348 2289 {}, 2349 2290 };