1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2004 Daniel McNeil <daniel@osdl.org> 4 * 2004 Open Source Development Lab 5 * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com> 6 */ 7 8/*\ 9 * [Description] 10 * 11 * This test is mixing direct I/O and truncate operations checking if they can 12 * be used together at the same time. Multiple children are spawned to read a 13 * file that is written to using direct I/O and truncated in a loop. 14 * 15 * [Algorithm] 16 * 17 * - Spawn multiple children which start to read on 'file' 18 * - Parent start to fill and truncate 'file' many times with zero char when 19 * children are reading 20 * - Parent start to fill and truncate a junk file many times with non-zero char 21 * 22 * If no issues occur on direct IO/truncate operations and the file always 23 * contains zero characters, test PASS. Otherwise, test will FAIL. 24 */ 25 26#define _GNU_SOURCE 27 28#include <stdlib.h> 29#include <stdio.h> 30#include <sys/stat.h> 31#include <sys/types.h> 32#include <fcntl.h> 33#include "tst_test.h" 34#include "tst_kconfig.h" 35#include "common.h" 36 37static volatile int *run_child; 38 39static char *str_numchildren; 40static char *str_filesize; 41static char *str_numappends; 42static char *str_numwrites; 43 44static int numchildren = 16; 45static long long filesize = 64 * 1024; 46static long long alignment; 47static int numappends = 100; 48static int numwrites = 100; 49 50static void dio_read(const char *filename, long long align, size_t bs) 51{ 52 int fd; 53 int r; 54 char *bufptr; 55 56 while ((fd = open(filename, O_RDONLY | O_DIRECT, 0666)) < 0) 57 usleep(100); 58 59 bufptr = SAFE_MEMALIGN(align, bs); 60 61 tst_res(TINFO, "child %i reading file", getpid()); 62 while (*run_child) { 63 off_t offset; 64 char *bufoff; 65 66 offset = SAFE_LSEEK(fd, SEEK_SET, 0); 67 do { 68 r = read(fd, bufptr, 64 * 1024); 69 if (r > 0) { 70 bufoff = check_zero(bufptr, r); 71 if (bufoff) { 72 tst_res(TINFO, "non-zero read at offset %zu", 73 offset + (bufoff - bufptr)); 74 free(bufptr); 75 SAFE_CLOSE(fd); 76 return; 77 } 78 offset += r; 79 } 80 } while (r > 0); 81 } 82 83 free(bufptr); 84 SAFE_CLOSE(fd); 85} 86 87static void setup(void) 88{ 89 struct stat sb; 90 static const char * const kconf_rt[] = {"CONFIG_PREEMPT_RT", NULL}; 91 92 if (tst_parse_int(str_numchildren, &numchildren, 1, INT_MAX)) 93 tst_brk(TBROK, "Invalid number of children '%s'", str_numchildren); 94 95 if (tst_parse_filesize(str_filesize, &filesize, 1, LLONG_MAX)) 96 tst_brk(TBROK, "Invalid file size '%s'", str_filesize); 97 98 if (tst_parse_int(str_numappends, &numappends, 1, INT_MAX)) 99 tst_brk(TBROK, "Invalid number of appends '%s'", str_numappends); 100 101 if (tst_parse_int(str_numwrites, &numwrites, 1, INT_MAX)) 102 tst_brk(TBROK, "Invalid number of truncate/append '%s'", str_numwrites); 103 104 SAFE_STAT(".", &sb); 105 alignment = sb.st_blksize; 106 107 run_child = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); 108 109 if (numchildren > 2 && !tst_kconfig_check(kconf_rt)) { 110 tst_res(TINFO, "Warning: This test may deadlock on RT kernels"); 111 tst_res(TINFO, "If it does, reduce number of threads to 2"); 112 } 113} 114 115static void cleanup(void) 116{ 117 if (run_child) { 118 *run_child = 0; 119 SAFE_MUNMAP((void *)run_child, sizeof(int)); 120 } 121} 122 123static void run(void) 124{ 125 char *filename = "file.bin"; 126 int wflags = O_DIRECT | O_WRONLY | O_CREAT; 127 int status; 128 int i; 129 int fail = 0; 130 131 *run_child = 1; 132 133 for (i = 0; i < numchildren; i++) { 134 if (!SAFE_FORK()) { 135 dio_read(filename, alignment, filesize); 136 return; 137 } 138 } 139 140 tst_res(TINFO, "Parent writes/truncates the file"); 141 142 for (i = 0; i < numwrites; i++) { 143 io_append(filename, 0, wflags, filesize, numappends); 144 SAFE_TRUNCATE(filename, 0); 145 io_append("junkfile", 0xaa, wflags, filesize, numappends); 146 SAFE_TRUNCATE("junkfile", 0); 147 148 if (SAFE_WAITPID(-1, &status, WNOHANG)) { 149 fail = 1; 150 break; 151 } 152 153 if (!tst_remaining_runtime()) { 154 tst_res(TINFO, "Test out of runtime, exiting"); 155 break; 156 } 157 } 158 159 if (fail) 160 tst_res(TFAIL, "Non zero bytes read"); 161 else 162 tst_res(TPASS, "All bytes read were zeroed"); 163 164 *run_child = 0; 165} 166 167static struct tst_test test = { 168 .test_all = run, 169 .setup = setup, 170 .cleanup = cleanup, 171 .needs_tmpdir = 1, 172 .forks_child = 1, 173 .max_runtime = 1800, 174 .options = (struct tst_option[]) { 175 {"n:", &str_numchildren, "Number of threads (default 16)"}, 176 {"s:", &str_filesize, "Size of file (default 64K)"}, 177 {"a:", &str_numappends, "Number of appends (default 100)"}, 178 {"c:", &str_numwrites, "Number of append & truncate (default 100)"}, 179 {} 180 }, 181 .skip_filesystems = (const char *[]) { 182 "tmpfs", 183 NULL 184 }, 185}; 186