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 17static_assert(FBUFSIZE == ((FBUFSIZE / sizeof(uint64_t)) * sizeof(uint64_t)), 18 "FBUFSIZE not multiple of uint64_t"); 19 20typedef struct worker worker_t; 21 22struct 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 43static worker_t* all_workers; 44 45bool worker_new(const char* where, const char* fn, 46 int (*work)(worker_t* w), uint32_t size, uint32_t flags); 47int worker_writer(worker_t* w); 48static bool init_environment(); 49 50#define F_RAND_IOSIZE 1 51#define KB(n) ((n)*1024) 52#define MB(n) ((n)*1024 * 1024) 53 54struct { 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 70int 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 124int 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 132int 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 149bool 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 176int 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 192bool 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 205static 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 217bool test_work_single_thread(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 233RUN_MINFS_TESTS(rw_workers_test, 234 RUN_TEST_MEDIUM(test_work_single_thread) 235) 236