From eebd04e61383c22f3170c422ef5ffaf41b260dd1 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sat, 25 May 2013 16:02:02 -0700 Subject: [PATCH] Add initial support for SANITIZE and a simple test to generate this opcode. --- Makefile.am | 1 + include/iscsi.h | 9 ++++++ include/scsi-lowlevel.h | 12 ++++++++ lib/iscsi-command.c | 24 ++++++++++++++++ lib/libiscsi.def | 3 ++ lib/libiscsi.syms | 3 ++ lib/scsi-lowlevel.c | 36 ++++++++++++++++++++++++ lib/sync.c | 22 +++++++++++++++ test-tool/iscsi-support.c | 37 ++++++++++++++++++++++++ test-tool/iscsi-support.h | 13 +++++++++ test-tool/iscsi-test-cu.c | 17 ++++++++++- test-tool/iscsi-test-cu.h | 2 ++ test-tool/test_sanitize_simple.c | 48 ++++++++++++++++++++++++++++++++ 13 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 test-tool/test_sanitize_simple.c diff --git a/Makefile.am b/Makefile.am index 1599037..6bf31c6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -266,6 +266,7 @@ bin_iscsi_test_cu_SOURCES = test-tool/iscsi-test-cu.c \ test-tool/test_reserve6_target_warm_reset.c \ test-tool/test_reserve6_target_cold_reset.c \ test-tool/test_reserve6_lun_reset.c \ + test-tool/test_sanitize_simple.c \ test-tool/test_startstopunit_simple.c \ test-tool/test_startstopunit_pwrcnd.c \ test-tool/test_startstopunit_noloej.c \ diff --git a/include/iscsi.h b/include/iscsi.h index 95898eb..c40866c 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -673,6 +673,11 @@ iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data); EXTERN struct scsi_task * +iscsi_sanitize_task(struct iscsi_context *iscsi, int lun, + int immed, int ause, int sa, int param_len, + struct iscsi_data *data, + iscsi_command_cb cb, void *private_data); +EXTERN struct scsi_task * iscsi_get_lba_status_task(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len, iscsi_command_cb cb, @@ -943,6 +948,10 @@ iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun); EXTERN struct scsi_task * iscsi_get_lba_status_sync(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len); +EXTERN struct scsi_task * +iscsi_sanitize_sync(struct iscsi_context *iscsi, int lun, + int immed, int ause, int sa, int param_len, + struct iscsi_data *data); EXTERN struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed); diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 51c7cfe..ffe11aa 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -48,6 +48,7 @@ enum scsi_opcode { SCSI_OPCODE_WRITE_SAME10 = 0x41, SCSI_OPCODE_UNMAP = 0x42, SCSI_OPCODE_READTOC = 0x43, + SCSI_OPCODE_SANITIZE = 0x48, SCSI_OPCODE_PERSISTENT_RESERVE_IN = 0x5E, SCSI_OPCODE_PERSISTENT_RESERVE_OUT = 0x5F, SCSI_OPCODE_READ16 = 0x88, @@ -296,6 +297,17 @@ EXTERN void *scsi_get_task_private_ptr(struct scsi_task *task); */ EXTERN struct scsi_task *scsi_cdb_testunitready(void); +/* + * SANITIZE + */ +#define SCSI_SANITIZE_OVERWRITE 0x01 +#define SCSI_SANITIZE_BLOCK_ERASE 0x02 +#define SCSI_SANITIZE_CRYPTO_ERASE 0x03 +#define SCSI_SANITIZE_EXIT_FAILURE_MODE 0x1f + +EXTERN struct scsi_task *scsi_cdb_sanitize(int immed, int ause, int sa, + int param_len); + /* * REPORTLUNS diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index 18a705f..1308bdf 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -1632,6 +1632,30 @@ iscsi_release6_task(struct iscsi_context *iscsi, int lun, return task; } + +struct scsi_task * +iscsi_sanitize_task(struct iscsi_context *iscsi, int lun, + int immed, int ause, int sa, int param_len, + struct iscsi_data *data, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + + task = scsi_cdb_sanitize(immed, ause, sa, param_len); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "sanitize cdb."); + return NULL; + } + if (iscsi_scsi_command_async(iscsi, lun, task, cb, + data, private_data) != 0) { + scsi_free_scsi_task(task); + return NULL; + } + + return task; +} + struct scsi_task * iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, int lun, int rctd, int options, diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 55c5aed..caec7f6 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -61,6 +61,8 @@ iscsi_release6_task iscsi_report_supported_opcodes_sync iscsi_report_supported_opcodes_task iscsi_reconnect +iscsi_sanitize_sync +iscsi_sanitize_task iscsi_set_noautoreconnect iscsi_set_reconnect_max_retries iscsi_set_timeout @@ -159,6 +161,7 @@ scsi_cdb_readtoc scsi_cdb_reserve6 scsi_cdb_release6 scsi_cdb_report_supported_opcodes +scsi_cdb_sanitize scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index fd01090..f5bafdc 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -59,6 +59,8 @@ iscsi_release6_task iscsi_report_supported_opcodes_sync iscsi_report_supported_opcodes_task iscsi_reconnect +iscsi_sanitize_sync +iscsi_sanitize_task iscsi_set_noautoreconnect iscsi_set_reconnect_max_retries iscsi_set_timeout @@ -157,6 +159,7 @@ scsi_cdb_readtoc scsi_cdb_reserve6 scsi_cdb_release6 scsi_cdb_report_supported_opcodes +scsi_cdb_sanitize scsi_cdb_serviceactionin16 scsi_cdb_startstopunit scsi_cdb_synchronizecache10 diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 51a451f..d348506 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -332,6 +332,42 @@ scsi_cdb_testunitready(void) return task; } +/* + * SANITIZE + */ +struct scsi_task * +scsi_cdb_sanitize(int immed, int ause, int sa, int param_len) +{ + struct scsi_task *task; + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) { + return NULL; + } + + memset(task, 0, sizeof(struct scsi_task)); + task->cdb[0] = SCSI_OPCODE_SANITIZE; + + task->cdb[1] = sa & 0x1f; + if (immed) { + task->cdb[1] |= 0x80; + } + if (ause) { + task->cdb[1] |= 0x20; + } + + scsi_set_uint16(&task->cdb[7], param_len); + + task->cdb_size = 10; + if (param_len != 0) { + task->xfer_dir = SCSI_XFER_WRITE; + } else { + task->xfer_dir = SCSI_XFER_NONE; + } + task->expxferlen = param_len; + + return task; +} /* * REPORTLUNS diff --git a/lib/sync.c b/lib/sync.c index 0be3306..a67c426 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -481,6 +481,28 @@ iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun) return state.task; } +struct scsi_task * +iscsi_sanitize_sync(struct iscsi_context *iscsi, int lun, + int immed, int ause, int sa, int param_len, + struct iscsi_data *data) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_sanitize_task(iscsi, lun, + immed, ause, sa, param_len, data, + scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, + "Failed to send Sanitize command"); + return NULL; + } + + event_loop(iscsi, &state); + + return state.task; +} + struct scsi_task * iscsi_get_lba_status_sync(struct iscsi_context *iscsi, int lun, uint64_t starting_lba, uint32_t alloc_len) { diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 31799ec..200710b 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -56,6 +56,7 @@ uint64_t num_blocks; int lbppb; enum scsi_inquiry_peripheral_device_type device_type; int data_loss; +int allow_sanitize; int readonly; int sbc3_support; int maximum_transfer_length; @@ -1313,6 +1314,42 @@ synchronizecache16_nomedium(struct iscsi_context *iscsi, int lun, uint64_t lba, return 0; } +int sanitize(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data) +{ + struct scsi_task *task; + + logging(LOG_VERBOSE, "Send SANITIZE IMMED:%d AUSE:%d SA:%d " + "PARAM_LEN:%d", + immed, ause, sa, param_len); + + task = iscsi_sanitize_sync(iscsi, lun, immed, ause, sa, param_len, + data); + if (task == NULL) { + logging(LOG_NORMAL, + "[FAILED] Failed to send SANITIZE command: %s", + iscsi_get_error(iscsi)); + return -1; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST + && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + logging(LOG_NORMAL, "[SKIPPED] SANITIZE is not " + "implemented on target"); + scsi_free_scsi_task(task); + return -2; + } + if (task->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, + "[FAILED] SANITIZE command: failed with sense. %s", + iscsi_get_error(iscsi)); + scsi_free_scsi_task(task); + return -1; + } + scsi_free_scsi_task(task); + logging(LOG_VERBOSE, "[OK] SANITIZE returned SUCCESS."); + return 0; +} + int startstopunit(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start) { struct scsi_task *task; diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index a267293..0b31d5b 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -53,6 +53,17 @@ do { \ } \ } while (0); +#define CHECK_FOR_SANITIZE \ +do { \ + if (!allow_sanitize) { \ + logging(LOG_VERBOSE, "[SKIPPED] --allow_sanitize flag " \ + "is not set. Skipping test."); \ + CU_PASS("[SKIPPED] --allow_sanitize flag is not set." \ + " Skipping test"); \ + return; \ + } \ +} while (0); + #define CHECK_FOR_READONLY \ do { \ if (!readonly) { \ @@ -137,6 +148,7 @@ extern size_t block_size; extern uint64_t num_blocks; extern int lbppb; extern int data_loss; +extern int allow_sanitize; extern int readonly; extern int sbc3_support; extern int maximum_transfer_length; @@ -248,6 +260,7 @@ int report_supported_opcodes_invalidfieldincdb(struct iscsi_context *iscsi, int int release6(struct iscsi_context *iscsi, int lun); int reserve6(struct iscsi_context *iscsi, int lun); int reserve6_conflict(struct iscsi_context *iscsi, int lun); +int sanitize(struct iscsi_context *iscsi, int lun, int immed, int ause, int sa, int param_len, struct iscsi_data *data); int startstopunit(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start); int startstopunit_preventremoval(struct iscsi_context *iscsi, int lun, int immed, int pcm, int pc, int no_flush, int loej, int start); int synchronizecache10(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int sync_nv, int immed); diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index f08d726..4a1bb0e 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -221,6 +221,11 @@ static CU_TestInfo tests_readonly[] = { CU_TEST_INFO_NULL }; +static CU_TestInfo tests_sanitize[] = { + { (char *)"Sanitize", test_sanitize_simple }, + CU_TEST_INFO_NULL +}; + static CU_TestInfo tests_report_supported_opcodes[] = { { (char *)"ReportSupportedOpcodesSimple", test_report_supported_opcodes_simple }, { (char *)"ReportSupportedOpcodesOneCommand", test_report_supported_opcodes_one_command }, @@ -415,6 +420,8 @@ static CU_SuiteInfo scsi_suites[] = { tests_report_supported_opcodes }, { (char *)"Reserve6", test_setup, test_teardown, tests_reserve6 }, + { (char *)"Sanitize", test_setup, test_teardown, + tests_sanitize }, { (char *)"StartStopUnit", test_setup, test_teardown, tests_startstopunit }, { (char *)"UnitReady", test_setup, test_teardown, @@ -519,6 +526,8 @@ static CU_SuiteInfo all_suites[] = { tests_report_supported_opcodes }, { (char *)"Reserve6", test_setup, test_teardown, tests_reserve6 }, + { (char *)"Sanitize", test_setup, test_teardown, + tests_sanitize }, { (char *)"StartStopUnit", test_setup, test_teardown, tests_startstopunit }, { (char *)"TestUnitReady", test_setup, test_teardown, @@ -668,6 +677,8 @@ print_usage(void) " -l|--list List all tests and exit\n"); fprintf(stderr, " -d|--dataloss Allow destructive tests\n"); + fprintf(stderr, + " -S|--allow-sanitize Allow sanitize-opcode tests\n"); fprintf(stderr, " -g|--ignore Error Action: Ignore test errors [DEFAULT]\n"); fprintf(stderr, @@ -890,6 +901,7 @@ main(int argc, char *argv[]) { "initiator-name-2", required_argument, 0, 'I' }, { "test", required_argument, 0, 't' }, { "dataloss", no_argument, 0, 'd' }, + { "allow-sanitize", no_argument, 0, 'S' }, { "ignore", no_argument, 0, 'g' }, { "fail", no_argument, 0, 'f' }, { "abort", no_argument, 0, 'A' }, @@ -903,7 +915,7 @@ main(int argc, char *argv[]) int i, c; int opt_idx = 0; - while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsnuvV", long_opts, + while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnuvV", long_opts, &opt_idx)) > 0) { switch (c) { case 'h': @@ -937,6 +949,9 @@ main(int argc, char *argv[]) case 's': mode = CU_BRM_SILENT; break; + case 'S': + allow_sanitize = 1; + break; case 'n': mode = CU_BRM_NORMAL; break; diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h index d3e1955..b19c209 100644 --- a/test-tool/iscsi-test-cu.h +++ b/test-tool/iscsi-test-cu.h @@ -151,6 +151,8 @@ void test_reserve6_target_cold_reset(void); void test_reserve6_target_warm_reset(void); void test_reserve6_lun_reset(void); +void test_sanitize_simple(void); + void test_startstopunit_simple(void); void test_startstopunit_pwrcnd(void); void test_startstopunit_noloej(void); diff --git a/test-tool/test_sanitize_simple.c b/test-tool/test_sanitize_simple.c new file mode 100644 index 0000000..7fefa7c --- /dev/null +++ b/test-tool/test_sanitize_simple.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2013 Ronnie Sahlberg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include + +#include + +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test-cu.h" + +void +test_sanitize_simple(void) +{ + int ret; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test basic SANITIZE"); + + CHECK_FOR_SANITIZE; + + logging(LOG_VERBOSE, "Test we can perform basic BLOCK ERASE SANITIZE"); + + ret = sanitize(iscsic, tgt_lun, + 0, 0, SCSI_SANITIZE_BLOCK_ERASE, 0, NULL); + if (ret == -2) { + logging(LOG_NORMAL, "[SKIPPED] SANITIZE is not implemented."); + CU_PASS("SANITIZE is not implemented."); + return; + } + CU_ASSERT_EQUAL(ret, 0); +}