1 // Copyright 2017 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 <atomic>
6 #include <assert.h>
7 #include <fcntl.h>
8 #include <math.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <utility>
14
15 #include <block-client/cpp/client.h>
16 #include <fbl/auto_lock.h>
17 #include <fbl/macros.h>
18 #include <fbl/mutex.h>
19 #include <fbl/unique_fd.h>
20 #include <fbl/unique_ptr.h>
21 #include <lib/fzl/fdio.h>
22 #include <lib/fzl/owned-vmo-mapper.h>
23 #include <lib/zx/fifo.h>
24 #include <lib/zx/thread.h>
25
26 #include <fuchsia/hardware/skipblock/c/fidl.h>
27 #include <lib/zircon-internal/xorshiftrand.h>
28 #include <zircon/assert.h>
29 #include <zircon/device/block.h>
30 #include <zircon/process.h>
31 #include <zircon/syscalls.h>
32 #include <zircon/threads.h>
33
34 namespace {
35
36 constexpr char kUsageMessage[] = R"""(
37 usage: iochk [OPTIONS] <device>
38
39 -bs block_size - number of bytes to treat as a unit (default=device block size)
40 -t thread# - the number of threads to run (default=1)
41 -c block_count - number of blocks to read (default=the whole device)
42 -o offset - block-size offset to start reading from (default=0)
43 -s seed - the seed to use for pseudorandom testing
44 --live-dangerously - skip confirmation prompt
45 --skip - verify skip-block interface instead of block interface
46 )""";
47
48 constexpr uint64_t kBlockHeader = 0xdeadbeef;
49
50 // Flags.
51 bool skip = false;
52 uint32_t start_block = 0;
53 size_t block_size = 0;
54 uint32_t block_count = 0;
55
56 // Constant after init.
57 uint64_t base_seed;
58
59 // Not thread safe.
60 class ProgressBar {
61 public:
ProgressBar()62 explicit ProgressBar()
63 : total_work_(0) {}
ProgressBar(uint32_t block_count,size_t num_threads)64 explicit ProgressBar(uint32_t block_count, size_t num_threads)
65 : total_work_(static_cast<uint32_t>(static_cast<int>(block_count * log(block_count)) *
66 num_threads)) {}
67
68 ProgressBar(const ProgressBar& other) = default;
69 ProgressBar& operator=(const ProgressBar& other) = default;
70
Update(uint32_t was_read)71 void Update(uint32_t was_read) {
72 int old_progress = static_cast<int>(100 * blocks_read_ / total_work_);
73 blocks_read_ += was_read;
74 int progress = static_cast<int>(100 * blocks_read_ / total_work_);
75
76 if (old_progress != progress) {
77 int ticks = 40;
78 char str[ticks + 1];
79 memset(str, ' ', ticks);
80 memset(str, '=', ticks * progress / 100);
81 str[ticks] = '\0';
82 printf("\r[%s] %02d%%", str, progress);
83 fflush(stdout);
84 }
85 if (progress == 100) {
86 printf("\n");
87 }
88 }
89
90 private:
91 uint32_t total_work_;
92 uint32_t blocks_read_ = 0;
93 };
94
95 // Context for thread workers.
96 class WorkContext {
97 public:
WorkContext(ProgressBar progress,bool okay)98 explicit WorkContext(ProgressBar progress, bool okay)
99 : progress(progress), okay_(okay) {}
~WorkContext()100 ~WorkContext() {}
101
102 DISALLOW_COPY_ASSIGN_AND_MOVE(WorkContext);
103
104 // Implementation specific information.
105 struct {
106 block_client::Client client;
107 block_info_t info = {};
108 // File descriptor to device being tested.
109 fbl::unique_fd fd;
110 } block;
111 struct {
112 fuchsia_hardware_skipblock_PartitionInfo info = {};
113 fzl::FdioCaller caller;
114 } skip;
115 // Protects |iochk_failure| and |progress|
116 fbl::Mutex lock;
117 bool iochk_failure = false;
118 ProgressBar progress;
119 bool okay_;
120 };
121
122 // Interface to abstract over block/skip-block device interface differences.
123 class Checker {
124 public:
125 // Fills the device with data based on location in the block.
Fill(uint32_t start,uint32_t count)126 virtual zx_status_t Fill(uint32_t start, uint32_t count) { return ZX_ERR_NOT_SUPPORTED; }
127
128 // Validates that data in specified was region on device is what was written
129 // by Fill.
Check(uint32_t start,uint32_t count)130 virtual zx_status_t Check(uint32_t start, uint32_t count) { return ZX_ERR_NOT_SUPPORTED; }
131
132 virtual ~Checker() = default;
133 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Checker);
134
135 protected:
Checker(void * buffer)136 Checker(void* buffer)
137 : buffer_(buffer) {}
138
GenerateBlockData(int block_idx,size_t length) const139 void GenerateBlockData(int block_idx, size_t length) const {
140 // Block size should be a multiple of sizeof(uint64_t), but assert just to be safe
141 ZX_ASSERT(length % sizeof(uint64_t) == 0);
142
143 rand64_t seed_gen = RAND63SEED(base_seed + block_idx);
144 for (int i = 0; i < 10; i++) {
145 rand64(&seed_gen);
146 }
147 rand64_t data_gen = RAND63SEED(rand64(&seed_gen));
148
149 auto* buf = static_cast<uint64_t*>(buffer_);
150 size_t idx = 0;
151 uint64_t data = kBlockHeader | (static_cast<uint64_t>(block_idx) << 32);
152
153 while (idx < length / sizeof(uint64_t)) {
154 buf[idx] = data;
155 data = rand64(&data_gen);
156 idx++;
157 }
158 }
159
CheckBlockData(int block_idx,size_t length) const160 int CheckBlockData(int block_idx, size_t length) const {
161 rand64_t seed_gen = RAND63SEED(base_seed + block_idx);
162 for (int i = 0; i < 10; i++) {
163 rand64(&seed_gen);
164 }
165 rand64_t data_gen = RAND63SEED(rand64(&seed_gen));
166
167 auto* buf = static_cast<uint64_t*>(buffer_);
168 uint64_t expected = kBlockHeader | (static_cast<uint64_t>(block_idx) << 32);
169 size_t idx = 0;
170
171 while (idx < length / sizeof(uint64_t)) {
172 if (buf[idx] != expected) {
173 printf("initial read verification failed: "
174 "block_idx=%d offset=%zu expected=0x%016lx val=0x%016lx\n",
175 block_idx, idx, expected, buf[idx]);
176 return ZX_ERR_INTERNAL;
177 }
178 idx++;
179 expected = rand64(&data_gen);
180 }
181 return 0;
182 }
183
184 void* buffer_;
185 };
186
187 class BlockChecker : public Checker {
188 public:
Initialize(const fbl::unique_fd & fd,block_info_t info,block_client::Client & client,fbl::unique_ptr<Checker> * checker)189 static zx_status_t Initialize(const fbl::unique_fd& fd, block_info_t info,
190 block_client::Client& client,
191 fbl::unique_ptr<Checker>* checker) {
192 fzl::OwnedVmoMapper mapping;
193 zx_status_t status = mapping.CreateAndMap(block_size, "");
194 if (status != ZX_OK) {
195 printf("Failled to CreateAndMap Vmo\n");
196 return status;
197 }
198
199 zx::vmo dup;
200 status = mapping.vmo().duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
201 if (status != ZX_OK) {
202 printf("cannot duplicate handle\n");
203 return status;
204 }
205
206 size_t s;
207 vmoid_t vmoid;
208 zx_handle_t raw_dup = dup.release();
209 if ((s = ioctl_block_attach_vmo(fd.get(), &raw_dup, &vmoid) != sizeof(vmoid_t))) {
210 printf("cannot attach vmo for init %lu\n", s);
211 return ZX_ERR_IO;
212 }
213
214 groupid_t group = next_txid_.fetch_add(1);
215 ZX_ASSERT(group < MAX_TXN_GROUP_COUNT);
216
217 checker->reset(new BlockChecker(std::move(mapping), info, client, vmoid, group));
218 return ZX_OK;
219 }
220
ResetAtomic()221 static void ResetAtomic() {
222 next_txid_.store(0);
223 }
224
Fill(uint32_t start,uint32_t count)225 virtual zx_status_t Fill(uint32_t start, uint32_t count) override {
226 for (uint32_t block_idx = start; block_idx < count; block_idx++) {
227 uint64_t length = (info_.block_size * info_.block_count) - (block_idx * block_size);
228 if (length > block_size) {
229 length = block_size;
230 }
231
232 GenerateBlockData(block_idx, block_size);
233 block_fifo_request_t request = {
234 .opcode = BLOCKIO_WRITE,
235 .reqid = 0,
236 .group = group_,
237 .vmoid = vmoid_,
238 .length = static_cast<uint32_t>(length / info_.block_size),
239 .vmo_offset = 0,
240 .dev_offset = (block_idx * block_size) / info_.block_size,
241 };
242 zx_status_t st;
243 if ((st = client_.Transaction(&request, 1)) != ZX_OK) {
244 printf("write block_fifo_txn error %d\n", st);
245 return st;
246 }
247 }
248 return ZX_OK;
249 }
250
Check(uint32_t start,uint32_t count)251 virtual zx_status_t Check(uint32_t start, uint32_t count) override {
252 for (uint32_t block_idx = start; block_idx < count; block_idx++) {
253 uint64_t length = (info_.block_size * info_.block_count) - (block_idx * block_size);
254 if (length > block_size) {
255 length = block_size;
256 }
257
258 block_fifo_request_t request = {
259 .opcode = BLOCKIO_READ,
260 .reqid = 0,
261 .group = group_,
262 .vmoid = vmoid_,
263 .length = static_cast<uint32_t>(length / info_.block_size),
264 .vmo_offset = 0,
265 .dev_offset = (block_idx * block_size) / info_.block_size,
266 };
267 zx_status_t st;
268 if ((st = client_.Transaction(&request, 1)) != ZX_OK) {
269 printf("read block_fifo_txn error %d\n", st);
270 return st;
271 }
272 if ((st = CheckBlockData(block_idx, length)) != ZX_OK) {
273 return st;
274 }
275 }
276 return ZX_OK;
277 }
278
279 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(BlockChecker);
280
281 private:
BlockChecker(fzl::OwnedVmoMapper mapper,block_info_t info,block_client::Client & client,vmoid_t vmoid,groupid_t group)282 BlockChecker(fzl::OwnedVmoMapper mapper, block_info_t info,
283 block_client::Client& client, vmoid_t vmoid, groupid_t group)
284 : Checker(mapper.start()), mapper_(std::move(mapper)), info_(info),
285 client_(client), vmoid_(vmoid), group_(group) {}
286 ~BlockChecker() = default;
287
288 static std::atomic<uint16_t> next_txid_;
289
290 fzl::OwnedVmoMapper mapper_;
291 block_info_t info_;
292 block_client::Client& client_;
293 vmoid_t vmoid_;
294 groupid_t group_;
295 };
296
297 std::atomic<uint16_t> BlockChecker::next_txid_;
298
299 class SkipBlockChecker : public Checker {
300 public:
Initialize(fzl::FdioCaller & caller,fuchsia_hardware_skipblock_PartitionInfo info,fbl::unique_ptr<Checker> * checker)301 static zx_status_t Initialize(fzl::FdioCaller& caller,
302 fuchsia_hardware_skipblock_PartitionInfo info,
303 fbl::unique_ptr<Checker>* checker) {
304 fzl::VmoMapper mapping;
305 zx::vmo vmo;
306 zx_status_t status = mapping.CreateAndMap(block_size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
307 nullptr, &vmo);
308 if (status != ZX_OK) {
309 printf("Failled to CreateAndMap Vmo\n");
310 return status;
311 }
312
313 checker->reset(new SkipBlockChecker(std::move(mapping), std::move(vmo), caller, info));
314 return ZX_OK;
315 }
316
Fill(uint32_t start,uint32_t count)317 virtual zx_status_t Fill(uint32_t start, uint32_t count) override {
318 for (uint32_t block_idx = start; block_idx < count; block_idx++) {
319 uint64_t length = (info_.block_size_bytes * info_.partition_block_count) -
320 (block_idx * block_size);
321 if (length > block_size) {
322 length = block_size;
323 }
324
325 zx::vmo dup;
326 zx_status_t st = vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
327 if (st != ZX_OK) {
328 printf("cannot duplicate handle\n");
329 return st;
330 }
331
332 GenerateBlockData(block_idx, block_size);
333 fuchsia_hardware_skipblock_ReadWriteOperation request = {
334 .vmo = dup.release(),
335 .vmo_offset = 0,
336 .block = static_cast<uint32_t>((block_idx * block_size) / info_.block_size_bytes),
337 .block_count = static_cast<uint32_t>(length / info_.block_size_bytes),
338 };
339 bool bad_block_grown;
340 fuchsia_hardware_skipblock_SkipBlockWrite(caller_.borrow_channel(), &request, &st,
341 &bad_block_grown);
342 if (st != ZX_OK) {
343 printf("SkipBlockWrite error %d\n", st);
344 return st;
345 }
346 }
347 return ZX_OK;
348 }
349
Check(uint32_t start,uint32_t count)350 virtual zx_status_t Check(uint32_t start, uint32_t count) override {
351 for (uint32_t block_idx = start; block_idx < count; block_idx++) {
352 uint64_t length = (info_.block_size_bytes * info_.partition_block_count) -
353 (block_idx * block_size);
354 if (length > block_size) {
355 length = block_size;
356 }
357
358 zx::vmo dup;
359 zx_status_t st = vmo_.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
360 if (st != ZX_OK) {
361 printf("cannot duplicate handle\n");
362 return st;
363 }
364
365 fuchsia_hardware_skipblock_ReadWriteOperation request = {
366 .vmo = dup.release(),
367 .vmo_offset = 0,
368 .block = static_cast<uint32_t>((block_idx * block_size) / info_.block_size_bytes),
369 .block_count = static_cast<uint32_t>(length / info_.block_size_bytes),
370 };
371 fuchsia_hardware_skipblock_SkipBlockRead(caller_.borrow_channel(), &request, &st);
372 if (st != ZX_OK) {
373 printf("SkipBlockRead error %d\n", st);
374 return st;
375 }
376 if ((st = CheckBlockData(block_idx, length)) != ZX_OK) {
377 return st;
378 }
379 }
380 return ZX_OK;
381 }
382
383 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SkipBlockChecker);
384
385 private:
SkipBlockChecker(fzl::VmoMapper mapper,zx::vmo vmo,fzl::FdioCaller & caller,fuchsia_hardware_skipblock_PartitionInfo info)386 SkipBlockChecker(fzl::VmoMapper mapper, zx::vmo vmo, fzl::FdioCaller& caller,
387 fuchsia_hardware_skipblock_PartitionInfo info)
388 : Checker(mapper.start()), mapper_(std::move(mapper)), vmo_(std::move(vmo)),
389 caller_(caller), info_(info) {}
390 ~SkipBlockChecker() = default;
391
392 fzl::VmoMapper mapper_;
393 zx::vmo vmo_;
394 fzl::FdioCaller& caller_;
395 fuchsia_hardware_skipblock_PartitionInfo info_;
396 };
397
InitializeChecker(WorkContext & ctx,fbl::unique_ptr<Checker> * checker)398 zx_status_t InitializeChecker(WorkContext& ctx, fbl::unique_ptr<Checker>* checker) {
399 return skip ? SkipBlockChecker::Initialize(ctx.skip.caller, ctx.skip.info, checker)
400 : BlockChecker::Initialize(ctx.block.fd, ctx.block.info, ctx.block.client, checker);
401 }
402
InitializeDevice(WorkContext & ctx)403 zx_status_t InitializeDevice(WorkContext& ctx) {
404 fbl::unique_ptr<Checker> checker;
405 zx_status_t status;
406 if ((status = InitializeChecker(ctx, &checker)) != ZX_OK) {
407 printf("Failed to alloc resources to init device\n");
408 return status;
409 }
410
411 printf("writing test data to device...\n");
412 fflush(stdout);
413 if ((status = checker->Fill(start_block, block_count)) != ZX_OK) {
414 printf("failed to write test data\n");
415 return status;
416 }
417 printf("done\n");
418
419 printf("verifying test data...\n");
420 fflush(stdout);
421 if ((status = checker->Check(start_block, block_count)) != ZX_OK) {
422 printf("failed to verify test data\n");
423 return status;
424 }
425 printf("done\n");
426
427 return 0;
428 }
429
DoWork(void * arg)430 int DoWork(void* arg) {
431 auto* ctx = static_cast<WorkContext*>(arg);
432
433 fbl::unique_ptr<Checker> checker;
434 zx_status_t status;
435 if ((status = InitializeChecker(*ctx, &checker)) != ZX_OK) {
436 printf("Failed to alloc resources to init device\n");
437 return status;
438 }
439
440 auto tid = static_cast<uintptr_t>(zx::thread::self()->get());
441 rand32_t seed_gen = RAND32SEED(static_cast<uint32_t>(base_seed + tid));
442 for (int i = 0; i < 20; i++) {
443 }
444 rand32_t work_gen = RAND32SEED(rand32(&seed_gen));
445 // The expected number of random pages we need to hit all of them is
446 // approx n*log(n) (the coupon collector problem)
447 uint32_t blocks_left = static_cast<uint32_t>(block_count * log(block_count));
448
449 while (blocks_left > 0 && !ctx->iochk_failure) {
450 uint32_t to_read = (rand32(&work_gen) % blocks_left) + 1;
451 uint32_t work_offset = rand32(&work_gen) % block_count;
452 if (work_offset + to_read > block_count) {
453 to_read = block_count - work_offset;
454 }
455
456 zx_status_t status;
457 if (rand32(&work_gen) % 2) {
458 status = checker->Check(start_block + work_offset, to_read);
459 } else {
460 status = checker->Fill(start_block + work_offset, to_read);
461 }
462
463 fbl::AutoLock al(&ctx->lock);
464 if (status != ZX_OK) {
465 ctx->iochk_failure = true;
466 } else if (!ctx->iochk_failure) {
467 ctx->progress.Update(to_read);
468 blocks_left -= to_read;
469 }
470 }
471
472 return 0;
473 }
474
Number(const char * str)475 uint64_t Number(const char* str) {
476 char* end;
477 uint64_t n = strtoull(str, &end, 10);
478
479 uint64_t m = 1;
480 switch (*end) {
481 case 'G':
482 case 'g':
483 m = 1024 * 1024 * 1024;
484 break;
485 case 'M':
486 case 'm':
487 m = 1024 * 1024;
488 break;
489 case 'K':
490 case 'k':
491 m = 1024;
492 break;
493 }
494 return m * n;
495 }
496
Usage(void)497 int Usage(void) {
498 printf("%s\n", kUsageMessage);
499 return -1;
500 }
501
502 } // namespace
503
iochk(int argc,char ** argv)504 int iochk(int argc, char** argv) {
505 const char* device = argv[argc - 1];
506 fbl::unique_fd fd(open(device, O_RDONLY));
507 if (fd.get() < 0) {
508 printf("cannot open '%s'\n", device);
509 return Usage();
510 }
511
512 bool seed_set = false;
513 size_t num_threads = 1;
514 bool confirmed = false;
515 char** end = argv + argc - 1;
516 argv++;
517 while (argv < end) {
518 if (strcmp(*argv, "-t") == 0) {
519 num_threads = atoi(argv[1]);
520 argv += 2;
521 } else if (strcmp(*argv, "-c") == 0) {
522 block_count = atoi(argv[1]);
523 argv += 2;
524 } else if (strcmp(*argv, "-o") == 0) {
525 start_block = atoi(argv[1]);
526 argv += 2;
527 } else if (strcmp(*argv, "-bs") == 0) {
528 block_size = Number(argv[1]);
529 argv += 2;
530 } else if (strcmp(*argv, "-s") == 0) {
531 base_seed = atoll(argv[1]);
532 seed_set = true;
533 argv += 2;
534 } else if (strcmp(*argv, "--live-dangerously") == 0) {
535 confirmed = true;
536 argv++;
537 } else if (strcmp(*argv, "--skip") == 0) {
538 skip = true;
539 argv++;
540 } else if (strcmp(*argv, "-h") == 0 ||
541 strcmp(*argv, "--help") == 0) {
542 return Usage();
543 } else {
544 printf("Invalid arg %s\n", *argv);
545 return Usage();
546 }
547 }
548
549 if (!confirmed) {
550 constexpr char kWarning[] = "\033[0;31mWARNING\033[0m";
551 printf("%s: iochk is a destructive operation.\n", kWarning);
552 printf("%s: All data on %s in the given range will be overwritten.\n",
553 kWarning, device);
554 printf("%s: Type 'y' to continue, 'n' or ESC to cancel:\n", kWarning);
555 for (;;) {
556 char c;
557 ssize_t r = read(STDIN_FILENO, &c, 1);
558 if (r < 0) {
559 printf("Error reading from stdin\n");
560 return -1;
561 }
562 if (c == 'y' || c == 'Y') {
563 break;
564 } else if (c == 'n' || c == 'N' || c == 27) {
565 return 0;
566 }
567 }
568 }
569
570 if (!seed_set) {
571 base_seed = zx_clock_get_monotonic();
572 }
573 printf("seed is %ld\n", base_seed);
574
575 WorkContext ctx(ProgressBar(), false);
576
577 if (skip) {
578 ctx.skip.caller.reset(std::move(fd));
579 // Skip Block Device Setup.
580 zx_status_t status;
581 fuchsia_hardware_skipblock_PartitionInfo info;
582 fuchsia_hardware_skipblock_SkipBlockGetPartitionInfo(ctx.skip.caller.borrow_channel(),
583 &status, &info);
584 if (status != ZX_OK) {
585 printf("unable to get skip-block partition info: %d\n", status);
586 printf("fd: %d\n", ctx.skip.caller.release().get());
587 return -1;
588 }
589 printf("opened %s - block_size_bytes=%lu, partition_block_count=%u\n", device,
590 info.block_size_bytes, info.partition_block_count);
591
592 ctx.skip.info = info;
593
594 if (block_size == 0) {
595 block_size = info.block_size_bytes;
596 } else if (block_size % info.block_size_bytes != 0) {
597 printf("block-size is not a multiple of device block size\n");
598 return -1;
599 }
600 uint32_t dev_blocks_per_block = static_cast<uint32_t>(block_size / info.block_size_bytes);
601
602 if (dev_blocks_per_block * start_block >= info.partition_block_count) {
603 printf("offset past end of device\n");
604 return -1;
605 }
606
607 if (block_count == 0) {
608 block_count = static_cast<uint32_t>((info.partition_block_count +
609 dev_blocks_per_block - 1) /
610 dev_blocks_per_block);
611 } else if (dev_blocks_per_block * (block_count + start_block) >=
612 dev_blocks_per_block + info.partition_block_count) {
613 // Don't allow blocks to start past the end of the device
614 printf("block_count+offset too large\n");
615 return -1;
616 }
617 } else {
618 ctx.block.fd = std::move(fd);
619 // Block Device Setup.
620 block_info_t info;
621 if (ioctl_block_get_info(ctx.block.fd.get(), &info) != sizeof(info)) {
622 printf("unable to get block info\n");
623 return -1;
624 }
625 printf("opened %s - block_size=%u, block_count=%lu\n",
626 device, info.block_size, info.block_count);
627
628 ctx.block.info = info;
629
630 if (block_size == 0) {
631 block_size = static_cast<uint32_t>(info.block_size);
632 } else if (block_size % info.block_size != 0) {
633 printf("block-size is not a multiple of device block size\n");
634 return -1;
635 }
636 uint32_t dev_blocks_per_block = static_cast<uint32_t>(block_size / info.block_size);
637
638 if (dev_blocks_per_block * start_block >= info.block_count) {
639 printf("offset past end of device\n");
640 return -1;
641 }
642
643 if (block_count == 0) {
644 block_count = static_cast<uint32_t>((info.block_count + dev_blocks_per_block - 1) /
645 dev_blocks_per_block);
646 } else if (dev_blocks_per_block * (block_count + start_block) >=
647 dev_blocks_per_block + info.block_count) {
648 // Don't allow blocks to start past the end of the device
649 printf("block_count+offset too large\n");
650 return -1;
651 }
652
653 if (info.max_transfer_size < block_size) {
654 printf("block-size is larger than max transfer size (%d)\n", info.max_transfer_size);
655 return -1;
656 }
657
658 zx::fifo fifo;
659 if (ioctl_block_get_fifos(ctx.block.fd.get(),
660 fifo.reset_and_get_address()) != sizeof(fifo)) {
661 printf("cannot get fifo for device\n");
662 return -1;
663 }
664
665 if (block_client::Client::Create(std::move(fifo), &ctx.block.client) != ZX_OK) {
666 printf("cannot create block client for device\n");
667 return -1;
668 }
669
670 BlockChecker::ResetAtomic();
671 }
672
673 ctx.progress = ProgressBar(block_count, num_threads);
674
675 if (InitializeDevice(ctx)) {
676 printf("device initialization failed\n");
677 return -1;
678 }
679
680 // Reset before launching any worker threads.
681 if (!skip) {
682 BlockChecker::ResetAtomic();
683 }
684
685 printf("starting worker threads...\n");
686 thrd_t threads[num_threads];
687
688 if (num_threads > MAX_TXN_GROUP_COUNT) {
689 printf("number of threads capped at %u\n", MAX_TXN_GROUP_COUNT);
690 num_threads = MAX_TXN_GROUP_COUNT;
691 }
692
693 for (auto& thread : threads) {
694 if (thrd_create(&thread, DoWork, &ctx) != thrd_success) {
695 printf("thread creation failed\n");
696 return -1;
697 }
698 }
699
700 for (auto& thread : threads) {
701 thrd_join(thread, nullptr);
702 }
703
704 // Reset after launching worker threads to avoid hitting the capacity.
705 if (!skip) {
706 BlockChecker::ResetAtomic();
707 }
708
709 if (!ctx.iochk_failure) {
710 printf("re-verifying device...\n");
711 fflush(stdout);
712 fbl::unique_ptr<Checker> checker;
713 zx_status_t status;
714 if ((status = InitializeChecker(ctx, &checker)) != ZX_OK) {
715 printf("failed to initialize verification thread\n");
716 return status;
717 }
718 if (checker->Check(start_block, block_count) != ZX_OK) {
719 printf("failed to re-verify test data\n");
720 ctx.iochk_failure = true;
721 } else {
722 printf("done\n");
723 }
724 }
725
726 if (!ctx.iochk_failure) {
727 printf("iochk completed successfully\n");
728 return 0;
729 } else {
730 printf("iochk failed (seed was %ld)\n", base_seed);
731 return -1;
732 }
733 }
734
main(int argc,char ** argv)735 int main(int argc, char** argv) {
736 if (argc < 2) {
737 return Usage();
738 }
739
740 int res = iochk(argc, argv);
741 return res;
742 }
743