1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
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 }
56
do_direct_reads(char * filename,char * bufptr,long long fsize,long long rsize)57 static int do_direct_reads(char *filename, char *bufptr, long long fsize, long long rsize)
58 {
59 int fd;
60 long long offset;
61 long long w;
62 int fail = 0;
63 int iter = 1;
64
65 fd = SAFE_OPEN(filename, O_RDONLY | O_DIRECT, 0666);
66
67 while (1) {
68 for (offset = 0; offset + rsize < fsize; offset += rsize) {
69 char *bufoff;
70
71 if (*children_completed >= numchildren) {
72 tst_res(TINFO,
73 "Writers finshed, exitting reader (iteration %i)",
74 iter);
75 goto exit;
76 }
77
78 w = pread(fd, bufptr, rsize, offset);
79 if (w < 0)
80 tst_brk(TBROK, "pread: %s", tst_strerrno(-w));
81 if (w != rsize)
82 tst_brk(TBROK, "pread: read %lld bytes out of %lld", w, rsize);
83
84 bufoff = check_zero(bufptr, rsize);
85 if (bufoff) {
86 fail = 1;
87 goto exit;
88 }
89
90 iter++;
91 }
92 }
93
94 exit:
95 SAFE_CLOSE(fd);
96
97 return fail;
98 }
99
setup(void)100 static void setup(void)
101 {
102 struct stat sb;
103 long long buffsize;
104 long long alignment;
105
106 if (tst_parse_int(str_numchildren, &numchildren, 1, INT_MAX))
107 tst_brk(TBROK, "Invalid number of children '%s'", str_numchildren);
108
109 if (tst_parse_filesize(str_filesize, &filesize, 1, LLONG_MAX))
110 tst_brk(TBROK, "Invalid file size '%s'", str_filesize);
111
112 if (tst_parse_filesize(str_writesize, &writesize, 1, filesize))
113 tst_brk(TBROK, "Invalid write blocks size '%s'", str_writesize);
114
115 if (tst_parse_filesize(str_readsize, &readsize, 1, filesize))
116 tst_brk(TBROK, "Invalid read blocks size '%s'", str_readsize);
117
118 SAFE_STAT(".", &sb);
119 alignment = sb.st_blksize;
120
121 buffsize = readsize > writesize ? readsize : writesize;
122
123 iobuf = SAFE_MEMALIGN(alignment, buffsize);
124
125 children_completed = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
126
127 fd = SAFE_OPEN(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);
128 }
129
cleanup(void)130 static void cleanup(void)
131 {
132 SAFE_CLOSE(fd);
133 }
134
run(void)135 static void run(void)
136 {
137 int i;
138 int fail;
139
140 // Fill the file with a known pattern so that the blocks
141 // on disk can be detected if they become exposed
142 do_buffered_writes(fd, iobuf, filesize, writesize, 1);
143 SAFE_FSYNC(fd);
144 SAFE_FTRUNCATE(fd, 0);
145 SAFE_FSYNC(fd);
146
147 SAFE_FTRUNCATE(fd, filesize);
148
149 *children_completed = 0;
150
151 for (i = 0; i < numchildren; i++) {
152 if (!SAFE_FORK()) {
153 do_buffered_writes(fd, iobuf, filesize, writesize, 0);
154 tst_atomic_add_return(1, children_completed);
155 return;
156 }
157 }
158
159 fail = do_direct_reads(filename, iobuf, filesize, readsize);
160
161 if (fail)
162 tst_res(TFAIL, "Non zero bytes read");
163 else
164 tst_res(TPASS, "All bytes read were zeroed");
165 }
166
167 static struct tst_test test = {
168 .test_all = run,
169 .setup = setup,
170 .cleanup = cleanup,
171 .needs_tmpdir = 1,
172 .forks_child = 1,
173 .options = (struct tst_option[]) {
174 {"n:", &str_numchildren, "Number of threads (default 8)"},
175 {"w:", &str_writesize, "Size of writing blocks (default 32M)"},
176 {"r:", &str_readsize, "Size of reading blocks (default 32M)"},
177 {"s:", &str_filesize, "File size (default 128M)"},
178 {}
179 },
180 .skip_filesystems = (const char *[]) {
181 "tmpfs",
182 NULL
183 },
184 };
185