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