1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <assert.h>
6 #include <errno.h>
7 #include <lib/zircon-internal/xorshiftrand.h>
8
9 #include "util.h"
10
11 #define FAIL -1
12 #define BUSY 0
13 #define DONE 1
14
15 #define FBUFSIZE 65536
16
17 static_assert(FBUFSIZE == ((FBUFSIZE / sizeof(uint64_t)) * sizeof(uint64_t)),
18 "FBUFSIZE not multiple of uint64_t");
19
20 typedef struct worker worker_t;
21
22 struct worker {
23 worker_t* next;
24 int (*work)(worker_t* w);
25
26 rand64_t rdata;
27 rand32_t rops;
28
29 int fd;
30 int status;
31 uint32_t flags;
32 uint32_t size;
33 uint32_t pos;
34
35 union {
36 uint8_t u8[FBUFSIZE];
37 uint64_t u64[FBUFSIZE / sizeof(uint64_t)];
38 };
39
40 char name[256];
41 };
42
43 static worker_t* all_workers;
44
45 bool worker_new(const char* where, const char* fn,
46 int (*work)(worker_t* w), uint32_t size, uint32_t flags);
47 int worker_writer(worker_t* w);
48 static bool init_environment();
49
50 #define F_RAND_IOSIZE 1
51 #define KB(n) ((n)*1024)
52 #define MB(n) ((n)*1024 * 1024)
53
54 struct {
55 int (*work)(worker_t*);
56 const char* name;
57 uint32_t size;
58 uint32_t flags;
59 } WORK[] = {
60 { worker_writer, "file0000", KB(512), F_RAND_IOSIZE, },
61 { worker_writer, "file0001", MB(10), F_RAND_IOSIZE, },
62 { worker_writer, "file0002", KB(512), F_RAND_IOSIZE, },
63 { worker_writer, "file0003", KB(512), F_RAND_IOSIZE, },
64 { worker_writer, "file0004", KB(512), 0, },
65 { worker_writer, "file0005", MB(20), 0, },
66 { worker_writer, "file0006", KB(512), 0, },
67 { worker_writer, "file0007", KB(512), 0, },
68 };
69
worker_rw(worker_t * w,bool do_read)70 int worker_rw(worker_t* w, bool do_read) {
71 if (w->pos == w->size) {
72 return DONE;
73 }
74
75 // offset into buffer
76 uint32_t off = w->pos % FBUFSIZE;
77
78 // fill our content buffer if it's empty
79 if (off == 0) {
80 for (unsigned n = 0; n < (FBUFSIZE / sizeof(uint64_t)); n++) {
81 w->u64[n] = rand64(&w->rdata);
82 }
83 }
84
85 // data in buffer available to write
86 uint32_t xfer = FBUFSIZE - off;
87
88 // do not exceed our desired size
89 if (xfer > (w->size - w->pos)) {
90 xfer = w->size - w->pos;
91 }
92
93 if ((w->flags & F_RAND_IOSIZE) && (xfer > 3000)) {
94 xfer = 3000 + (rand32(&w->rops) % (xfer - 3000));
95 }
96
97 int r;
98 if (do_read) {
99 uint8_t buffer[FBUFSIZE];
100 if ((r = emu_read(w->fd, buffer, xfer)) < 0) {
101 fprintf(stderr, "worker('%s') read failed @%u: %d\n",
102 w->name, w->pos, errno);
103 return FAIL;
104 }
105
106 if (memcmp(buffer, w->u8 + off, r)) {
107 fprintf(stderr, "worker('%s) verify failed @%u\n",
108 w->name, w->pos);
109 return FAIL;
110 }
111 } else {
112 if ((r = emu_write(w->fd, w->u8 + off, xfer)) < 0) {
113 fprintf(stderr, "worker('%s') write failed @%u: %d\n",
114 w->name, w->pos, errno);
115 return FAIL;
116 }
117 }
118
119 // advance
120 w->pos += r;
121 return BUSY;
122 }
123
worker_verify(worker_t * w)124 int worker_verify(worker_t* w) {
125 int r = worker_rw(w, true);
126 if (r == DONE) {
127 emu_close(w->fd);
128 }
129 return r;
130 }
131
worker_writer(worker_t * w)132 int worker_writer(worker_t* w) {
133 int r = worker_rw(w, false);
134 if (r == DONE) {
135 if (emu_lseek(w->fd, 0, SEEK_SET) != 0) {
136 fprintf(stderr, "worker('%s') seek failed: %s\n",
137 w->name, strerror(errno));
138 return FAIL;
139 }
140 // start at 0 and reset our data generator seed
141 srand64(&w->rdata, w->name);
142 w->pos = 0;
143 w->work = worker_verify;
144 return BUSY;
145 }
146 return r;
147 }
148
worker_new(const char * where,const char * fn,int (* work)(worker_t * w),uint32_t size,uint32_t flags)149 bool worker_new(const char* where, const char* fn,
150 int (*work)(worker_t* w), uint32_t size, uint32_t flags) {
151 worker_t* w = (worker_t*)calloc(1, sizeof(worker_t));
152 ASSERT_NE(w, nullptr);
153
154 snprintf(w->name, sizeof(w->name), "%s%s", where, fn);
155 srand64(&w->rdata, w->name);
156 srand32(&w->rops, w->name);
157 w->size = size;
158 w->work = work;
159 w->flags = flags;
160
161 if ((w->fd = emu_open(w->name, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) {
162 fprintf(stderr, "worker('%s') cannot create file\n", w->name);
163 free(w);
164 return false;
165 }
166
167 if (all_workers) {
168 w->next = all_workers;
169 }
170
171 all_workers = w;
172
173 return true;
174 }
175
do_work()176 int do_work() {
177 uint32_t busy_count = 0;
178 for (worker_t* w = all_workers; w != nullptr; w = w->next) {
179 if (w->status == BUSY) {
180 busy_count++;
181 if ((w->status = w->work(w)) == FAIL) {
182 return FAIL;
183 }
184 if (w->status == DONE) {
185 fprintf(stderr, "worker('%s') finished\n", w->name);
186 }
187 }
188 }
189 return busy_count ? BUSY : DONE;
190 }
191
do_all_work()192 bool do_all_work() {
193 BEGIN_HELPER;
194 for (;;) {
195 int r = do_work();
196 ASSERT_NE(r, FAIL);
197 if (r == DONE) {
198 break;
199 }
200 ASSERT_EQ(run_fsck(), 0);
201 }
202 END_HELPER;
203 }
204
init_environment()205 static bool init_environment() {
206 all_workers = nullptr;
207
208 // assemble workers
209 const char* where = "::";
210 for (unsigned n = 0; n < fbl::count_of(WORK); n++) {
211 ASSERT_TRUE(worker_new(where, WORK[n].name, WORK[n].work,
212 WORK[n].size, WORK[n].flags));
213 }
214 return true;
215 }
216
TestWorkSingleThread(void)217 bool TestWorkSingleThread(void) {
218 BEGIN_TEST;
219
220 ASSERT_TRUE(init_environment());
221 ASSERT_TRUE(do_all_work());
222 worker_t* w = all_workers;
223 worker_t* next;
224 while (w != NULL) {
225 next = w->next;
226 free(w);
227 w = next;
228 }
229
230 END_TEST;
231 }
232
233 RUN_MINFS_TESTS(rw_workers_test,
234 RUN_TEST_MEDIUM(TestWorkSingleThread)
235 )
236