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