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 <errno.h> 6#include <fcntl.h> 7#include <limits.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/stat.h> 12#include <sys/types.h> 13#include <time.h> 14#include <threads.h> 15#include <unistd.h> 16 17#include <block-client/client.h> 18#include <zircon/device/block.h> 19#include <zircon/syscalls.h> 20#include <fbl/algorithm.h> 21#include <fbl/alloc_checker.h> 22#include <fbl/array.h> 23#include <fbl/limits.h> 24#include <fbl/unique_ptr.h> 25#include <fbl/unique_fd.h> 26#include <pretty/hexdump.h> 27#include <unittest/unittest.h> 28 29#include <blktest/blktest.h> 30 31namespace tests { 32 33static int get_testdev(uint64_t* blk_size, uint64_t* blk_count) { 34 const char* blkdev_path = getenv(BLKTEST_BLK_DEV); 35 ASSERT_NONNULL(blkdev_path, "No test device specified"); 36 // Open the block device 37 int fd = open(blkdev_path, O_RDWR); 38 if (fd < 0) { 39 printf("OPENING BLKDEV (path=%s) FAILURE. Errno: %d\n", blkdev_path, errno); 40 } 41 ASSERT_GE(fd, 0, "Could not open block device"); 42 block_info_t info; 43 ssize_t rc = ioctl_block_get_info(fd, &info); 44 ASSERT_GE(rc, 0, "Could not get block size"); 45 *blk_size = info.block_size; 46 *blk_count = info.block_count; 47 return fd; 48} 49 50static bool blkdev_test_simple(void) { 51 BEGIN_TEST; 52 53 uint64_t blk_size, blk_count; 54 fbl::unique_fd fd(get_testdev(&blk_size, &blk_count)); 55 int64_t buffer_size = blk_size * 2; 56 57 fbl::AllocChecker checker; 58 fbl::unique_ptr<uint8_t[]> buf(new (&checker) uint8_t[buffer_size]); 59 ASSERT_TRUE(checker.check()); 60 fbl::unique_ptr<uint8_t[]> out(new (&checker) uint8_t[buffer_size]); 61 ASSERT_TRUE(checker.check()); 62 63 memset(buf.get(), 'a', sizeof(buf)); 64 memset(out.get(), 0, sizeof(out)); 65 66 // Write three blocks. 67 ASSERT_EQ(write(fd.get(), buf.get(), buffer_size), buffer_size); 68 ASSERT_EQ(write(fd.get(), buf.get(), buffer_size / 2), buffer_size / 2); 69 70 // Seek to the start of the device and read the contents 71 ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0, ""); 72 ASSERT_EQ(read(fd.get(), out.get(), buffer_size), buffer_size); 73 ASSERT_EQ(memcmp(out.get(), buf.get(), buffer_size), 0); 74 ASSERT_EQ(read(fd.get(), out.get(), buffer_size / 2), buffer_size / 2); 75 ASSERT_EQ(memcmp(out.get(), buf.get(), buffer_size / 2), 0); 76 77 END_TEST; 78} 79 80bool blkdev_test_bad_requests(void) { 81 BEGIN_TEST; 82 83 uint64_t blk_size, blk_count; 84 fbl::unique_fd fd(get_testdev(&blk_size, &blk_count)); 85 86 fbl::AllocChecker checker; 87 fbl::unique_ptr<uint8_t[]> buf(new (&checker) uint8_t[blk_size * 4]); 88 ASSERT_TRUE(checker.check()); 89 memset(buf.get(), 'a', blk_size * 4); 90 91 // Read / write non-multiples of the block size 92 ASSERT_EQ(write(fd.get(), buf.get(), blk_size - 1), -1); 93 ASSERT_EQ(write(fd.get(), buf.get(), blk_size / 2), -1); 94 ASSERT_EQ(write(fd.get(), buf.get(), blk_size * 2 - 1), -1); 95 ASSERT_EQ(read(fd.get(), buf.get(), blk_size - 1), -1); 96 ASSERT_EQ(read(fd.get(), buf.get(), blk_size / 2), -1); 97 ASSERT_EQ(read(fd.get(), buf.get(), blk_size * 2 - 1), -1); 98 99 // Read / write from unaligned offset 100 ASSERT_EQ(lseek(fd.get(), 1, SEEK_SET), 1); 101 ASSERT_EQ(write(fd.get(), buf.get(), blk_size), -1); 102 ASSERT_EQ(errno, EINVAL); 103 ASSERT_EQ(read(fd.get(), buf.get(), blk_size), -1); 104 ASSERT_EQ(errno, EINVAL); 105 106 // Read / write from beyond end of device 107 off_t dev_size = blk_size * blk_count; 108 ASSERT_EQ(lseek(fd.get(), dev_size, SEEK_SET), dev_size); 109 ASSERT_EQ(write(fd.get(), buf.get(), blk_size), -1); 110 ASSERT_EQ(read(fd.get(), buf.get(), blk_size), -1); 111 112 END_TEST; 113} 114 115#if 0 116bool blkdev_test_multiple(void) { 117 uint8_t buf[PAGE_SIZE]; 118 uint8_t out[PAGE_SIZE]; 119 120 BEGIN_TEST; 121 int fd1 = get_testdev("blkdev-test-A", PAGE_SIZE, 512); 122 int fd2 = get_testdev("blkdev-test-B", PAGE_SIZE, 512); 123 124 // Write 'a' to fd1, write 'b', to fd2 125 memset(buf, 'a', sizeof(buf)); 126 ASSERT_EQ(write(fd1, buf, sizeof(buf)), (ssize_t) sizeof(buf), ""); 127 memset(buf, 'b', sizeof(buf)); 128 ASSERT_EQ(write(fd2, buf, sizeof(buf)), (ssize_t) sizeof(buf), ""); 129 130 ASSERT_EQ(lseek(fd1, 0, SEEK_SET), 0, ""); 131 ASSERT_EQ(lseek(fd2, 0, SEEK_SET), 0, ""); 132 133 // Read 'b' from fd2, read 'a' from fd1 134 ASSERT_EQ(read(fd2, out, sizeof(buf)), (ssize_t) sizeof(buf), ""); 135 ASSERT_EQ(memcmp(out, buf, sizeof(out)), 0, ""); 136 close(fd2); 137 138 memset(buf, 'a', sizeof(buf)); 139 ASSERT_EQ(read(fd1, out, sizeof(buf)), (ssize_t) sizeof(buf), ""); 140 ASSERT_EQ(memcmp(out, buf, sizeof(out)), 0, ""); 141 close(fd1); 142 143 END_TEST; 144} 145#endif 146 147bool blkdev_test_fifo_no_op(void) { 148 // Get a FIFO connection to a blkdev and immediately close it 149 BEGIN_TEST; 150 uint64_t blk_size, blk_count; 151 int fd = get_testdev(&blk_size, &blk_count); 152 zx_handle_t fifo; 153 ssize_t expected = sizeof(fifo); 154 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 155 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 156 close(fd); 157 END_TEST; 158} 159 160static void fill_random(uint8_t* buf, uint64_t size) { 161 for (size_t i = 0; i < size; i++) { 162 buf[i] = static_cast<uint8_t>(rand()); 163 } 164} 165 166bool blkdev_test_fifo_basic(void) { 167 BEGIN_TEST; 168 uint64_t blk_size, blk_count; 169 // Set up the initial handshake connection with the blkdev 170 int fd = get_testdev(&blk_size, &blk_count); 171 zx_handle_t fifo; 172 ssize_t expected = sizeof(fifo); 173 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 174 groupid_t group = 0; 175 176 // Create an arbitrary VMO, fill it with some stuff 177 const uint64_t vmo_size = blk_size * 3; 178 zx_handle_t vmo; 179 ASSERT_EQ(zx_vmo_create(vmo_size, 0, &vmo), ZX_OK, "Failed to create VMO"); 180 fbl::AllocChecker ac; 181 fbl::unique_ptr<uint8_t[]> buf(new (&ac) uint8_t[vmo_size]); 182 ASSERT_TRUE(ac.check(), ""); 183 fill_random(buf.get(), vmo_size); 184 185 ASSERT_EQ(zx_vmo_write(vmo, buf.get(), 0, vmo_size), ZX_OK, ""); 186 187 // Send a handle to the vmo to the block device, get a vmoid which identifies it 188 vmoid_t vmoid; 189 expected = sizeof(vmoid_t); 190 zx_handle_t xfer_vmo; 191 ASSERT_EQ(zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK, ""); 192 ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &vmoid), expected, 193 "Failed to attach vmo"); 194 195 // Batch write the VMO to the blkdev 196 // Split it into two requests, spread across the disk 197 block_fifo_request_t requests[2]; 198 requests[0].group = group; 199 requests[0].vmoid = vmoid; 200 requests[0].opcode = BLOCKIO_WRITE; 201 requests[0].length = 1; 202 requests[0].vmo_offset = 0; 203 requests[0].dev_offset = 0; 204 205 requests[1].group = group; 206 requests[1].vmoid = vmoid; 207 requests[1].opcode = BLOCKIO_WRITE; 208 requests[1].length = 2; 209 requests[1].vmo_offset = 1; 210 requests[1].dev_offset = 100; 211 212 fifo_client_t* client; 213 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 214 ASSERT_EQ(block_fifo_txn(client, &requests[0], fbl::count_of(requests)), ZX_OK, ""); 215 216 // Empty the vmo, then read the info we just wrote to the disk 217 fbl::unique_ptr<uint8_t[]> out(new (&ac) uint8_t[vmo_size]()); 218 ASSERT_TRUE(ac.check(), ""); 219 220 ASSERT_EQ(zx_vmo_write(vmo, out.get(), 0, vmo_size), ZX_OK, ""); 221 requests[0].opcode = BLOCKIO_READ; 222 requests[1].opcode = BLOCKIO_READ; 223 ASSERT_EQ(block_fifo_txn(client, &requests[0], fbl::count_of(requests)), ZX_OK, ""); 224 ASSERT_EQ(zx_vmo_read(vmo, out.get(), 0, vmo_size), ZX_OK, ""); 225 ASSERT_EQ(memcmp(buf.get(), out.get(), blk_size * 3), 0, "Read data not equal to written data"); 226 227 // Close the current vmo 228 requests[0].opcode = BLOCKIO_CLOSE_VMO; 229 ASSERT_EQ(block_fifo_txn(client, &requests[0], 1), ZX_OK, ""); 230 231 ASSERT_EQ(zx_handle_close(vmo), ZX_OK, ""); 232 block_fifo_release_client(client); 233 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 234 close(fd); 235 END_TEST; 236} 237 238bool blkdev_test_fifo_whole_disk(void) { 239 BEGIN_TEST; 240 uint64_t blk_size, blk_count; 241 // Set up the initial handshake connection with the blkdev 242 int fd = get_testdev(&blk_size, &blk_count); 243 zx_handle_t fifo; 244 ssize_t expected = sizeof(fifo); 245 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 246 groupid_t group = 0; 247 248 // Create an arbitrary VMO, fill it with some stuff 249 uint64_t vmo_size = blk_size * blk_count; 250 zx_handle_t vmo; 251 ASSERT_EQ(zx_vmo_create(vmo_size, 0, &vmo), ZX_OK, "Failed to create VMO"); 252 fbl::AllocChecker ac; 253 fbl::unique_ptr<uint8_t[]> buf(new (&ac) uint8_t[vmo_size]); 254 ASSERT_TRUE(ac.check(), ""); 255 fill_random(buf.get(), vmo_size); 256 257 ASSERT_EQ(zx_vmo_write(vmo, buf.get(), 0, vmo_size), ZX_OK, ""); 258 259 // Send a handle to the vmo to the block device, get a vmoid which identifies it 260 vmoid_t vmoid; 261 expected = sizeof(vmoid_t); 262 zx_handle_t xfer_vmo; 263 ASSERT_EQ(zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK, ""); 264 ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &vmoid), expected, 265 "Failed to attach vmo"); 266 267 // Batch write the VMO to the blkdev 268 block_fifo_request_t request; 269 request.group = group; 270 request.vmoid = vmoid; 271 request.opcode = BLOCKIO_WRITE; 272 request.length = static_cast<uint32_t>(blk_count); 273 request.vmo_offset = 0; 274 request.dev_offset = 0; 275 276 fifo_client_t* client; 277 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 278 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, ""); 279 280 // Empty the vmo, then read the info we just wrote to the disk 281 fbl::unique_ptr<uint8_t[]> out(new (&ac) uint8_t[vmo_size]()); 282 ASSERT_TRUE(ac.check(), ""); 283 284 ASSERT_EQ(zx_vmo_write(vmo, out.get(), 0, vmo_size), ZX_OK, ""); 285 request.opcode = BLOCKIO_READ; 286 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, ""); 287 ASSERT_EQ(zx_vmo_read(vmo, out.get(), 0, vmo_size), ZX_OK, ""); 288 ASSERT_EQ(memcmp(buf.get(), out.get(), blk_size * 3), 0, "Read data not equal to written data"); 289 290 // Close the current vmo 291 request.opcode = BLOCKIO_CLOSE_VMO; 292 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, ""); 293 294 ASSERT_EQ(zx_handle_close(vmo), ZX_OK, ""); 295 block_fifo_release_client(client); 296 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 297 close(fd); 298 END_TEST; 299} 300 301typedef struct { 302 uint64_t vmo_size; 303 zx_handle_t vmo; 304 vmoid_t vmoid; 305 fbl::unique_ptr<uint8_t[]> buf; 306} test_vmo_object_t; 307 308// Creates a VMO, fills it with data, and gives it to the block device. 309bool create_vmo_helper(int fd, test_vmo_object_t* obj, size_t kBlockSize) { 310 obj->vmo_size = kBlockSize + (rand() % 5) * kBlockSize; 311 ASSERT_EQ(zx_vmo_create(obj->vmo_size, 0, &obj->vmo), ZX_OK, 312 "Failed to create vmo"); 313 fbl::AllocChecker ac; 314 obj->buf.reset(new (&ac) uint8_t[obj->vmo_size]); 315 ASSERT_TRUE(ac.check(), ""); 316 fill_random(obj->buf.get(), obj->vmo_size); 317 ASSERT_EQ(zx_vmo_write(obj->vmo, obj->buf.get(), 0, obj->vmo_size), 318 ZX_OK, "Failed to write to vmo"); 319 320 ssize_t expected = sizeof(vmoid_t); 321 zx_handle_t xfer_vmo; 322 ASSERT_EQ(zx_handle_duplicate(obj->vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK, 323 "Failed to duplicate vmo"); 324 ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &obj->vmoid), expected, 325 "Failed to attach vmo"); 326 return true; 327} 328 329// Write all vmos in a striped pattern on disk. 330// For objs.size() == 10, 331// i = 0 will write vmo block 0, 1, 2, 3... to dev block 0, 10, 20, 30... 332// i = 1 will write vmo block 0, 1, 2, 3... to dev block 1, 11, 21, 31... 333bool write_striped_vmo_helper(fifo_client_t* client, test_vmo_object_t* obj, size_t i, size_t objs, 334 groupid_t group, size_t kBlockSize) { 335 // Make a separate request for each block 336 size_t blocks = obj->vmo_size / kBlockSize; 337 fbl::AllocChecker ac; 338 fbl::Array<block_fifo_request_t> requests(new (&ac) block_fifo_request_t[blocks], blocks); 339 ASSERT_TRUE(ac.check(), ""); 340 for (size_t b = 0; b < blocks; b++) { 341 requests[b].group = group; 342 requests[b].vmoid = obj->vmoid; 343 requests[b].opcode = BLOCKIO_WRITE; 344 requests[b].length = 1; 345 requests[b].vmo_offset = b; 346 requests[b].dev_offset = i + b * objs; 347 } 348 // Write entire vmos at once 349 ASSERT_EQ(block_fifo_txn(client, &requests[0], requests.size()), ZX_OK, ""); 350 return true; 351} 352 353// Verifies the result from "write_striped_vmo_helper" 354bool read_striped_vmo_helper(fifo_client_t* client, test_vmo_object_t* obj, size_t i, size_t objs, 355 groupid_t group, size_t kBlockSize) { 356 // First, empty out the VMO 357 fbl::AllocChecker ac; 358 fbl::unique_ptr<uint8_t[]> out(new (&ac) uint8_t[obj->vmo_size]()); 359 ASSERT_TRUE(ac.check(), ""); 360 ASSERT_EQ(zx_vmo_write(obj->vmo, out.get(), 0, obj->vmo_size), 361 ZX_OK, ""); 362 363 // Next, read to the vmo from the disk 364 size_t blocks = obj->vmo_size / kBlockSize; 365 fbl::Array<block_fifo_request_t> requests(new (&ac) block_fifo_request_t[blocks], blocks); 366 ASSERT_TRUE(ac.check(), ""); 367 for (size_t b = 0; b < blocks; b++) { 368 requests[b].group = group; 369 requests[b].vmoid = obj->vmoid; 370 requests[b].opcode = BLOCKIO_READ; 371 requests[b].length = 1; 372 requests[b].vmo_offset = b; 373 requests[b].dev_offset = i + b * objs; 374 } 375 // Read entire vmos at once 376 ASSERT_EQ(block_fifo_txn(client, &requests[0], requests.size()), ZX_OK, ""); 377 378 // Finally, write from the vmo to an out buffer, where we can compare 379 // the results with the input buffer. 380 ASSERT_EQ(zx_vmo_read(obj->vmo, out.get(), 0, obj->vmo_size), 381 ZX_OK, ""); 382 ASSERT_EQ(memcmp(obj->buf.get(), out.get(), obj->vmo_size), 0, 383 "Read data not equal to written data"); 384 return true; 385} 386 387// Tears down an object created by "create_vmo_helper". 388bool close_vmo_helper(fifo_client_t* client, test_vmo_object_t* obj, groupid_t group) { 389 block_fifo_request_t request; 390 request.group = group; 391 request.vmoid = obj->vmoid; 392 request.opcode = BLOCKIO_CLOSE_VMO; 393 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, ""); 394 ASSERT_EQ(zx_handle_close(obj->vmo), ZX_OK, ""); 395 return true; 396} 397 398bool blkdev_test_fifo_multiple_vmo(void) { 399 BEGIN_TEST; 400 // Set up the initial handshake connection with the blkdev 401 uint64_t blk_size, blk_count; 402 int fd = get_testdev(&blk_size, &blk_count); 403 zx_handle_t fifo; 404 ssize_t expected = sizeof(fifo); 405 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 406 groupid_t group = 0; 407 fifo_client_t* client; 408 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 409 410 // Create multiple VMOs 411 fbl::AllocChecker ac; 412 fbl::Array<test_vmo_object_t> objs(new (&ac) test_vmo_object_t[10](), 10); 413 ASSERT_TRUE(ac.check(), ""); 414 for (size_t i = 0; i < objs.size(); i++) { 415 ASSERT_TRUE(create_vmo_helper(fd, &objs[i], blk_size), ""); 416 } 417 418 for (size_t i = 0; i < objs.size(); i++) { 419 ASSERT_TRUE(write_striped_vmo_helper(client, &objs[i], i, objs.size(), group, blk_size), ""); 420 } 421 422 for (size_t i = 0; i < objs.size(); i++) { 423 ASSERT_TRUE(read_striped_vmo_helper(client, &objs[i], i, objs.size(), group, blk_size), ""); 424 } 425 426 for (size_t i = 0; i < objs.size(); i++) { 427 ASSERT_TRUE(close_vmo_helper(client, &objs[i], group), ""); 428 } 429 430 block_fifo_release_client(client); 431 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 432 close(fd); 433 END_TEST; 434} 435 436typedef struct { 437 test_vmo_object_t* obj; 438 size_t i; 439 size_t objs; 440 int fd; 441 fifo_client_t* client; 442 groupid_t group; 443 size_t kBlockSize; 444} test_thread_arg_t; 445 446int fifo_vmo_thread(void* arg) { 447 test_thread_arg_t* fifoarg = (test_thread_arg_t*) arg; 448 test_vmo_object_t* obj = fifoarg->obj; 449 size_t i = fifoarg->i; 450 size_t objs = fifoarg->objs; 451 int fd = fifoarg->fd; 452 fifo_client_t* client = fifoarg->client; 453 groupid_t group = fifoarg->group; 454 size_t kBlockSize = fifoarg->kBlockSize; 455 456 ASSERT_TRUE(create_vmo_helper(fd, obj, kBlockSize), ""); 457 ASSERT_TRUE(write_striped_vmo_helper(client, obj, i, objs, group, kBlockSize), ""); 458 ASSERT_TRUE(read_striped_vmo_helper(client, obj, i, objs, group, kBlockSize), ""); 459 ASSERT_TRUE(close_vmo_helper(client, obj, group), ""); 460 return 0; 461} 462 463bool blkdev_test_fifo_multiple_vmo_multithreaded(void) { 464 BEGIN_TEST; 465 // Set up the initial handshake connection with the blkdev 466 uint64_t kBlockSize, blk_count; 467 int fd = get_testdev(&kBlockSize, &blk_count); 468 zx_handle_t fifo; 469 ssize_t expected = sizeof(fifo); 470 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 471 fifo_client_t* client; 472 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 473 474 // Create multiple VMOs 475 size_t num_threads = MAX_TXN_GROUP_COUNT; 476 fbl::AllocChecker ac; 477 fbl::Array<test_vmo_object_t> objs(new (&ac) test_vmo_object_t[num_threads](), num_threads); 478 ASSERT_TRUE(ac.check(), ""); 479 480 fbl::Array<thrd_t> threads(new (&ac) thrd_t[num_threads](), num_threads); 481 ASSERT_TRUE(ac.check(), ""); 482 483 fbl::Array<test_thread_arg_t> thread_args(new (&ac) test_thread_arg_t[num_threads](), 484 num_threads); 485 ASSERT_TRUE(ac.check(), ""); 486 487 for (size_t i = 0; i < num_threads; i++) { 488 // Yes, this does create a bunch of duplicate fields, but it's an easy way to 489 // transfer some data without creating global variables. 490 thread_args[i].obj = &objs[i]; 491 thread_args[i].i = i; 492 thread_args[i].objs = objs.size(); 493 thread_args[i].fd = fd; 494 thread_args[i].client = client; 495 thread_args[i].group = static_cast<groupid_t>(i); 496 thread_args[i].kBlockSize = kBlockSize; 497 ASSERT_EQ(thrd_create(&threads[i], fifo_vmo_thread, &thread_args[i]), 498 thrd_success, ""); 499 } 500 501 for (size_t i = 0; i < num_threads; i++) { 502 int res; 503 ASSERT_EQ(thrd_join(threads[i], &res), thrd_success, ""); 504 ASSERT_EQ(res, 0, ""); 505 } 506 507 block_fifo_release_client(client); 508 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 509 close(fd); 510 END_TEST; 511} 512 513bool blkdev_test_fifo_unclean_shutdown(void) { 514 BEGIN_TEST; 515 // Set up the blkdev 516 uint64_t kBlockSize, blk_count; 517 int fd = get_testdev(&kBlockSize, &blk_count); 518 519 // Create a connection to the blkdev 520 zx_handle_t fifo; 521 ssize_t expected = sizeof(fifo); 522 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 523 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), ZX_ERR_ALREADY_BOUND, 524 "Expected fifo to already be bound"); 525 fifo_client_t* client; 526 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 527 groupid_t group = 0; 528 529 // Create multiple VMOs 530 fbl::AllocChecker ac; 531 fbl::Array<test_vmo_object_t> objs(new (&ac) test_vmo_object_t[10](), 10); 532 ASSERT_TRUE(ac.check(), ""); 533 for (size_t i = 0; i < objs.size(); i++) { 534 ASSERT_TRUE(create_vmo_helper(fd, &objs[i], kBlockSize), ""); 535 } 536 537 // Now that we've set up the connection for a few VMOs, shut down the fifo 538 ASSERT_EQ(zx_handle_close(fifo), ZX_OK, ""); 539 540 // Attempting to batch any operations to the fifo should fail 541 block_fifo_request_t request; 542 request.group = group; 543 request.vmoid = objs[0].vmoid; 544 request.opcode = BLOCKIO_CLOSE_VMO; 545 ASSERT_NE(block_fifo_txn(client, &request, 1), ZX_OK, 546 "Expected operation to fail after closing FIFO"); 547 548 // Free the dead client 549 block_fifo_release_client(client); 550 551 // Give the block server a moment to realize our side of the fifo has been closed 552 usleep(10000); 553 554 // The block server should still be functioning. We should be able to re-bind to it 555 expected = sizeof(fifo); 556 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 557 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 558 559 for (size_t i = 0; i < objs.size(); i++) { 560 ASSERT_TRUE(create_vmo_helper(fd, &objs[i], kBlockSize), ""); 561 } 562 for (size_t i = 0; i < objs.size(); i++) { 563 ASSERT_TRUE(write_striped_vmo_helper(client, &objs[i], i, objs.size(), group, kBlockSize), ""); 564 } 565 for (size_t i = 0; i < objs.size(); i++) { 566 ASSERT_TRUE(read_striped_vmo_helper(client, &objs[i], i, objs.size(), group, kBlockSize), ""); 567 } 568 for (size_t i = 0; i < objs.size(); i++) { 569 ASSERT_TRUE(close_vmo_helper(client, &objs[i], group), ""); 570 } 571 572 block_fifo_release_client(client); 573 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 574 close(fd); 575 END_TEST; 576} 577 578bool blkdev_test_fifo_bad_client_vmoid(void) { 579 // Try to flex the server's error handling by sending 'malicious' client requests. 580 BEGIN_TEST; 581 // Set up the blkdev 582 uint64_t kBlockSize, blk_count; 583 int fd = get_testdev(&kBlockSize, &blk_count); 584 585 // Create a connection to the blkdev 586 zx_handle_t fifo; 587 ssize_t expected = sizeof(fifo); 588 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 589 fifo_client_t* client; 590 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 591 groupid_t group = 0; 592 593 // Create a vmo 594 test_vmo_object_t obj; 595 ASSERT_TRUE(create_vmo_helper(fd, &obj, kBlockSize), ""); 596 597 // Bad request: Writing to the wrong vmoid 598 block_fifo_request_t request; 599 request.group = group; 600 request.vmoid = static_cast<vmoid_t>(obj.vmoid + 5); 601 request.opcode = BLOCKIO_WRITE; 602 request.length = 1; 603 request.vmo_offset = 0; 604 request.dev_offset = 0; 605 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_IO, "Expected IO error with bad vmoid"); 606 607 block_fifo_release_client(client); 608 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 609 close(fd); 610 END_TEST; 611} 612 613bool blkdev_test_fifo_bad_client_unaligned_request(void) { 614 // Try to flex the server's error handling by sending 'malicious' client requests. 615 BEGIN_TEST; 616 // Set up the blkdev 617 uint64_t kBlockSize, blk_count; 618 int fd = get_testdev(&kBlockSize, &blk_count); 619 620 // Create a connection to the blkdev 621 zx_handle_t fifo; 622 ssize_t expected = sizeof(fifo); 623 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 624 fifo_client_t* client; 625 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 626 groupid_t group = 0; 627 628 // Create a vmo of at least size "kBlockSize * 2", since we'll 629 // be reading "kBlockSize" bytes from an offset below, and we want it 630 // to fit within the bounds of the VMO. 631 test_vmo_object_t obj; 632 ASSERT_TRUE(create_vmo_helper(fd, &obj, kBlockSize * 2), ""); 633 634 block_fifo_request_t request; 635 request.group = group; 636 request.vmoid = static_cast<vmoid_t>(obj.vmoid); 637 request.opcode = BLOCKIO_WRITE; 638 639 // Send a request that has zero length 640 request.length = 0; 641 request.vmo_offset = 0; 642 request.dev_offset = 0; 643 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_INVALID_ARGS, ""); 644 645 block_fifo_release_client(client); 646 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 647 close(fd); 648 END_TEST; 649} 650 651bool blkdev_test_fifo_bad_client_overflow(void) { 652 // Try to flex the server's error handling by sending 'malicious' client requests. 653 BEGIN_TEST; 654 // Set up the blkdev 655 uint64_t kBlockSize, blk_count; 656 int fd = get_testdev(&kBlockSize, &blk_count); 657 658 // Create a connection to the blkdev 659 zx_handle_t fifo; 660 ssize_t expected = sizeof(fifo); 661 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 662 fifo_client_t* client; 663 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 664 groupid_t group = 0; 665 666 // Create a vmo of at least size "kBlockSize * 2", since we'll 667 // be reading "kBlockSize" bytes from an offset below, and we want it 668 // to fit within the bounds of the VMO. 669 test_vmo_object_t obj; 670 ASSERT_TRUE(create_vmo_helper(fd, &obj, kBlockSize * 2), ""); 671 672 block_fifo_request_t request; 673 request.group = group; 674 request.vmoid = static_cast<vmoid_t>(obj.vmoid); 675 request.opcode = BLOCKIO_WRITE; 676 677 // Send a request that is barely out-of-bounds for the device 678 request.length = 1; 679 request.vmo_offset = 0; 680 request.dev_offset = blk_count; 681 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE); 682 683 // Send a request that is half out-of-bounds for the device 684 request.length = 2; 685 request.vmo_offset = 0; 686 request.dev_offset = blk_count - 1; 687 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE); 688 689 // Send a request that is very out-of-bounds for the device 690 request.length = 1; 691 request.vmo_offset = 0; 692 request.dev_offset = blk_count + 1; 693 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE); 694 695 // Send a request that tries to overflow the VMO 696 request.length = 2; 697 request.vmo_offset = fbl::numeric_limits<uint64_t>::max(); 698 request.dev_offset = 0; 699 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE); 700 701 // Send a request that tries to overflow the device 702 request.length = 2; 703 request.vmo_offset = 0; 704 request.dev_offset = fbl::numeric_limits<uint64_t>::max(); 705 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE); 706 707 block_fifo_release_client(client); 708 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 709 close(fd); 710 END_TEST; 711} 712 713bool blkdev_test_fifo_bad_client_bad_vmo(void) { 714 // Try to flex the server's error handling by sending 'malicious' client requests. 715 BEGIN_TEST; 716 // Set up the blkdev 717 uint64_t kBlockSize, blk_count; 718 int fd = get_testdev(&kBlockSize, &blk_count); 719 720 // Create a connection to the blkdev 721 zx_handle_t fifo; 722 ssize_t expected = sizeof(fifo); 723 ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO"); 724 fifo_client_t* client; 725 ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, ""); 726 groupid_t group = 0; 727 728 // Create a vmo of one block. 729 // 730 // The underlying VMO may be rounded up to the nearest PAGE_SIZE. 731 test_vmo_object_t obj; 732 obj.vmo_size = kBlockSize; 733 ASSERT_EQ(zx_vmo_create(obj.vmo_size, 0, &obj.vmo), ZX_OK, 734 "Failed to create vmo"); 735 fbl::AllocChecker ac; 736 obj.buf.reset(new (&ac) uint8_t[obj.vmo_size]); 737 ASSERT_TRUE(ac.check(), ""); 738 fill_random(obj.buf.get(), obj.vmo_size); 739 ASSERT_EQ(zx_vmo_write(obj.vmo, obj.buf.get(), 0, obj.vmo_size), 740 ZX_OK, "Failed to write to vmo"); 741 zx_handle_t xfer_vmo; 742 ASSERT_EQ(zx_handle_duplicate(obj.vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK, 743 "Failed to duplicate vmo"); 744 expected = sizeof(vmoid_t); 745 ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &obj.vmoid), expected, 746 "Failed to attach vmo"); 747 748 // Send a request to write to write multiple blocks -- enough that 749 // the request is larger than the VMO. 750 const uint64_t length = 1 + (fbl::round_up(obj.vmo_size, 751 static_cast<uint64_t>(PAGE_SIZE)) 752 / kBlockSize); 753 block_fifo_request_t request; 754 request.group = group; 755 request.vmoid = static_cast<vmoid_t>(obj.vmoid); 756 request.opcode = BLOCKIO_WRITE; 757 request.length = static_cast<uint32_t>(length); 758 request.vmo_offset = 0; 759 request.dev_offset = 0; 760 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE, ""); 761 // Do the same thing, but for reading 762 request.opcode = BLOCKIO_READ; 763 ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE, ""); 764 765 block_fifo_release_client(client); 766 ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo"); 767 close(fd); 768 END_TEST; 769} 770 771BEGIN_TEST_CASE(blkdev_tests) 772RUN_TEST(blkdev_test_simple) 773RUN_TEST(blkdev_test_bad_requests) 774#if 0 775RUN_TEST(blkdev_test_multiple) 776#endif 777RUN_TEST(blkdev_test_fifo_no_op) 778RUN_TEST(blkdev_test_fifo_basic) 779//RUN_TEST(blkdev_test_fifo_whole_disk) 780RUN_TEST(blkdev_test_fifo_multiple_vmo) 781RUN_TEST(blkdev_test_fifo_multiple_vmo_multithreaded) 782// TODO(smklein): Test ops across different vmos 783RUN_TEST(blkdev_test_fifo_unclean_shutdown) 784RUN_TEST(blkdev_test_fifo_bad_client_vmoid) 785RUN_TEST(blkdev_test_fifo_bad_client_unaligned_request) 786RUN_TEST(blkdev_test_fifo_bad_client_overflow) 787RUN_TEST(blkdev_test_fifo_bad_client_bad_vmo) 788END_TEST_CASE(blkdev_tests) 789 790} // namespace tests 791