diff --git a/Makefile.am b/Makefile.am index 532b046..83e44c5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -79,7 +79,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/0232_write12_flags.c test-tool/0233_write12_0blocks.c \ test-tool/0234_write12_beyondeol.c \ test-tool/0240_prefetch10_simple.c \ - test-tool/0250_prefetch16_simple.c + test-tool/0250_prefetch16_simple.c \ + test-tool/0260_get_lba_status_simple.c endif # LD_PRELOAD library. diff --git a/include/iscsi.h b/include/iscsi.h index ab77979..cee0c74 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -579,8 +579,13 @@ iscsi_readcapacity10_task(struct iscsi_context *iscsi, int lun, int lba, void *private_data); EXTERN struct scsi_task * iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, - iscsi_command_cb cb, - void *private_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, + void *private_data); EXTERN struct scsi_task * iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, @@ -725,6 +730,9 @@ iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba, EXTERN struct scsi_task * 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_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 42daf89..8219ee3 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -43,7 +43,8 @@ enum scsi_opcode { }; enum scsi_service_action_in { - SCSI_READCAPACITY16 = 0x10 + SCSI_READCAPACITY16 = 0x10, + SCSI_GET_LBA_STATUS = 0x12 }; /* sense keys */ @@ -576,6 +577,23 @@ struct scsi_readcapacity16 { uint16_t lalba; }; +enum scsi_provisioning_type { + SCSI_PROVISIONING_TYPE_MAPPED = 0x00, + SCSI_PROVISIONING_TYPE_DEALLOCATED = 0x01, + SCSI_PROVISIONING_TYPE_ANCHORED = 0x02 +}; + +struct scsi_lba_status_descriptor { + uint64_t lba; + uint32_t num_blocks; + enum scsi_provisioning_type provisioning; +}; + +struct scsi_get_lba_status { + uint32_t num_descriptors; + struct scsi_lba_status_descriptor *descriptors; +}; + EXTERN int scsi_datain_getfullsize(struct scsi_task *task); EXTERN void *scsi_datain_unmarshall(struct scsi_task *task); @@ -592,6 +610,7 @@ 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_get_lba_status(uint64_t starting_lba, uint32_t alloc_len); EXTERN struct scsi_task *scsi_cdb_unmap(int anchor, int group, uint16_t xferlen); EXTERN struct scsi_task *scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, int pbdata, int lbdata, uint32_t lba, int group, uint16_t num_blocks); EXTERN struct scsi_task *scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, int pbdata, int lbdata, uint64_t lba, int group, uint32_t num_blocks); diff --git a/lib/libiscsi.def b/lib/libiscsi.def index c5f7e88..b658936 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -11,6 +11,8 @@ iscsi_full_connect_async iscsi_full_connect_sync iscsi_get_error iscsi_get_fd +iscsi_get_lba_status_sync +iscsi_get_lba_status_task iscsi_get_target_address iscsi_inquiry_sync iscsi_inquiry_task @@ -85,6 +87,7 @@ iscsi_writesame16_sync iscsi_writesame16_task scsi_association_to_str scsi_cdb_inquiry +scsi_cdb_get_lba_status scsi_cdb_modesense6 scsi_cdb_prefetch10 scsi_cdb_prefetch16 diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 8245ae5..ed2477c 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -9,6 +9,8 @@ iscsi_full_connect_async iscsi_full_connect_sync iscsi_get_error iscsi_get_fd +iscsi_get_lba_status_sync +iscsi_get_lba_status_task iscsi_get_target_address iscsi_inquiry_sync iscsi_inquiry_task @@ -83,6 +85,7 @@ iscsi_writesame16_sync iscsi_writesame16_task scsi_association_to_str scsi_cdb_inquiry +scsi_cdb_get_lba_status scsi_cdb_modesense6 scsi_cdb_prefetch10 scsi_cdb_prefetch16 diff --git a/lib/scsi-command.c b/lib/scsi-command.c index 67340a6..c5a1e49 100644 --- a/lib/scsi-command.c +++ b/lib/scsi-command.c @@ -625,6 +625,28 @@ iscsi_readcapacity16_task(struct iscsi_context *iscsi, int lun, return task; } +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, void *private_data) +{ + struct scsi_task *task; + + task = scsi_cdb_get_lba_status(starting_lba, alloc_len); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "get-lba-status cdb."); + return NULL; + } + if (iscsi_scsi_command_async(iscsi, lun, task, cb, NULL, + private_data) != 0) { + scsi_free_scsi_task(task); + return NULL; + } + + return task; +} + struct scsi_task * iscsi_read6_task(struct iscsi_context *iscsi, int lun, uint32_t lba, uint32_t datalen, int blocksize, diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index af11d84..bb87073 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -269,6 +269,9 @@ static void * scsi_serviceactionin_datain_unmarshall(struct scsi_task *task) { struct scsi_readcapacity16 *rc16; + struct scsi_get_lba_status *gls; + int32_t len; + int i; switch (task->params.serviceactionin.sa) { case SCSI_READCAPACITY16: @@ -276,17 +279,45 @@ scsi_serviceactionin_datain_unmarshall(struct scsi_task *task) if (rc16 == NULL) { return NULL; } - rc16->returned_lba = htonl(*(uint32_t *)&(task->datain.data[0])); - rc16->returned_lba = (rc16->returned_lba << 32) | htonl(*(uint32_t *)&(task->datain.data[4])); - rc16->block_length = htonl(*(uint32_t *)&(task->datain.data[8])); + rc16->returned_lba = ntohl(*(uint32_t *)&(task->datain.data[0])); + rc16->returned_lba = (rc16->returned_lba << 32) | ntohl(*(uint32_t *)&(task->datain.data[4])); + rc16->block_length = ntohl(*(uint32_t *)&(task->datain.data[8])); rc16->p_type = (task->datain.data[12] >> 1) & 0x07; rc16->prot_en = task->datain.data[12] & 0x01; rc16->p_i_exp = (task->datain.data[13] >> 4) & 0x0f; rc16->lbppbe = task->datain.data[13] & 0x0f; rc16->lbpme = !!(task->datain.data[14] & 0x80); rc16->lbprz = !!(task->datain.data[14] & 0x40); - rc16->lalba = htons(*(uint16_t *)&(task->datain.data[14])) & 0x3fff; + rc16->lalba = ntohs(*(uint16_t *)&(task->datain.data[14])) & 0x3fff; return rc16; + case SCSI_GET_LBA_STATUS: + len = ntohl(*(uint32_t *)&(task->datain.data[0])); + if (len > task->datain.size - 4) { + len = task->datain.size - 4; + } + len = len / 16; + + gls = scsi_malloc(task, sizeof(struct scsi_get_lba_status)); + if (gls == NULL) { + return NULL; + } + gls->num_descriptors = len; + gls->descriptors = scsi_malloc(task, sizeof(struct scsi_lba_status_descriptor) * gls->num_descriptors); + if (gls->descriptors == NULL) { + return NULL; + } + + for (i = 0; i < (int)gls->num_descriptors; i++) { + gls->descriptors[i].lba = ntohl(*(uint32_t *)&(task->datain.data[8 + i * sizeof(struct scsi_lba_status_descriptor) + 0])); + gls->descriptors[i].lba <<= 32; + gls->descriptors[i].lba |= ntohl(*(uint32_t *)&(task->datain.data[8 + i * sizeof(struct scsi_lba_status_descriptor) + 4])); + + gls->descriptors[i].num_blocks = ntohl(*(uint32_t *)&(task->datain.data[8 + i * sizeof(struct scsi_lba_status_descriptor) + 8])); + + gls->descriptors[i].provisioning = task->datain.data[8 + i * sizeof(struct scsi_lba_status_descriptor) + 12] & 0x0f; + } + + return gls; } return NULL; @@ -1388,6 +1419,37 @@ scsi_cdb_readcapacity16(void) return scsi_cdb_serviceactionin16(SCSI_READCAPACITY16, 32); } +/* + * GET_LBA_STATUS + */ +struct scsi_task * +scsi_cdb_get_lba_status(uint64_t starting_lba, uint32_t alloc_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_SERVICE_ACTION_IN; + + task->cdb[1] = SCSI_GET_LBA_STATUS; + + *(uint32_t *)&task->cdb[2] = htonl(starting_lba >> 32); + *(uint32_t *)&task->cdb[6] = htonl(starting_lba & 0xffffffff); + *(uint32_t *)&task->cdb[10] = htonl(alloc_len); + + task->cdb_size = 16; + task->xfer_dir = SCSI_XFER_READ; + task->expxferlen = alloc_len; + + task->params.serviceactionin.sa = SCSI_GET_LBA_STATUS; + + return task; +} + int scsi_datain_getfullsize(struct scsi_task *task) { diff --git a/lib/sync.c b/lib/sync.c index 3fbe90b..6b3fd83 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -355,6 +355,25 @@ iscsi_readcapacity16_sync(struct iscsi_context *iscsi, int lun) 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) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_get_lba_status_task(iscsi, lun, starting_lba, alloc_len, + scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, + "Failed to send GetLbaStatus command"); + return NULL; + } + + event_loop(iscsi, &state); + + return state.task; +} + struct scsi_task * iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba, int num_blocks, int syncnv, int immed) diff --git a/test-tool/0260_get_lba_status_simple.c b/test-tool/0260_get_lba_status_simple.c new file mode 100644 index 0000000..fe8498d --- /dev/null +++ b/test-tool/0260_get_lba_status_simple.c @@ -0,0 +1,116 @@ +/* + 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 T0260_get_lba_status_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; + uint64_t 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); + + ret = 0; + + /* try reading one descriptor at offset 0 */ + printf("Read one descriptor at LBA 0 ... "); + task = iscsi_get_lba_status_sync(iscsi, lun, 0, 8 + 16); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send GET_LBA_STATUS command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("GET_LBA_STATUS 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"); + + + /* try reading one descriptor at end-of-device */ + printf("Read one descriptor at end-of-device ... "); + task = iscsi_get_lba_status_sync(iscsi, lun, num_blocks, 8 + 16); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send GET_LBA_STATUS command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("GET_LBA_STATUS 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 cb6fd36..bb570dc 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -108,6 +108,9 @@ struct scsi_test tests[] = { /* prefetch16*/ { "T0250_prefetch16_simple", T0250_prefetch16_simple }, +/* get_lba_status */ +{ "T0260_get_lba_status_simple", T0260_get_lba_status_simple }, + { NULL, NULL } }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index e12b185..33c47b2 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -77,3 +77,5 @@ int T0234_write12_beyondeol(const char *initiator, const char *url); int T0240_prefetch10_simple(const char *initiator, const char *url); int T0250_prefetch16_simple(const char *initiator, const char *url); + +int T0260_get_lba_status_simple(const char *initiator, const char *url);