diff --git a/Makefile.am b/Makefile.am index e111680..197ab35 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,7 +63,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0132_verify10_mismatch_no_cmp.c \ test-tool/0143_read12_rdprotect.c \ test-tool/0153_read16_rdprotect.c \ - test-tool/0160_readcapacity16_simple.c + test-tool/0160_readcapacity16_simple.c \ + test-tool/0170_unmap_simple.c endif # LD_PRELOAD library. diff --git a/include/iscsi.h b/include/iscsi.h index 84114ae..a92c595 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -613,6 +613,16 @@ iscsi_modesense6_task(struct iscsi_context *iscsi, int lun, int dbd, unsigned char alloc_len, iscsi_command_cb cb, void *private_data); +struct unmap_list { + uint64_t lba; + uint32_t num; +}; + +EXTERN struct scsi_task * +iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, + struct unmap_list *list, int list_len, + iscsi_command_cb cb, void *private_data); + /* * Sync commands for SCSI @@ -662,6 +672,10 @@ iscsi_verify10_sync(struct iscsi_context *iscsi, int lun, int vprotect, int dpo, int bytchk, int blocksize); +EXTERN struct scsi_task * +iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, + struct unmap_list *list, int list_len); + /* * This function is used when the application wants to specify its own buffers to read the data * from the DATA-IN PDUs into. diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index d1bfb62..043b6cf 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -29,6 +29,7 @@ enum scsi_opcode { SCSI_OPCODE_WRITE10 = 0x2A, SCSI_OPCODE_VERIFY10 = 0x2F, SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35, + SCSI_OPCODE_UNMAP = 0x42, SCSI_OPCODE_READ16 = 0x88, SCSI_OPCODE_SERVICE_ACTION_IN = 0x9E, SCSI_OPCODE_REPORTLUNS = 0xA0, @@ -561,7 +562,9 @@ EXTERN struct scsi_task *scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed); EXTERN struct scsi_task *scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen); EXTERN struct scsi_task *scsi_cdb_readcapacity16(void); +EXTERN struct scsi_task *scsi_cdb_unmap(int anchor, int group, uint16_t xferlen); +void *scsi_malloc(struct scsi_task *task, size_t size); #endif /* __scsi_lowlevel_h__ */ diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 988fa07..f7de01f 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -91,4 +91,5 @@ scsi_cdb_write10 scsi_cdb_synchronizecache10 scsi_cdb_readcapacity16 scsi_cdb_serviceactionin16 +scsi_cdb_unmap diff --git a/lib/scsi-command.c b/lib/scsi-command.c index e3e481b..51f6636 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -794,6 +794,53 @@ iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba, return task; } +struct scsi_task * +iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, + struct unmap_list *list, int list_len, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + struct iscsi_data outdata; + unsigned char *data; + int xferlen; + int i; + + xferlen = 8 + list_len * 16; + + task = scsi_cdb_unmap(anchor, group, xferlen); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "unmap cdb."); + return NULL; + } + + data = scsi_malloc(task, xferlen); + if (data == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "unmap parameters."); + scsi_free_scsi_task(task); + return NULL; + } + *((uint16_t *)&data[0]) = htons(xferlen - 2); + *((uint16_t *)&data[2]) = htons(xferlen - 8); + for (i = 0; i < list_len; i++) { + *((uint32_t *)&data[8 + 16 * i]) = htonl(list[0].lba >> 32); + *((uint32_t *)&data[8 + 16 * i + 4]) = htonl(list[0].lba & 0xffffffff); + *((uint32_t *)&data[8 + 16 * i + 8]) = htonl(list[0].num); + } + + outdata.data = data; + outdata.size = xferlen; + + if (iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata, + private_data) != 0) { + scsi_free_scsi_task(task); + return NULL; + } + + return task; +} + unsigned char * iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, uint32_t pos, ssize_t *count) { diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index b2eb435..4ea7dcb 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -55,7 +55,7 @@ scsi_free_scsi_task(struct scsi_task *task) free(task); } -static void * +void * scsi_malloc(struct scsi_task *task, size_t size) { struct scsi_allocated_memory *mem; @@ -759,6 +759,36 @@ scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int byt } +/* + * UNMAP + */ +struct scsi_task * +scsi_cdb_unmap(int anchor, int group, uint16_t xferlen) +{ + 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_UNMAP; + + if (anchor) { + task->cdb[1] |= 0x01; + } + task->cdb[6] |= group & 0x1f; + + *(uint16_t *)&task->cdb[7] = htons(xferlen); + + task->cdb_size = 10; + task->xfer_dir = SCSI_XFER_WRITE; + task->expxferlen = xferlen; + + return task; +} + /* * MODESENSE6 */ diff --git a/lib/sync.c b/lib/sync.c index 352094d..6b9e876 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -361,6 +361,26 @@ iscsi_verify10_sync(struct iscsi_context *iscsi, int lun, unsigned char *data, u return state.task; } +struct scsi_task * +iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, + struct unmap_list *list, int list_len) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_unmap_task(iscsi, lun, anchor, group, list, list_len, + scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, + "Failed to send UNMAP command"); + return NULL; + } + + event_loop(iscsi, &state); + + return state.task; +} + struct scsi_task * iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun, struct scsi_task *task, struct iscsi_data *data) diff --git a/test-tool/0170_unmap_simple.c b/test-tool/0170_unmap_simple.c new file mode 100644 index 0000000..b9cc0ae --- /dev/null +++ b/test-tool/0170_unmap_simple.c @@ -0,0 +1,134 @@ +/* + Copyright (C) 2012 by 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 "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +int T0170_unmap_simple(const char *initiator, const char *url) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_readcapacity16 *rc16; + int ret, i, lun; + uint32_t block_size, num_blocks; + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + /* find the size of the LUN */ + task = iscsi_readcapacity16_sync(iscsi, lun); + if (task == NULL) { + printf("Failed to send readcapacity16 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("Readcapacity command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + printf("failed to unmarshall readcapacity16 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + + if (rc16->lbpme == 0){ + printf("Logical unit is fully provisioned. Skipping test\n"); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + + block_size = rc16->block_length; + num_blocks = rc16->returned_lba; + + scsi_free_scsi_task(task); + + if (!data_loss) { + printf("data_loss flag is not set. Skipping test\n"); + ret = -1; + goto finished; + } + + ret = 0; + + /* unmap the first 1 - 256 blocks at the start of the LUN */ + printf("Unmapping first 1-256 blocks ... "); + for (i=1; i<=256; i++) { + struct unmap_list list[1]; + + list[0].lba = 0; + list[0].num = i; + task = iscsi_unmap_sync(iscsi, lun, 0, 0, &list[0], 1); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send UNMAP command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("UNMAP command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + } + printf("[OK]\n"); + + + /* unmap the last 1 - 256 blocks at the end of the LUN */ + printf("Unmapping last 1-256 blocks ... "); + for (i=1; i<=256; i++) { + struct unmap_list list[1]; + + list[0].lba = num_blocks + 1 - i; + list[0].num = i; + task = iscsi_unmap_sync(iscsi, lun, 0, 0, &list[0], 1); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send UNMAP command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("UNMAP command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + scsi_free_scsi_task(task); + } + printf("[OK]\n"); + + +finished: + iscsi_logout_sync(iscsi); + iscsi_destroy_context(iscsi); + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index ed6b1f3..103f131 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -29,6 +29,7 @@ #include "iscsi-test.h" const char *initiator = "iqn.2010-11.iscsi-test"; +int data_loss = 0; struct scsi_test { const char *name; @@ -67,6 +68,9 @@ struct scsi_test tests[] = { /* readcapacity16*/ { "T0160_readcapacity16_simple", T0160_readcapacity16_simple }, +/* unmap*/ +{ "T0170_unmap_simple", T0170_unmap_simple }, + { NULL, NULL } }; @@ -161,6 +165,7 @@ int main(int argc, const char *argv[]) { "list", 'l', POPT_ARG_NONE, &list_tests, 0, "List all tests", NULL }, { "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" }, { "test", 't', POPT_ARG_STRING, &testname, 0, "Which test to run", "testname" }, + { "dataloss", 0, POPT_ARG_NONE, &data_loss, 0, "Allow destructuve tests", NULL }, POPT_TABLEEND }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 7847f09..72b7c0d 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -18,6 +18,7 @@ */ struct iscsi_context *iscsi_context_login(const char *initiatorname, const char *url, int *lun); +extern int data_loss; int T0100_read10_simple(const char *initiator, const char *url); @@ -43,3 +44,5 @@ int T0143_read12_rdprotect(const char *initiator, const char *url); int T0153_read16_rdprotect(const char *initiator, const char *url); int T0160_readcapacity16_simple(const char *initiator, const char *url); + +int T0170_unmap_simple(const char *initiator, const char *url);