1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2021 SUSE LLC Andrea Cervesato <[email protected]>
4 */
5
6 /*\
7 * [Description]
8 *
9 * Create a file using buffered writes while other processes are doing
10 * O_DIRECT reads and check if the buffer reads always see zero.
11 */
12
13 #define _GNU_SOURCE
14
15 #include <unistd.h>
16 #include <limits.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include "tst_test.h"
21 #include "common.h"
22
23 static char *str_numchildren;
24 static char *str_writesize;
25 static char *str_readsize;
26 static char *str_filesize;
27
28 static char *filename = "file.bin";
29 static int numchildren = 8;
30 static long long writesize = 32 * 1024 * 1024;
31 static long long readsize = 32 * 1024 * 1024;
32 static long long filesize = 128 * 1024 * 1024;
33 static int *children_completed;
34 static char *iobuf;
35 static int fd;
36
do_buffered_writes(int fd,char * bufptr,long long fsize,long long wsize,int pattern)37 static void do_buffered_writes(int fd, char *bufptr, long long fsize, long long wsize, int pattern)
38 {
39 long long offset;
40 long long w;
41
42 memset(bufptr, pattern, wsize);
43
44 tst_res(TINFO, "child %i writing file", getpid());
45
46 for (offset = 0; offset + wsize <= fsize; offset += wsize) {
47 w = pwrite(fd, bufptr, wsize, offset);
48 if (w < 0)
49 tst_brk(TBROK, "pwrite: %s", tst_strerrno(-w));
50 if (w != wsize)
51 tst_brk(TBROK, "pwrite: wrote %lld bytes out of %lld", w, wsize);
52
53 SAFE_FSYNC(fd);
54
55 if (!tst_remaining_runtime())
56 return;
57 }
58 }
59
do_direct_reads(char * filename,char * bufptr,long long fsize,long long rsize)60 static int do_direct_reads(char *filename, char *bufptr, long long fsize, long long rsize)
61 {
62 int fd;
63 long long offset;
64 long long w;
65 int fail = 0;
66 int iter = 1;
67
68 fd = SAFE_OPEN(filename, O_RDONLY | O_DIRECT, 0666);
69
70 while (1) {
71 for (offset = 0; offset + rsize < fsize; offset += rsize) {
72 char *bufoff;
73
74 if (*children_completed >= numchildren) {
75 tst_res(TINFO,
76 "Writers finshed, exiting reader (iteration %i)",
77 iter);
78 goto exit;
79 }
80
81 if (!tst_remaining_runtime()) {
82 tst_res(TINFO, "Test out of runtime, exiting");
83 goto exit;
84 }
85
86 w = pread(fd, bufptr, rsize, offset);
87 if (w < 0)
88 tst_brk(TBROK, "pread: %s", tst_strerrno(-w));
89 if (w != rsize)
90 tst_brk(TBROK, "pread: read %lld bytes out of %lld", w, rsize);
91
92 bufoff = check_zero(bufptr, rsize);
93 if (bufoff) {
94 fail = 1;
95 goto exit;
96 }
97
98 iter++;
99 }
100 }
101
102 exit:
103 SAFE_CLOSE(fd);
104
105 return fail;
106 }
107
setup(void)108 static void setup(void)
109 {
110 struct stat sb;
111 long long buffsize;
112 long long alignment;
113
114 if (tst_parse_int(str_numchildren, &numchildren, 1, INT_MAX))
115 tst_brk(TBROK, "Invalid number of children '%s'", str_numchildren);
116
117 if (tst_parse_filesize(str_filesize, &filesize, 1, LLONG_MAX))
118 tst_brk(TBROK, "Invalid file size '%s'", str_filesize);
119
120 if (tst_parse_filesize(str_writesize, &writesize, 1, filesize))
121 tst_brk(TBROK, "Invalid write blocks size '%s'", str_writesize);
122
123 if (tst_parse_filesize(str_readsize, &readsize, 1, filesize))
124 tst_brk(TBROK, "Invalid read blocks size '%s'", str_readsize);
125
126 SAFE_STAT(".", &sb);
127 alignment = sb.st_blksize;
128
129 buffsize = readsize > writesize ? readsize : writesize;
130
131 iobuf = SAFE_MEMALIGN(alignment, buffsize);
132
133 children_completed = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
134
135 fd = SAFE_OPEN(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);
136 }
137
cleanup(void)138 static void cleanup(void)
139 {
140 SAFE_CLOSE(fd);
141 }
142
run(void)143 static void run(void)
144 {
145 int i;
146 int fail;
147
148 // Fill the file with a known pattern so that the blocks
149 // on disk can be detected if they become exposed
150 do_buffered_writes(fd, iobuf, filesize, writesize, 1);
151 SAFE_FSYNC(fd);
152 SAFE_FTRUNCATE(fd, 0);
153 SAFE_FSYNC(fd);
154
155 SAFE_FTRUNCATE(fd, filesize);
156
157 *children_completed = 0;
158
159 for (i = 0; i < numchildren; i++) {
160 if (!SAFE_FORK()) {
161 do_buffered_writes(fd, iobuf, filesize, writesize, 0);
162 tst_atomic_add_return(1, children_completed);
163 return;
164 }
165 }
166
167 fail = do_direct_reads(filename, iobuf, filesize, readsize);
168
169 if (fail)
170 tst_res(TFAIL, "Non zero bytes read");
171 else
172 tst_res(TPASS, "All bytes read were zeroed");
173 }
174
175 static struct tst_test test = {
176 .test_all = run,
177 .setup = setup,
178 .cleanup = cleanup,
179 .needs_tmpdir = 1,
180 .forks_child = 1,
181 .max_runtime = 1800,
182 .options = (struct tst_option[]) {
183 {"n:", &str_numchildren, "Number of threads (default 8)"},
184 {"w:", &str_writesize, "Size of writing blocks (default 32M)"},
185 {"r:", &str_readsize, "Size of reading blocks (default 32M)"},
186 {"s:", &str_filesize, "File size (default 128M)"},
187 {}
188 },
189 .skip_filesystems = (const char *[]) {
190 "tmpfs",
191 NULL
192 },
193 };
194