xref: /linux/tools/testing/selftests/net/rds/getsockopt.c (revision b74360369e13a68337a411efba1fd2bdbb6f1b80)
1*b7436036SBreno Leitao // SPDX-License-Identifier: GPL-2.0
2*b7436036SBreno Leitao /*
3*b7436036SBreno Leitao  * Exercise the RDS getsockopt() paths that were converted to the
4*b7436036SBreno Leitao  * getsockopt_iter() / sockopt_t callback.
5*b7436036SBreno Leitao  *
6*b7436036SBreno Leitao  * Three distinct paths are covered:
7*b7436036SBreno Leitao  *
8*b7436036SBreno Leitao  *   - RDS_RECVERR and SO_RDS_TRANSPORT, which now return their int value
9*b7436036SBreno Leitao  *     through copy_to_iter() and report the written length in opt->optlen.
10*b7436036SBreno Leitao  *
11*b7436036SBreno Leitao  *   - RDS_INFO_*, which pins the userspace buffer with
12*b7436036SBreno Leitao  *     iov_iter_extract_pages() (including a non-zero starting page offset)
13*b7436036SBreno Leitao  *     and lets the info producers memcpy the snapshot in under a spinlock.
14*b7436036SBreno Leitao  *
15*b7436036SBreno Leitao  * The kvec (in-kernel buffer) -> -EOPNOTSUPP path of rds_info_getsockopt()
16*b7436036SBreno Leitao  * is not reachable from a userspace getsockopt() and so is not tested here.
17*b7436036SBreno Leitao  */
18*b7436036SBreno Leitao #include <errno.h>
19*b7436036SBreno Leitao #include <stdint.h>
20*b7436036SBreno Leitao #include <string.h>
21*b7436036SBreno Leitao #include <unistd.h>
22*b7436036SBreno Leitao #include <sys/mman.h>
23*b7436036SBreno Leitao #include <sys/socket.h>
24*b7436036SBreno Leitao #include <linux/rds.h>
25*b7436036SBreno Leitao 
26*b7436036SBreno Leitao #include "../../kselftest_harness.h"
27*b7436036SBreno Leitao 
28*b7436036SBreno Leitao #ifndef AF_RDS
29*b7436036SBreno Leitao #define AF_RDS 21
30*b7436036SBreno Leitao #endif
31*b7436036SBreno Leitao 
32*b7436036SBreno Leitao FIXTURE(rds) {
33*b7436036SBreno Leitao 	int fd;
34*b7436036SBreno Leitao };
35*b7436036SBreno Leitao 
36*b7436036SBreno Leitao FIXTURE_SETUP(rds)
37*b7436036SBreno Leitao {
38*b7436036SBreno Leitao 	self->fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
39*b7436036SBreno Leitao 	if (self->fd < 0)
40*b7436036SBreno Leitao 		SKIP(return, "AF_RDS unavailable (errno %d) - load the rds module",
41*b7436036SBreno Leitao 		     errno);
42*b7436036SBreno Leitao }
43*b7436036SBreno Leitao 
44*b7436036SBreno Leitao FIXTURE_TEARDOWN(rds)
45*b7436036SBreno Leitao {
46*b7436036SBreno Leitao 	if (self->fd >= 0)
47*b7436036SBreno Leitao 		close(self->fd);
48*b7436036SBreno Leitao }
49*b7436036SBreno Leitao 
50*b7436036SBreno Leitao /* RDS_RECVERR defaults to 0 and is reported back as a 4-byte int. */
51*b7436036SBreno Leitao TEST_F(rds, recverr_default)
52*b7436036SBreno Leitao {
53*b7436036SBreno Leitao 	socklen_t len = sizeof(int);
54*b7436036SBreno Leitao 	int val = 0xdeadbeef;
55*b7436036SBreno Leitao 
56*b7436036SBreno Leitao 	ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, &len));
57*b7436036SBreno Leitao 	EXPECT_EQ(sizeof(int), len);
58*b7436036SBreno Leitao 	EXPECT_EQ(0, val);
59*b7436036SBreno Leitao }
60*b7436036SBreno Leitao 
61*b7436036SBreno Leitao /* A value set via setsockopt() must be readable back unchanged. */
62*b7436036SBreno Leitao TEST_F(rds, recverr_set_get)
63*b7436036SBreno Leitao {
64*b7436036SBreno Leitao 	socklen_t len = sizeof(int);
65*b7436036SBreno Leitao 	int val = 1;
66*b7436036SBreno Leitao 
67*b7436036SBreno Leitao 	ASSERT_EQ(0, setsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, len));
68*b7436036SBreno Leitao 
69*b7436036SBreno Leitao 	val = 0;
70*b7436036SBreno Leitao 	ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, &val, &len));
71*b7436036SBreno Leitao 	EXPECT_EQ(sizeof(int), len);
72*b7436036SBreno Leitao 	EXPECT_EQ(1, val);
73*b7436036SBreno Leitao }
74*b7436036SBreno Leitao 
75*b7436036SBreno Leitao /* A buffer smaller than an int is rejected with EINVAL, not silently. */
76*b7436036SBreno Leitao TEST_F(rds, recverr_short_buffer)
77*b7436036SBreno Leitao {
78*b7436036SBreno Leitao 	socklen_t len = sizeof(int) - 1;
79*b7436036SBreno Leitao 	char buf[sizeof(int)];
80*b7436036SBreno Leitao 
81*b7436036SBreno Leitao 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_RECVERR, buf, &len));
82*b7436036SBreno Leitao 	EXPECT_EQ(EINVAL, errno);
83*b7436036SBreno Leitao }
84*b7436036SBreno Leitao 
85*b7436036SBreno Leitao /* An unbound socket reports RDS_TRANS_NONE for SO_RDS_TRANSPORT. */
86*b7436036SBreno Leitao TEST_F(rds, transport_unbound)
87*b7436036SBreno Leitao {
88*b7436036SBreno Leitao 	socklen_t len = sizeof(int);
89*b7436036SBreno Leitao 	int val = 0;
90*b7436036SBreno Leitao 
91*b7436036SBreno Leitao 	ASSERT_EQ(0, getsockopt(self->fd, SOL_RDS, SO_RDS_TRANSPORT, &val,
92*b7436036SBreno Leitao 				&len));
93*b7436036SBreno Leitao 	EXPECT_EQ(sizeof(int), len);
94*b7436036SBreno Leitao 	EXPECT_EQ(RDS_TRANS_NONE, (unsigned int)val);
95*b7436036SBreno Leitao }
96*b7436036SBreno Leitao 
97*b7436036SBreno Leitao TEST_F(rds, transport_short_buffer)
98*b7436036SBreno Leitao {
99*b7436036SBreno Leitao 	socklen_t len = sizeof(int) - 1;
100*b7436036SBreno Leitao 	char buf[sizeof(int)];
101*b7436036SBreno Leitao 
102*b7436036SBreno Leitao 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, SO_RDS_TRANSPORT, buf,
103*b7436036SBreno Leitao 				 &len));
104*b7436036SBreno Leitao 	EXPECT_EQ(EINVAL, errno);
105*b7436036SBreno Leitao }
106*b7436036SBreno Leitao 
107*b7436036SBreno Leitao /*
108*b7436036SBreno Leitao  * RDS_INFO_COUNTERS with a zero-length buffer is the "probe" call: it must
109*b7436036SBreno Leitao  * fail with ENOSPC and report the required snapshot size in optlen.
110*b7436036SBreno Leitao  */
111*b7436036SBreno Leitao TEST_F(rds, info_counters_probe)
112*b7436036SBreno Leitao {
113*b7436036SBreno Leitao 	socklen_t len = 0;
114*b7436036SBreno Leitao 
115*b7436036SBreno Leitao 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL,
116*b7436036SBreno Leitao 				 &len));
117*b7436036SBreno Leitao 	EXPECT_EQ(ENOSPC, errno);
118*b7436036SBreno Leitao 	EXPECT_GT(len, 0);
119*b7436036SBreno Leitao 	/* The snapshot is an array of fixed-size counter records. */
120*b7436036SBreno Leitao 	EXPECT_EQ(0, len % (socklen_t)sizeof(struct rds_info_counter));
121*b7436036SBreno Leitao }
122*b7436036SBreno Leitao 
123*b7436036SBreno Leitao /*
124*b7436036SBreno Leitao  * A real snapshot into an unaligned userspace buffer exercises the
125*b7436036SBreno Leitao  * iov_iter_extract_pages() path, including the non-zero offset0 handling
126*b7436036SBreno Leitao  * that the patch reworked. Place the buffer at a non-page-aligned address
127*b7436036SBreno Leitao  * spanning into the next page to make sure multi-page pinning works too.
128*b7436036SBreno Leitao  */
129*b7436036SBreno Leitao TEST_F(rds, info_counters_snapshot)
130*b7436036SBreno Leitao {
131*b7436036SBreno Leitao 	struct rds_info_counter *ctr;
132*b7436036SBreno Leitao 	socklen_t need = 0, len;
133*b7436036SBreno Leitao 	long pagesz = sysconf(_SC_PAGESIZE);
134*b7436036SBreno Leitao 	size_t offset, map_len;
135*b7436036SBreno Leitao 	unsigned int i, n;
136*b7436036SBreno Leitao 	char *region, *buf;
137*b7436036SBreno Leitao 	int ret;
138*b7436036SBreno Leitao 
139*b7436036SBreno Leitao 	/* Probe for the required size. */
140*b7436036SBreno Leitao 	getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, &need);
141*b7436036SBreno Leitao 	ASSERT_GT(need, 0);
142*b7436036SBreno Leitao 
143*b7436036SBreno Leitao 	/*
144*b7436036SBreno Leitao 	 * Place the buffer at a non-page-aligned offset that runs past the
145*b7436036SBreno Leitao 	 * first page boundary, and size the mapping from the probed length so
146*b7436036SBreno Leitao 	 * the test keeps working if the counter set grows.
147*b7436036SBreno Leitao 	 */
148*b7436036SBreno Leitao 	offset = pagesz - 64;
149*b7436036SBreno Leitao 	map_len = ((offset + need + pagesz - 1) / pagesz) * pagesz;
150*b7436036SBreno Leitao 
151*b7436036SBreno Leitao 	region = mmap(NULL, map_len, PROT_READ | PROT_WRITE,
152*b7436036SBreno Leitao 		      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
153*b7436036SBreno Leitao 	ASSERT_NE(MAP_FAILED, region);
154*b7436036SBreno Leitao 
155*b7436036SBreno Leitao 	buf = region + offset;
156*b7436036SBreno Leitao 
157*b7436036SBreno Leitao 	/*
158*b7436036SBreno Leitao 	 * On success the RDS_INFO path returns the positive per-element size
159*b7436036SBreno Leitao 	 * (lens.each) rather than 0, and writes the full snapshot length back
160*b7436036SBreno Leitao 	 * into optlen.
161*b7436036SBreno Leitao 	 */
162*b7436036SBreno Leitao 	len = need;
163*b7436036SBreno Leitao 	ret = getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, buf, &len);
164*b7436036SBreno Leitao 	ASSERT_GE(ret, 0) {
165*b7436036SBreno Leitao 		TH_LOG("getsockopt snapshot failed: errno %d", errno);
166*b7436036SBreno Leitao 	}
167*b7436036SBreno Leitao 	EXPECT_EQ(sizeof(struct rds_info_counter), ret);
168*b7436036SBreno Leitao 	EXPECT_EQ(need, len);
169*b7436036SBreno Leitao 
170*b7436036SBreno Leitao 	/* The counter names must be NUL-terminated, non-empty strings. */
171*b7436036SBreno Leitao 	ctr = (struct rds_info_counter *)buf;
172*b7436036SBreno Leitao 	n = len / sizeof(*ctr);
173*b7436036SBreno Leitao 	ASSERT_GT(n, 0);
174*b7436036SBreno Leitao 	for (i = 0; i < n; i++) {
175*b7436036SBreno Leitao 		size_t namelen = strnlen((char *)ctr[i].name,
176*b7436036SBreno Leitao 					 sizeof(ctr[i].name));
177*b7436036SBreno Leitao 
178*b7436036SBreno Leitao 		EXPECT_GT(namelen, 0);
179*b7436036SBreno Leitao 		EXPECT_LT(namelen, sizeof(ctr[i].name));
180*b7436036SBreno Leitao 	}
181*b7436036SBreno Leitao 
182*b7436036SBreno Leitao 	munmap(region, map_len);
183*b7436036SBreno Leitao }
184*b7436036SBreno Leitao 
185*b7436036SBreno Leitao /*
186*b7436036SBreno Leitao  * A non-zero but too-small buffer must report ENOSPC and the full required
187*b7436036SBreno Leitao  * length, without corrupting memory past the buffer.
188*b7436036SBreno Leitao  */
189*b7436036SBreno Leitao TEST_F(rds, info_counters_short_buffer)
190*b7436036SBreno Leitao {
191*b7436036SBreno Leitao 	socklen_t need = 0, len;
192*b7436036SBreno Leitao 	char small[sizeof(struct rds_info_counter)];
193*b7436036SBreno Leitao 
194*b7436036SBreno Leitao 	getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, NULL, &need);
195*b7436036SBreno Leitao 	ASSERT_GT(need, 0);
196*b7436036SBreno Leitao 
197*b7436036SBreno Leitao 	/* Ask with a buffer guaranteed smaller than the full snapshot. */
198*b7436036SBreno Leitao 	if (need <= (socklen_t)sizeof(small))
199*b7436036SBreno Leitao 		SKIP(return, "snapshot fits in one record; nothing to test");
200*b7436036SBreno Leitao 
201*b7436036SBreno Leitao 	len = 1; /* < sizeof(struct rds_info_counter) */
202*b7436036SBreno Leitao 	EXPECT_EQ(-1, getsockopt(self->fd, SOL_RDS, RDS_INFO_COUNTERS, small,
203*b7436036SBreno Leitao 				 &len));
204*b7436036SBreno Leitao 	EXPECT_EQ(ENOSPC, errno);
205*b7436036SBreno Leitao 	EXPECT_EQ(need, len);
206*b7436036SBreno Leitao }
207*b7436036SBreno Leitao 
208*b7436036SBreno Leitao TEST_HARNESS_MAIN
209