/* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2016 Intel Corporation. * All rights reserved. */ #include "spdk/stdinc.h" #include "scsi/task.c" #include "scsi/scsi_bdev.c" #include "common/lib/test_env.c" #include "spdk_cunit.h" #include "spdk_internal/mock.h" #include "spdk/bdev_module.h" SPDK_LOG_REGISTER_COMPONENT(scsi) static uint64_t g_test_bdev_num_blocks; TAILQ_HEAD(, spdk_bdev_io) g_bdev_io_queue; int g_scsi_cb_called = 0; TAILQ_HEAD(, spdk_bdev_io_wait_entry) g_io_wait_queue; bool g_bdev_io_pool_full = false; bool spdk_bdev_io_type_supported(struct spdk_bdev *bdev, enum spdk_bdev_io_type io_type) { abort(); return false; } DEFINE_STUB_V(spdk_bdev_free_io, (struct spdk_bdev_io *bdev_io)); DEFINE_STUB(spdk_bdev_get_name, const char *, (const struct spdk_bdev *bdev), "test"); DEFINE_STUB(spdk_bdev_get_block_size, uint32_t, (const struct spdk_bdev *bdev), 512); DEFINE_STUB(spdk_bdev_get_md_size, uint32_t, (const struct spdk_bdev *bdev), 8); DEFINE_STUB(spdk_bdev_is_md_interleaved, bool, (const struct spdk_bdev *bdev), false); DEFINE_STUB(spdk_bdev_get_data_block_size, uint32_t, (const struct spdk_bdev *bdev), 512); DEFINE_STUB(spdk_bdev_get_physical_block_size, uint32_t, (const struct spdk_bdev *bdev), 4096); uint64_t spdk_bdev_get_num_blocks(const struct spdk_bdev *bdev) { return g_test_bdev_num_blocks; } DEFINE_STUB(spdk_bdev_get_product_name, const char *, (const struct spdk_bdev *bdev), "test product"); DEFINE_STUB(spdk_bdev_has_write_cache, bool, (const struct spdk_bdev *bdev), false); DEFINE_STUB(spdk_bdev_get_dif_type, enum spdk_dif_type, (const struct spdk_bdev *bdev), SPDK_DIF_DISABLE); DEFINE_STUB(spdk_bdev_is_dif_head_of_md, bool, (const struct spdk_bdev *bdev), false); DEFINE_STUB(spdk_bdev_is_dif_check_enabled, bool, (const struct spdk_bdev *bdev, enum spdk_dif_check_type check_type), false); DEFINE_STUB(scsi_pr_out, int, (struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len), 0); DEFINE_STUB(scsi_pr_in, int, (struct spdk_scsi_task *task, uint8_t *cdb, uint8_t *data, uint16_t data_len), 0); DEFINE_STUB(scsi2_reserve, int, (struct spdk_scsi_task *task, uint8_t *cdb), 0); DEFINE_STUB(scsi2_release, int, (struct spdk_scsi_task *task), 0); void scsi_lun_complete_task(struct spdk_scsi_lun *lun, struct spdk_scsi_task *task) { g_scsi_cb_called++; } DEFINE_STUB_V(scsi_lun_complete_reset_task, (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)); DEFINE_STUB(spdk_scsi_lun_id_int_to_fmt, uint64_t, (int lun_id), 0); DEFINE_STUB(spdk_scsi_dev_get_first_lun, struct spdk_scsi_lun *, (struct spdk_scsi_dev *dev), NULL); DEFINE_STUB(spdk_scsi_dev_get_next_lun, struct spdk_scsi_lun *, (struct spdk_scsi_lun *prev_lun), NULL); static void ut_put_task(struct spdk_scsi_task *task) { if (task->alloc_len) { free(task->iov.iov_base); } task->iov.iov_base = NULL; task->iov.iov_len = 0; task->alloc_len = 0; SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); } static void ut_init_task(struct spdk_scsi_task *task) { memset(task, 0xFF, sizeof(*task)); task->iov.iov_base = NULL; task->iovs = &task->iov; task->iovcnt = 1; task->alloc_len = 0; task->dxfer_dir = SPDK_SCSI_DIR_NONE; } void spdk_bdev_io_get_scsi_status(const struct spdk_bdev_io *bdev_io, int *sc, int *sk, int *asc, int *ascq) { switch (bdev_io->internal.status) { case SPDK_BDEV_IO_STATUS_SUCCESS: *sc = SPDK_SCSI_STATUS_GOOD; *sk = SPDK_SCSI_SENSE_NO_SENSE; *asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE; *ascq = SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE; break; case SPDK_BDEV_IO_STATUS_SCSI_ERROR: *sc = bdev_io->internal.error.scsi.sc; *sk = bdev_io->internal.error.scsi.sk; *asc = bdev_io->internal.error.scsi.asc; *ascq = bdev_io->internal.error.scsi.ascq; break; default: *sc = SPDK_SCSI_STATUS_CHECK_CONDITION; *sk = SPDK_SCSI_SENSE_ABORTED_COMMAND; *asc = SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE; *ascq = SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE; break; } } void spdk_bdev_io_get_iovec(struct spdk_bdev_io *bdev_io, struct iovec **iovp, int *iovcntp) { *iovp = NULL; *iovcntp = 0; } static void ut_bdev_io_flush(void) { struct spdk_bdev_io *bdev_io; struct spdk_bdev_io_wait_entry *entry; while (!TAILQ_EMPTY(&g_bdev_io_queue) || !TAILQ_EMPTY(&g_io_wait_queue)) { while (!TAILQ_EMPTY(&g_bdev_io_queue)) { bdev_io = TAILQ_FIRST(&g_bdev_io_queue); TAILQ_REMOVE(&g_bdev_io_queue, bdev_io, internal.link); bdev_io->internal.cb(bdev_io, true, bdev_io->internal.caller_ctx); free(bdev_io); } while (!TAILQ_EMPTY(&g_io_wait_queue)) { entry = TAILQ_FIRST(&g_io_wait_queue); TAILQ_REMOVE(&g_io_wait_queue, entry, link); entry->cb_fn(entry->cb_arg); } } } static int _spdk_bdev_io_op(spdk_bdev_io_completion_cb cb, void *cb_arg) { struct spdk_bdev_io *bdev_io; if (g_bdev_io_pool_full) { g_bdev_io_pool_full = false; return -ENOMEM; } bdev_io = calloc(1, sizeof(*bdev_io)); SPDK_CU_ASSERT_FATAL(bdev_io != NULL); bdev_io->internal.status = SPDK_BDEV_IO_STATUS_SUCCESS; bdev_io->internal.cb = cb; bdev_io->internal.caller_ctx = cb_arg; TAILQ_INSERT_TAIL(&g_bdev_io_queue, bdev_io, internal.link); return 0; } int spdk_bdev_readv_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg) { return _spdk_bdev_io_op(cb, cb_arg); } int spdk_bdev_writev_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, struct iovec *iov, int iovcnt, uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg) { return _spdk_bdev_io_op(cb, cb_arg); } int spdk_bdev_unmap_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg) { return _spdk_bdev_io_op(cb, cb_arg); } int spdk_bdev_reset(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, spdk_bdev_io_completion_cb cb, void *cb_arg) { return _spdk_bdev_io_op(cb, cb_arg); } int spdk_bdev_flush_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, uint64_t offset_blocks, uint64_t num_blocks, spdk_bdev_io_completion_cb cb, void *cb_arg) { return _spdk_bdev_io_op(cb, cb_arg); } int spdk_bdev_queue_io_wait(struct spdk_bdev *bdev, struct spdk_io_channel *ch, struct spdk_bdev_io_wait_entry *entry) { TAILQ_INSERT_TAIL(&g_io_wait_queue, entry, link); return 0; } int spdk_dif_ctx_init(struct spdk_dif_ctx *ctx, uint32_t block_size, uint32_t md_size, bool md_interleave, bool dif_loc, enum spdk_dif_type dif_type, uint32_t dif_flags, uint32_t init_ref_tag, uint16_t apptag_mask, uint16_t app_tag, uint32_t data_offset, uint16_t guard_seed) { ctx->init_ref_tag = init_ref_tag; ctx->ref_tag_offset = data_offset / 512; return 0; } /* * This test specifically tests a mode select 6 command from the * Windows SCSI compliance test that caused SPDK to crash. */ static void mode_select_6_test(void) { struct spdk_bdev bdev; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; char cdb[16]; char data[24]; int rc; ut_init_task(&task); cdb[0] = 0x15; cdb[1] = 0x11; cdb[2] = 0x00; cdb[3] = 0x00; cdb[4] = 0x18; cdb[5] = 0x00; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; memset(data, 0, sizeof(data)); data[4] = 0x08; data[5] = 0x02; spdk_scsi_task_set_data(&task, data, sizeof(data)); rc = bdev_scsi_execute(&task); CU_ASSERT_EQUAL(rc, 0); ut_put_task(&task); } /* * This test specifically tests a mode select 6 command which * contains no mode pages. */ static void mode_select_6_test2(void) { struct spdk_bdev bdev; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; char cdb[16]; int rc; ut_init_task(&task); cdb[0] = 0x15; cdb[1] = 0x00; cdb[2] = 0x00; cdb[3] = 0x00; cdb[4] = 0x00; cdb[5] = 0x00; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; rc = bdev_scsi_execute(&task); CU_ASSERT_EQUAL(rc, 0); ut_put_task(&task); } /* * This test specifically tests a mode sense 6 command which * return all subpage 00h mode pages. */ static void mode_sense_6_test(void) { struct spdk_bdev bdev; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; char cdb[12]; unsigned char *data; int rc; unsigned char mode_data_len = 0; unsigned char medium_type = 0; unsigned char dev_specific_param = 0; unsigned char blk_descriptor_len = 0; memset(&bdev, 0, sizeof(struct spdk_bdev)); ut_init_task(&task); memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x1A; cdb[2] = 0x3F; cdb[4] = 0xFF; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; rc = bdev_scsi_execute(&task); SPDK_CU_ASSERT_FATAL(rc == 0); data = task.iovs[0].iov_base; mode_data_len = data[0]; medium_type = data[1]; dev_specific_param = data[2]; blk_descriptor_len = data[3]; CU_ASSERT(mode_data_len >= 11); CU_ASSERT_EQUAL(medium_type, 0); CU_ASSERT_EQUAL(dev_specific_param, 0); CU_ASSERT_EQUAL(blk_descriptor_len, 8); ut_put_task(&task); } /* * This test specifically tests a mode sense 10 command which * return all subpage 00h mode pages. */ static void mode_sense_10_test(void) { struct spdk_bdev bdev; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; char cdb[12]; unsigned char *data; int rc; unsigned short mode_data_len = 0; unsigned char medium_type = 0; unsigned char dev_specific_param = 0; unsigned short blk_descriptor_len = 0; memset(&bdev, 0, sizeof(struct spdk_bdev)); ut_init_task(&task); memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x5A; cdb[2] = 0x3F; cdb[8] = 0xFF; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; rc = bdev_scsi_execute(&task); SPDK_CU_ASSERT_FATAL(rc == 0); data = task.iovs[0].iov_base; mode_data_len = ((data[0] << 8) + data[1]); medium_type = data[2]; dev_specific_param = data[3]; blk_descriptor_len = ((data[6] << 8) + data[7]); CU_ASSERT(mode_data_len >= 14); CU_ASSERT_EQUAL(medium_type, 0); CU_ASSERT_EQUAL(dev_specific_param, 0); CU_ASSERT_EQUAL(blk_descriptor_len, 8); ut_put_task(&task); } /* * This test specifically tests a scsi inquiry command from the * Windows SCSI compliance test that failed to return the * expected SCSI error sense code. */ static void inquiry_evpd_test(void) { struct spdk_bdev bdev; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; char cdb[6]; int rc; ut_init_task(&task); cdb[0] = 0x12; cdb[1] = 0x00; /* EVPD = 0 */ cdb[2] = 0xff; /* PageCode non-zero */ cdb[3] = 0x00; cdb[4] = 0xff; cdb[5] = 0x00; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; rc = bdev_scsi_execute(&task); SPDK_CU_ASSERT_FATAL(rc == 0); CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT_EQUAL(task.sense_data[2] & 0xf, SPDK_SCSI_SENSE_ILLEGAL_REQUEST); CU_ASSERT_EQUAL(task.sense_data[12], 0x24); CU_ASSERT_EQUAL(task.sense_data[13], 0x0); ut_put_task(&task); } /* * This test is to verify specific return data for a standard scsi inquiry * command: Version */ static void inquiry_standard_test(void) { struct spdk_bdev bdev = { .blocklen = 512 }; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; char cdb[6]; char *data; struct spdk_scsi_cdb_inquiry_data *inq_data; int rc; ut_init_task(&task); cdb[0] = 0x12; cdb[1] = 0x00; /* EVPD = 0 */ cdb[2] = 0x00; /* PageCode zero - requesting standard inquiry */ cdb[3] = 0x00; cdb[4] = 0xff; /* Indicate data size used by conformance test */ cdb[5] = 0x00; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; rc = bdev_scsi_execute(&task); data = task.iovs[0].iov_base; inq_data = (struct spdk_scsi_cdb_inquiry_data *)&data[0]; CU_ASSERT_EQUAL(inq_data->version, SPDK_SPC_VERSION_SPC3); CU_ASSERT_EQUAL(rc, 0); ut_put_task(&task); } static void _inquiry_overflow_test(uint8_t alloc_len) { struct spdk_bdev bdev = { .blocklen = 512 }; struct spdk_scsi_task task; struct spdk_scsi_lun lun; struct spdk_scsi_dev dev; uint8_t cdb[6]; int rc; /* expects a 4K internal data buffer */ char data[4096], data_compare[4096]; ut_init_task(&task); cdb[0] = 0x12; cdb[1] = 0x00; /* EVPD = 0 */ cdb[2] = 0x00; /* PageCode zero - requesting standard inquiry */ cdb[3] = 0x00; cdb[4] = alloc_len; /* Indicate data size used by conformance test */ cdb[5] = 0x00; task.cdb = cdb; snprintf(&dev.name[0], sizeof(dev.name), "spdk_iscsi_translation_test"); lun.bdev = &bdev; lun.dev = &dev; task.lun = &lun; memset(data, 0, sizeof(data)); memset(data_compare, 0, sizeof(data_compare)); spdk_scsi_task_set_data(&task, data, sizeof(data)); rc = bdev_scsi_execute(&task); SPDK_CU_ASSERT_FATAL(rc == 0); CU_ASSERT_EQUAL(memcmp(data + alloc_len, data_compare + alloc_len, sizeof(data) - alloc_len), 0); CU_ASSERT(task.data_transferred <= alloc_len); ut_put_task(&task); } static void inquiry_overflow_test(void) { int i; for (i = 0; i < 256; i++) { _inquiry_overflow_test(i); } } static void scsi_name_padding_test(void) { char name[SPDK_SCSI_DEV_MAX_NAME + 1]; char buf[SPDK_SCSI_DEV_MAX_NAME + 1]; int written, i; /* case 1 */ memset(name, '\0', sizeof(name)); memset(name, 'x', 251); written = bdev_scsi_pad_scsi_name(buf, name); CU_ASSERT(written == 252); CU_ASSERT(buf[250] == 'x'); CU_ASSERT(buf[251] == '\0'); /* case 2: */ memset(name, '\0', sizeof(name)); memset(name, 'x', 252); written = bdev_scsi_pad_scsi_name(buf, name); CU_ASSERT(written == 256); CU_ASSERT(buf[251] == 'x'); for (i = 252; i < 256; i++) { CU_ASSERT(buf[i] == '\0'); } /* case 3 */ memset(name, '\0', sizeof(name)); memset(name, 'x', 255); written = bdev_scsi_pad_scsi_name(buf, name); CU_ASSERT(written == 256); CU_ASSERT(buf[254] == 'x'); CU_ASSERT(buf[255] == '\0'); } /* * This test is to verify specific error translation from bdev to scsi. */ static void task_complete_test(void) { struct spdk_scsi_task task; struct spdk_bdev_io bdev_io = {}; struct spdk_scsi_lun lun; ut_init_task(&task); TAILQ_INIT(&lun.tasks); TAILQ_INSERT_TAIL(&lun.tasks, &task, scsi_link); task.lun = &lun; bdev_io.internal.status = SPDK_BDEV_IO_STATUS_SUCCESS; bdev_scsi_task_complete_cmd(&bdev_io, bdev_io.internal.status, &task); CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; bdev_io.internal.status = SPDK_BDEV_IO_STATUS_SCSI_ERROR; bdev_io.internal.error.scsi.sc = SPDK_SCSI_STATUS_CHECK_CONDITION; bdev_io.internal.error.scsi.sk = SPDK_SCSI_SENSE_HARDWARE_ERROR; bdev_io.internal.error.scsi.asc = SPDK_SCSI_ASC_WARNING; bdev_io.internal.error.scsi.ascq = SPDK_SCSI_ASCQ_POWER_LOSS_EXPECTED; bdev_scsi_task_complete_cmd(&bdev_io, bdev_io.internal.status, &task); CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT_EQUAL(task.sense_data[2] & 0xf, SPDK_SCSI_SENSE_HARDWARE_ERROR); CU_ASSERT_EQUAL(task.sense_data[12], SPDK_SCSI_ASC_WARNING); CU_ASSERT_EQUAL(task.sense_data[13], SPDK_SCSI_ASCQ_POWER_LOSS_EXPECTED); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; bdev_io.internal.status = SPDK_BDEV_IO_STATUS_FAILED; bdev_scsi_task_complete_cmd(&bdev_io, bdev_io.internal.status, &task); CU_ASSERT_EQUAL(task.status, SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT_EQUAL(task.sense_data[2] & 0xf, SPDK_SCSI_SENSE_ABORTED_COMMAND); CU_ASSERT_EQUAL(task.sense_data[12], SPDK_SCSI_ASC_NO_ADDITIONAL_SENSE); CU_ASSERT_EQUAL(task.sense_data[13], SPDK_SCSI_ASCQ_CAUSE_NOT_REPORTABLE); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; ut_put_task(&task); } static void lba_range_test(void) { struct spdk_bdev bdev = { .blocklen = 512 }; struct spdk_scsi_lun lun; struct spdk_scsi_task task; uint8_t cdb[16]; int rc; lun.bdev = &bdev; ut_init_task(&task); task.lun = &lun; task.lun->bdev_desc = NULL; task.lun->io_channel = NULL; task.cdb = cdb; memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x88; /* READ (16) */ /* Test block device size of 4 blocks */ g_test_bdev_num_blocks = 4; /* LBA = 0, length = 1 (in range) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 1); /* transfer length */ task.transfer_len = 1 * 512; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue)); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; /* LBA = 4, length = 1 (LBA out of range) */ to_be64(&cdb[2], 4); /* LBA */ to_be32(&cdb[10], 1); /* transfer length */ task.transfer_len = 1 * 512; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE); CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE); SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); /* LBA = 0, length = 4 (in range, max valid size) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 4); /* transfer length */ task.transfer_len = 4 * 512; task.status = 0xFF; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue)); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; /* LBA = 0, length = 5 (LBA in range, length beyond end of bdev) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 5); /* transfer length */ task.transfer_len = 5 * 512; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE); CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE); SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); ut_put_task(&task); } static void xfer_len_test(void) { struct spdk_bdev bdev = { .blocklen = 512 }; struct spdk_scsi_lun lun; struct spdk_scsi_task task; uint8_t cdb[16]; int rc; lun.bdev = &bdev; ut_init_task(&task); task.lun = &lun; task.lun->bdev_desc = NULL; task.lun->io_channel = NULL; task.cdb = cdb; memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x88; /* READ (16) */ /* Test block device size of 512 MiB */ g_test_bdev_num_blocks = 512 * 1024 * 1024; /* 1 block */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 1); /* transfer length */ task.transfer_len = 1 * 512; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue)); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; /* max transfer length (as reported in block limits VPD page) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], SPDK_WORK_BLOCK_SIZE / 512); /* transfer length */ task.transfer_len = SPDK_WORK_BLOCK_SIZE; task.status = 0xFF; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); SPDK_CU_ASSERT_FATAL(!TAILQ_EMPTY(&g_bdev_io_queue)); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; /* max transfer length plus one block (invalid) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], SPDK_WORK_BLOCK_SIZE / 512 + 1); /* transfer length */ task.transfer_len = SPDK_WORK_BLOCK_SIZE + 512; task.offset = 0; task.length = 1 * 512; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE); CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT((task.sense_data[2] & 0xf) == SPDK_SCSI_SENSE_ILLEGAL_REQUEST); CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_INVALID_FIELD_IN_CDB); SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); /* zero transfer length (valid) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 0); /* transfer length */ task.transfer_len = 0; task.offset = 0; task.length = 0; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(task.data_transferred == 0); SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); /* zero transfer length past end of disk (invalid) */ to_be64(&cdb[2], g_test_bdev_num_blocks); /* LBA */ to_be32(&cdb[10], 0); /* transfer length */ task.transfer_len = 0; task.offset = 0; task.length = 0; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_COMPLETE); CU_ASSERT(task.status == SPDK_SCSI_STATUS_CHECK_CONDITION); CU_ASSERT(task.sense_data[12] == SPDK_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE); SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); ut_put_task(&task); } static void _xfer_test(bool bdev_io_pool_full) { struct spdk_bdev bdev = { .blocklen = 512 }; struct spdk_scsi_lun lun; struct spdk_scsi_task task; uint8_t cdb[16]; char data[4096]; int rc; lun.bdev = &bdev; /* Test block device size of 512 MiB */ g_test_bdev_num_blocks = 512 * 1024 * 1024; /* Read 1 block */ ut_init_task(&task); task.lun = &lun; task.lun->bdev_desc = NULL; task.lun->io_channel = NULL; task.cdb = cdb; memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x88; /* READ (16) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 1); /* transfer length */ task.transfer_len = 1 * 512; task.offset = 0; task.length = 1 * 512; g_bdev_io_pool_full = bdev_io_pool_full; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; ut_put_task(&task); /* Write 1 block */ ut_init_task(&task); task.lun = &lun; task.cdb = cdb; memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x8a; /* WRITE (16) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 1); /* transfer length */ task.transfer_len = 1 * 512; task.offset = 0; task.length = 1 * 512; g_bdev_io_pool_full = bdev_io_pool_full; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; ut_put_task(&task); /* Unmap 5 blocks using 2 descriptors */ ut_init_task(&task); task.lun = &lun; task.cdb = cdb; memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x42; /* UNMAP */ to_be16(&data[7], 2); /* 2 parameters in list */ memset(data, 0, sizeof(data)); to_be16(&data[2], 32); /* 2 descriptors */ to_be64(&data[8], 1); /* LBA 1 */ to_be32(&data[16], 2); /* 2 blocks */ to_be64(&data[24], 10); /* LBA 10 */ to_be32(&data[32], 3); /* 3 blocks */ spdk_scsi_task_set_data(&task, data, sizeof(data)); task.status = SPDK_SCSI_STATUS_GOOD; g_bdev_io_pool_full = bdev_io_pool_full; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; ut_put_task(&task); /* Flush 1 block */ ut_init_task(&task); task.lun = &lun; task.cdb = cdb; memset(cdb, 0, sizeof(cdb)); cdb[0] = 0x91; /* SYNCHRONIZE CACHE (16) */ to_be64(&cdb[2], 0); /* LBA */ to_be32(&cdb[10], 1); /* 1 blocks */ g_bdev_io_pool_full = bdev_io_pool_full; rc = bdev_scsi_execute(&task); CU_ASSERT(rc == SPDK_SCSI_TASK_PENDING); CU_ASSERT(task.status == 0xFF); ut_bdev_io_flush(); CU_ASSERT(task.status == SPDK_SCSI_STATUS_GOOD); CU_ASSERT(g_scsi_cb_called == 1); g_scsi_cb_called = 0; SPDK_CU_ASSERT_FATAL(TAILQ_EMPTY(&g_bdev_io_queue)); ut_put_task(&task); } static void xfer_test(void) { _xfer_test(false); _xfer_test(true); } static void get_dif_ctx_test(void) { struct spdk_bdev bdev = {}; struct spdk_scsi_task task = {}; struct spdk_dif_ctx dif_ctx = {}; uint8_t cdb[16]; bool ret; cdb[0] = SPDK_SBC_READ_6; cdb[1] = 0x12; cdb[2] = 0x34; cdb[3] = 0x50; task.cdb = cdb; task.offset = 0x6 * 512; ret = bdev_scsi_get_dif_ctx(&bdev, &task, &dif_ctx); CU_ASSERT(ret == true); CU_ASSERT(dif_ctx.init_ref_tag + dif_ctx.ref_tag_offset == 0x123456); cdb[0] = SPDK_SBC_WRITE_12; to_be32(&cdb[2], 0x12345670); task.offset = 0x8 * 512; ret = bdev_scsi_get_dif_ctx(&bdev, &task, &dif_ctx); CU_ASSERT(ret == true); CU_ASSERT(dif_ctx.init_ref_tag + dif_ctx.ref_tag_offset == 0x12345678); cdb[0] = SPDK_SBC_WRITE_16; to_be64(&cdb[2], 0x0000000012345670); task.offset = 0x8 * 512; ret = bdev_scsi_get_dif_ctx(&bdev, &task, &dif_ctx); CU_ASSERT(ret == true); CU_ASSERT(dif_ctx.init_ref_tag + dif_ctx.ref_tag_offset == 0x12345678); } int main(int argc, char **argv) { CU_pSuite suite = NULL; unsigned int num_failures; TAILQ_INIT(&g_bdev_io_queue); TAILQ_INIT(&g_io_wait_queue); CU_set_error_action(CUEA_ABORT); CU_initialize_registry(); suite = CU_add_suite("translation_suite", NULL, NULL); CU_ADD_TEST(suite, mode_select_6_test); CU_ADD_TEST(suite, mode_select_6_test2); CU_ADD_TEST(suite, mode_sense_6_test); CU_ADD_TEST(suite, mode_sense_10_test); CU_ADD_TEST(suite, inquiry_evpd_test); CU_ADD_TEST(suite, inquiry_standard_test); CU_ADD_TEST(suite, inquiry_overflow_test); CU_ADD_TEST(suite, task_complete_test); CU_ADD_TEST(suite, lba_range_test); CU_ADD_TEST(suite, xfer_len_test); CU_ADD_TEST(suite, xfer_test); CU_ADD_TEST(suite, scsi_name_padding_test); CU_ADD_TEST(suite, get_dif_ctx_test); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); num_failures = CU_get_number_of_failures(); CU_cleanup_registry(); return num_failures; }