1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (C) 2019 Cyril Hrubis <chrubis@suse.cz> 4f08c3bdfSopenharmony_ci */ 5f08c3bdfSopenharmony_ci 6f08c3bdfSopenharmony_ci/* 7f08c3bdfSopenharmony_ci * This is a basic functional test for RWF_NOWAIT flag, we are attempting to 8f08c3bdfSopenharmony_ci * force preadv2() either to return a short read or EAGAIN with three 9f08c3bdfSopenharmony_ci * concurently running threads: 10f08c3bdfSopenharmony_ci * 11f08c3bdfSopenharmony_ci * nowait_reader: reads from a random offset from a random file with 12f08c3bdfSopenharmony_ci * RWF_NOWAIT flag and expects to get EAGAIN and short 13f08c3bdfSopenharmony_ci * read sooner or later 14f08c3bdfSopenharmony_ci * 15f08c3bdfSopenharmony_ci * writer_thread: rewrites random file in order to keep the underlying device 16f08c3bdfSopenharmony_ci * busy so that pages evicted from cache cannot be faulted 17f08c3bdfSopenharmony_ci * immediately 18f08c3bdfSopenharmony_ci * 19f08c3bdfSopenharmony_ci * cache_dropper: attempts to evict pages from a cache in order for reader to 20f08c3bdfSopenharmony_ci * hit evicted page sooner or later 21f08c3bdfSopenharmony_ci */ 22f08c3bdfSopenharmony_ci 23f08c3bdfSopenharmony_ci/* 24f08c3bdfSopenharmony_ci * If test fails with EOPNOTSUPP you have likely hit a glibc bug: 25f08c3bdfSopenharmony_ci * 26f08c3bdfSopenharmony_ci * https://sourceware.org/bugzilla/show_bug.cgi?id=23579 27f08c3bdfSopenharmony_ci * 28f08c3bdfSopenharmony_ci * Which can be worked around by calling preadv2() directly by syscall() such as: 29f08c3bdfSopenharmony_ci * 30f08c3bdfSopenharmony_ci * static ssize_t sys_preadv2(int fd, const struct iovec *iov, int iovcnt, 31f08c3bdfSopenharmony_ci * off_t offset, int flags) 32f08c3bdfSopenharmony_ci * { 33f08c3bdfSopenharmony_ci * return syscall(SYS_preadv2, fd, iov, iovcnt, offset, offset>>32, flags); 34f08c3bdfSopenharmony_ci * } 35f08c3bdfSopenharmony_ci * 36f08c3bdfSopenharmony_ci */ 37f08c3bdfSopenharmony_ci 38f08c3bdfSopenharmony_ci#define _GNU_SOURCE 39f08c3bdfSopenharmony_ci#include <string.h> 40f08c3bdfSopenharmony_ci#include <sys/uio.h> 41f08c3bdfSopenharmony_ci#include <stdio.h> 42f08c3bdfSopenharmony_ci#include <stdlib.h> 43f08c3bdfSopenharmony_ci#include <ctype.h> 44f08c3bdfSopenharmony_ci#include <pthread.h> 45f08c3bdfSopenharmony_ci 46f08c3bdfSopenharmony_ci#include "tst_test.h" 47f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h" 48f08c3bdfSopenharmony_ci#include "lapi/preadv2.h" 49f08c3bdfSopenharmony_ci 50f08c3bdfSopenharmony_ci#define CHUNK_SZ 4123 51f08c3bdfSopenharmony_ci#define CHUNKS 60 52f08c3bdfSopenharmony_ci#define MNTPOINT "mntpoint" 53f08c3bdfSopenharmony_ci#define FILES 500 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_cistatic int fds[FILES]; 56f08c3bdfSopenharmony_ci 57f08c3bdfSopenharmony_cistatic volatile int stop; 58f08c3bdfSopenharmony_ci 59f08c3bdfSopenharmony_cistatic void drop_caches(void) 60f08c3bdfSopenharmony_ci{ 61f08c3bdfSopenharmony_ci SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "3"); 62f08c3bdfSopenharmony_ci} 63f08c3bdfSopenharmony_ci 64f08c3bdfSopenharmony_ci/* 65f08c3bdfSopenharmony_ci * All files are divided in chunks each filled with the same bytes starting with 66f08c3bdfSopenharmony_ci * '0' at offset 0 and with increasing value on each next chunk. 67f08c3bdfSopenharmony_ci * 68f08c3bdfSopenharmony_ci * 000....000111....111.......AAA......AAA... 69f08c3bdfSopenharmony_ci * | chunk0 || chunk1 | ... | chunk17 | 70f08c3bdfSopenharmony_ci */ 71f08c3bdfSopenharmony_cistatic int verify_short_read(struct iovec *iov, size_t iov_cnt, 72f08c3bdfSopenharmony_ci off_t off, size_t size) 73f08c3bdfSopenharmony_ci{ 74f08c3bdfSopenharmony_ci unsigned int i; 75f08c3bdfSopenharmony_ci size_t j, checked = 0; 76f08c3bdfSopenharmony_ci 77f08c3bdfSopenharmony_ci for (i = 0; i < iov_cnt; i++) { 78f08c3bdfSopenharmony_ci char *buf = iov[i].iov_base; 79f08c3bdfSopenharmony_ci for (j = 0; j < iov[i].iov_len; j++) { 80f08c3bdfSopenharmony_ci char exp_val = '0' + (off + checked)/CHUNK_SZ; 81f08c3bdfSopenharmony_ci 82f08c3bdfSopenharmony_ci if (exp_val != buf[j]) { 83f08c3bdfSopenharmony_ci tst_res(TFAIL, 84f08c3bdfSopenharmony_ci "Wrong value read pos %zu size %zu %c (%i) %c (%i)!", 85f08c3bdfSopenharmony_ci checked, size, exp_val, exp_val, 86f08c3bdfSopenharmony_ci isprint(buf[j]) ? buf[j] : ' ', buf[j]); 87f08c3bdfSopenharmony_ci return 1; 88f08c3bdfSopenharmony_ci } 89f08c3bdfSopenharmony_ci 90f08c3bdfSopenharmony_ci if (++checked >= size) 91f08c3bdfSopenharmony_ci return 0; 92f08c3bdfSopenharmony_ci } 93f08c3bdfSopenharmony_ci } 94f08c3bdfSopenharmony_ci 95f08c3bdfSopenharmony_ci return 0; 96f08c3bdfSopenharmony_ci} 97f08c3bdfSopenharmony_ci 98f08c3bdfSopenharmony_cistatic void *nowait_reader(void *unused LTP_ATTRIBUTE_UNUSED) 99f08c3bdfSopenharmony_ci{ 100f08c3bdfSopenharmony_ci char buf1[CHUNK_SZ/2]; 101f08c3bdfSopenharmony_ci char buf2[CHUNK_SZ]; 102f08c3bdfSopenharmony_ci unsigned int full_read_cnt = 0, eagain_cnt = 0; 103f08c3bdfSopenharmony_ci unsigned int short_read_cnt = 0, zero_read_cnt = 0; 104f08c3bdfSopenharmony_ci 105f08c3bdfSopenharmony_ci struct iovec rd_iovec[] = { 106f08c3bdfSopenharmony_ci {buf1, sizeof(buf1)}, 107f08c3bdfSopenharmony_ci {buf2, sizeof(buf2)}, 108f08c3bdfSopenharmony_ci }; 109f08c3bdfSopenharmony_ci 110f08c3bdfSopenharmony_ci while (!stop) { 111f08c3bdfSopenharmony_ci if (eagain_cnt >= 100 && short_read_cnt >= 10) 112f08c3bdfSopenharmony_ci stop = 1; 113f08c3bdfSopenharmony_ci 114f08c3bdfSopenharmony_ci /* Ensure short reads doesn't happen because of tripping on EOF */ 115f08c3bdfSopenharmony_ci off_t off = random() % ((CHUNKS - 2) * CHUNK_SZ); 116f08c3bdfSopenharmony_ci int fd = fds[random() % FILES]; 117f08c3bdfSopenharmony_ci 118f08c3bdfSopenharmony_ci TEST(preadv2(fd, rd_iovec, 2, off, RWF_NOWAIT)); 119f08c3bdfSopenharmony_ci 120f08c3bdfSopenharmony_ci if (TST_RET < 0) { 121f08c3bdfSopenharmony_ci if (TST_ERR != EAGAIN) 122f08c3bdfSopenharmony_ci tst_brk(TBROK | TTERRNO, "preadv2() failed"); 123f08c3bdfSopenharmony_ci 124f08c3bdfSopenharmony_ci eagain_cnt++; 125f08c3bdfSopenharmony_ci continue; 126f08c3bdfSopenharmony_ci } 127f08c3bdfSopenharmony_ci 128f08c3bdfSopenharmony_ci 129f08c3bdfSopenharmony_ci if (TST_RET == 0) { 130f08c3bdfSopenharmony_ci zero_read_cnt++; 131f08c3bdfSopenharmony_ci continue; 132f08c3bdfSopenharmony_ci } 133f08c3bdfSopenharmony_ci 134f08c3bdfSopenharmony_ci if (TST_RET != CHUNK_SZ + CHUNK_SZ/2) { 135f08c3bdfSopenharmony_ci verify_short_read(rd_iovec, 2, off, TST_RET); 136f08c3bdfSopenharmony_ci short_read_cnt++; 137f08c3bdfSopenharmony_ci continue; 138f08c3bdfSopenharmony_ci } 139f08c3bdfSopenharmony_ci 140f08c3bdfSopenharmony_ci full_read_cnt++; 141f08c3bdfSopenharmony_ci } 142f08c3bdfSopenharmony_ci 143f08c3bdfSopenharmony_ci tst_res(TINFO, 144f08c3bdfSopenharmony_ci "Number of full_reads %u, short reads %u, zero len reads %u, EAGAIN(s) %u", 145f08c3bdfSopenharmony_ci full_read_cnt, short_read_cnt, zero_read_cnt, eagain_cnt); 146f08c3bdfSopenharmony_ci 147f08c3bdfSopenharmony_ci return (void*)(long)eagain_cnt; 148f08c3bdfSopenharmony_ci} 149f08c3bdfSopenharmony_ci 150f08c3bdfSopenharmony_cistatic void *writer_thread(void *unused) 151f08c3bdfSopenharmony_ci{ 152f08c3bdfSopenharmony_ci char buf[CHUNK_SZ]; 153f08c3bdfSopenharmony_ci unsigned int j, write_cnt = 0; 154f08c3bdfSopenharmony_ci 155f08c3bdfSopenharmony_ci struct iovec wr_iovec[] = { 156f08c3bdfSopenharmony_ci {buf, sizeof(buf)}, 157f08c3bdfSopenharmony_ci }; 158f08c3bdfSopenharmony_ci 159f08c3bdfSopenharmony_ci while (!stop) { 160f08c3bdfSopenharmony_ci int fd = fds[random() % FILES]; 161f08c3bdfSopenharmony_ci 162f08c3bdfSopenharmony_ci for (j = 0; j < CHUNKS && !stop; j++) { 163f08c3bdfSopenharmony_ci memset(buf, '0' + j, sizeof(buf)); 164f08c3bdfSopenharmony_ci 165f08c3bdfSopenharmony_ci off_t off = CHUNK_SZ * j; 166f08c3bdfSopenharmony_ci 167f08c3bdfSopenharmony_ci if (pwritev(fd, wr_iovec, 1, off) < 0) { 168f08c3bdfSopenharmony_ci if (errno == EBADF) { 169f08c3bdfSopenharmony_ci tst_res(TINFO | TERRNO, "FDs closed, exiting..."); 170f08c3bdfSopenharmony_ci return unused; 171f08c3bdfSopenharmony_ci } 172f08c3bdfSopenharmony_ci 173f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "pwritev()"); 174f08c3bdfSopenharmony_ci } 175f08c3bdfSopenharmony_ci 176f08c3bdfSopenharmony_ci write_cnt++; 177f08c3bdfSopenharmony_ci } 178f08c3bdfSopenharmony_ci } 179f08c3bdfSopenharmony_ci 180f08c3bdfSopenharmony_ci tst_res(TINFO, "Number of writes %u", write_cnt); 181f08c3bdfSopenharmony_ci 182f08c3bdfSopenharmony_ci return unused; 183f08c3bdfSopenharmony_ci} 184f08c3bdfSopenharmony_ci 185f08c3bdfSopenharmony_cistatic void *cache_dropper(void *unused) 186f08c3bdfSopenharmony_ci{ 187f08c3bdfSopenharmony_ci unsigned int drop_cnt = 0; 188f08c3bdfSopenharmony_ci 189f08c3bdfSopenharmony_ci while (!stop) { 190f08c3bdfSopenharmony_ci drop_caches(); 191f08c3bdfSopenharmony_ci drop_cnt++; 192f08c3bdfSopenharmony_ci } 193f08c3bdfSopenharmony_ci 194f08c3bdfSopenharmony_ci tst_res(TINFO, "Cache dropped %u times", drop_cnt); 195f08c3bdfSopenharmony_ci 196f08c3bdfSopenharmony_ci return unused; 197f08c3bdfSopenharmony_ci} 198f08c3bdfSopenharmony_ci 199f08c3bdfSopenharmony_cistatic void verify_preadv2(void) 200f08c3bdfSopenharmony_ci{ 201f08c3bdfSopenharmony_ci pthread_t reader, dropper, writer; 202f08c3bdfSopenharmony_ci void *eagains; 203f08c3bdfSopenharmony_ci 204f08c3bdfSopenharmony_ci stop = 0; 205f08c3bdfSopenharmony_ci 206f08c3bdfSopenharmony_ci drop_caches(); 207f08c3bdfSopenharmony_ci 208f08c3bdfSopenharmony_ci SAFE_PTHREAD_CREATE(&dropper, NULL, cache_dropper, NULL); 209f08c3bdfSopenharmony_ci SAFE_PTHREAD_CREATE(&reader, NULL, nowait_reader, NULL); 210f08c3bdfSopenharmony_ci SAFE_PTHREAD_CREATE(&writer, NULL, writer_thread, NULL); 211f08c3bdfSopenharmony_ci 212f08c3bdfSopenharmony_ci while (!stop && tst_remaining_runtime()) 213f08c3bdfSopenharmony_ci usleep(100000); 214f08c3bdfSopenharmony_ci 215f08c3bdfSopenharmony_ci stop = 1; 216f08c3bdfSopenharmony_ci 217f08c3bdfSopenharmony_ci SAFE_PTHREAD_JOIN(reader, &eagains); 218f08c3bdfSopenharmony_ci SAFE_PTHREAD_JOIN(dropper, NULL); 219f08c3bdfSopenharmony_ci SAFE_PTHREAD_JOIN(writer, NULL); 220f08c3bdfSopenharmony_ci 221f08c3bdfSopenharmony_ci if (eagains) 222f08c3bdfSopenharmony_ci tst_res(TPASS, "Got some EAGAIN"); 223f08c3bdfSopenharmony_ci else 224f08c3bdfSopenharmony_ci tst_res(TFAIL, "Haven't got EAGAIN"); 225f08c3bdfSopenharmony_ci} 226f08c3bdfSopenharmony_ci 227f08c3bdfSopenharmony_cistatic void check_preadv2_nowait(int fd) 228f08c3bdfSopenharmony_ci{ 229f08c3bdfSopenharmony_ci char buf[1]; 230f08c3bdfSopenharmony_ci struct iovec iovec[] = { 231f08c3bdfSopenharmony_ci {buf, sizeof(buf)}, 232f08c3bdfSopenharmony_ci }; 233f08c3bdfSopenharmony_ci 234f08c3bdfSopenharmony_ci TEST(preadv2(fd, iovec, 1, 0, RWF_NOWAIT)); 235f08c3bdfSopenharmony_ci 236f08c3bdfSopenharmony_ci if (TST_ERR == EOPNOTSUPP) 237f08c3bdfSopenharmony_ci tst_brk(TCONF | TTERRNO, "preadv2()"); 238f08c3bdfSopenharmony_ci} 239f08c3bdfSopenharmony_ci 240f08c3bdfSopenharmony_cistatic void setup(void) 241f08c3bdfSopenharmony_ci{ 242f08c3bdfSopenharmony_ci char path[1024]; 243f08c3bdfSopenharmony_ci char buf[CHUNK_SZ]; 244f08c3bdfSopenharmony_ci unsigned int i; 245f08c3bdfSopenharmony_ci char j; 246f08c3bdfSopenharmony_ci 247f08c3bdfSopenharmony_ci for (i = 0; i < FILES; i++) { 248f08c3bdfSopenharmony_ci snprintf(path, sizeof(path), MNTPOINT"/file_%i", i); 249f08c3bdfSopenharmony_ci 250f08c3bdfSopenharmony_ci fds[i] = SAFE_OPEN(path, O_RDWR | O_CREAT, 0644); 251f08c3bdfSopenharmony_ci 252f08c3bdfSopenharmony_ci if (i == 0) 253f08c3bdfSopenharmony_ci check_preadv2_nowait(fds[i]); 254f08c3bdfSopenharmony_ci 255f08c3bdfSopenharmony_ci for (j = 0; j < CHUNKS; j++) { 256f08c3bdfSopenharmony_ci memset(buf, '0' + j, sizeof(buf)); 257f08c3bdfSopenharmony_ci SAFE_WRITE(SAFE_WRITE_RETRY, fds[i], buf, sizeof(buf)); 258f08c3bdfSopenharmony_ci } 259f08c3bdfSopenharmony_ci } 260f08c3bdfSopenharmony_ci} 261f08c3bdfSopenharmony_ci 262f08c3bdfSopenharmony_cistatic void do_cleanup(void) 263f08c3bdfSopenharmony_ci{ 264f08c3bdfSopenharmony_ci unsigned int i; 265f08c3bdfSopenharmony_ci 266f08c3bdfSopenharmony_ci for (i = 0; i < FILES; i++) { 267f08c3bdfSopenharmony_ci if (fds[i] > 0) 268f08c3bdfSopenharmony_ci SAFE_CLOSE(fds[i]); 269f08c3bdfSopenharmony_ci } 270f08c3bdfSopenharmony_ci} 271f08c3bdfSopenharmony_ci 272f08c3bdfSopenharmony_ciTST_DECLARE_ONCE_FN(cleanup, do_cleanup); 273f08c3bdfSopenharmony_ci 274f08c3bdfSopenharmony_cistatic struct tst_test test = { 275f08c3bdfSopenharmony_ci .setup = setup, 276f08c3bdfSopenharmony_ci .cleanup = cleanup, 277f08c3bdfSopenharmony_ci .test_all = verify_preadv2, 278f08c3bdfSopenharmony_ci .mntpoint = MNTPOINT, 279f08c3bdfSopenharmony_ci .mount_device = 1, 280f08c3bdfSopenharmony_ci .all_filesystems = 1, 281f08c3bdfSopenharmony_ci .max_runtime = 60, 282f08c3bdfSopenharmony_ci .needs_root = 1, 283f08c3bdfSopenharmony_ci}; 284