diff --git a/Makefile.am b/Makefile.am index 07afcda..7f162bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -152,7 +152,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/1041_unsolicited_immediate_data.c \ test-tool/1042_unsolicited_nonimmediate_data.c \ test-tool/1100_persistent_reserve_in_read_keys_simple.c \ - test-tool/1110_persistent_reserve_in_serviceaction_range.c + test-tool/1110_persistent_reserve_in_serviceaction_range.c \ + test-tool/1120_persistent_reserve_out_clear_simple.c endif diff --git a/include/iscsi.h b/include/iscsi.h index cff3e02..c9e6ded 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -778,6 +778,11 @@ iscsi_persistent_reserve_in_task(struct iscsi_context *iscsi, int lun, int sa, uint16_t xferlen, iscsi_command_cb cb, void *private_data); +EXTERN struct scsi_task * +iscsi_persistent_reserve_out_task(struct iscsi_context *iscsi, int lun, + int sa, int scope, int type, void *params, + iscsi_command_cb cb, void *private_data); + EXTERN struct scsi_task * iscsi_unmap_task(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len, @@ -953,6 +958,11 @@ iscsi_writesame16_sync(struct iscsi_context *iscsi, int lun, EXTERN struct scsi_task * iscsi_persistent_reserve_in_sync(struct iscsi_context *iscsi, int lun, int sa, uint16_t xferlen); + +EXTERN struct scsi_task * +iscsi_persistent_reserve_out_sync(struct iscsi_context *iscsi, int lun, + int sa, int scope, int type, void *params); + EXTERN struct scsi_task * iscsi_unmap_sync(struct iscsi_context *iscsi, int lun, int anchor, int group, struct unmap_list *list, int list_len); diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index 27b7ae4..8340746 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -48,7 +48,8 @@ enum scsi_opcode { SCSI_OPCODE_WRITE_SAME10 = 0x41, SCSI_OPCODE_UNMAP = 0x42, SCSI_OPCODE_READTOC = 0x43, - SCSI_OPCODE_PERSISTENT_RESERVE_IN = 0x5E, + SCSI_OPCODE_PERSISTENT_RESERVE_IN = 0x5E, + SCSI_OPCODE_PERSISTENT_RESERVE_OUT = 0x5F, SCSI_OPCODE_READ16 = 0x88, SCSI_OPCODE_COMPARE_AND_WRITE = 0x89, SCSI_OPCODE_WRITE16 = 0x8A, @@ -79,6 +80,38 @@ enum scsi_service_action_in { SCSI_GET_LBA_STATUS = 0x12 }; +enum scsi_persistent_out_sa { + SCSI_PERSISTENT_RESERVE_REGISTER = 0, + SCSI_PERSISTENT_RESERVE_RESERVE = 1, + SCSI_PERSISTENT_RESERVE_RELEASE = 2, + SCSI_PERSISTENT_RESERVE_CLEAR = 3, + SCSI_PERSISTENT_RESERVE_PREEMPT = 4, + SCSI_PERSISTENT_RESERVE_PREEMPT_AND_ABORT = 5, + SCSI_PERSISTENT_RESERVE_REGISTER_AND_IGNORE_EXISTING_KEY = 6, + SCSI_PERSISTENT_RESERVE_REGISTER_AND_MOVE = 7 +}; + +enum scsi_persistent_out_scope { + SCSI_PERSISTENT_RESERVE_SCOPE_LU = 0 +}; + +enum scsi_persistent_out_type { + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE = 1, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS = 3, + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY = 5, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY = 6, + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS = 7, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS = 8 +}; + +struct scsi_persistent_reserve_out_basic { + uint64_t reservation_key; + uint64_t service_action_reservation_key; + uint8_t spec_i_pt; + uint8_t all_tg_pt; + uint8_t aptpl; +}; + enum scsi_maintenance_in { SCSI_REPORT_SUPPORTED_OP_CODES = 0x0c }; @@ -146,7 +179,7 @@ enum scsi_readtoc_fmt { SCSI_READ_PMA = 3, SCSI_READ_ATIP = 4 }; -struct scsi_readtoc_desc{ +struct scsi_readtoc_desc { union { struct scsi_toc_desc { int adr; @@ -735,6 +768,7 @@ 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_persistent_reserve_in(enum scsi_persistent_in_sa sa, uint16_t xferlen); +EXTERN struct scsi_task *scsi_cdb_persistent_reserve_out(enum scsi_persistent_out_sa sa, enum scsi_persistent_out_scope scope, enum scsi_persistent_out_type type, void *params); 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); EXTERN struct scsi_task *scsi_cdb_prefetch10(uint32_t lba, int num_blocks, int immed, int group); diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index a9fd53f..06d7d66 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -1276,6 +1276,28 @@ iscsi_persistent_reserve_in_task(struct iscsi_context *iscsi, int lun, return task; } +struct scsi_task * +iscsi_persistent_reserve_out_task(struct iscsi_context *iscsi, int lun, + int sa, int scope, int type, void *param, + iscsi_command_cb cb, void *private_data) +{ + struct scsi_task *task; + + task = scsi_cdb_persistent_reserve_out(sa, scope, type, param); + if (task == NULL) { + iscsi_set_error(iscsi, "Out-of-memory: Failed to create " + "persistent-reserver-out 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_prefetch10_task(struct iscsi_context *iscsi, int lun, uint32_t lba, int num_blocks, int immed, int group, diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 788fbfe..127c611 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -30,6 +30,8 @@ iscsi_parse_full_url iscsi_parse_portal_url iscsi_persistent_reserve_in_task iscsi_persistent_reserve_in_sync +iscsi_persistent_reserve_out_task +iscsi_persistent_reserve_out_sync iscsi_prefetch10_sync iscsi_prefetch10_task iscsi_prefetch16_sync @@ -136,6 +138,7 @@ scsi_cdb_inquiry scsi_cdb_get_lba_status scsi_cdb_modesense6 scsi_cdb_persistent_reserve_in +scsi_cdb_persistent_reserve_out scsi_cdb_prefetch10 scsi_cdb_prefetch16 scsi_cdb_preventallow diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index b79242e..1a723e1 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -28,6 +28,8 @@ iscsi_parse_full_url iscsi_parse_portal_url iscsi_persistent_reserve_in_task iscsi_persistent_reserve_in_sync +iscsi_persistent_reserve_out_task +iscsi_persistent_reserve_out_sync iscsi_prefetch10_sync iscsi_prefetch10_task iscsi_prefetch16_sync @@ -134,6 +136,7 @@ scsi_cdb_inquiry scsi_cdb_get_lba_status scsi_cdb_modesense6 scsi_cdb_persistent_reserve_in +scsi_cdb_persistent_reserve_out scsi_cdb_prefetch10 scsi_cdb_prefetch16 scsi_cdb_preventallow diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 2276a9a..7602e87 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -38,6 +38,8 @@ #include "slist.h" #include "scsi-lowlevel.h" +void scsi_task_set_iov_out(struct scsi_task *task, struct scsi_iovec *iov, int niov); + struct scsi_allocated_memory { struct scsi_allocated_memory *next; char buf[0]; @@ -186,6 +188,19 @@ scsi_get_uint16(const unsigned char *c) return ntohs(*(uint16_t *)c); } +inline void +scsi_set_uint64(unsigned char *c, uint64_t v) +{ + uint32_t val; + + val = (v >> 32) & 0xffffffff; + *(uint32_t *)c = htonl(val); + + c += 4; + val = v & 0xffffffff; + *(uint32_t *)c = htonl(val); +} + inline void scsi_set_uint32(unsigned char *c, uint32_t val) { @@ -1702,6 +1717,91 @@ scsi_cdb_persistent_reserve_in(enum scsi_persistent_in_sa sa, uint16_t xferlen) return task; } +/* + * PERSISTENT_RESERVE_OUT + */ +struct scsi_task * +scsi_cdb_persistent_reserve_out(enum scsi_persistent_out_sa sa, enum scsi_persistent_out_scope scope, enum scsi_persistent_out_type type, void *param) +{ + struct scsi_task *task; + struct scsi_persistent_reserve_out_basic *basic; + struct scsi_iovec *iov; + unsigned char *buf; + int xferlen; + + task = malloc(sizeof(struct scsi_task)); + if (task == NULL) { + return NULL; + } + + iov = scsi_malloc(task, sizeof(struct scsi_iovec)); + if (iov == NULL) { + free(task); + return NULL; + } + + switch(sa) { + case SCSI_PERSISTENT_RESERVE_REGISTER: + case SCSI_PERSISTENT_RESERVE_RESERVE: + case SCSI_PERSISTENT_RESERVE_RELEASE: + case SCSI_PERSISTENT_RESERVE_CLEAR: + case SCSI_PERSISTENT_RESERVE_PREEMPT: + case SCSI_PERSISTENT_RESERVE_PREEMPT_AND_ABORT: + case SCSI_PERSISTENT_RESERVE_REGISTER_AND_IGNORE_EXISTING_KEY: + basic = param; + + xferlen = 24; + buf = scsi_malloc(task, xferlen); + if (buf == NULL) { + free(task); + free(iov); + return NULL; + } + + memset(buf, 0, xferlen); + scsi_set_uint64(&buf[0], basic->reservation_key); + scsi_set_uint64(&buf[8], basic->service_action_reservation_key); + if (basic->spec_i_pt) { + buf[20] |= 0x08; + } + if (basic->all_tg_pt) { + buf[20] |= 0x04; + } + if (basic->aptpl) { + buf[20] |= 0x01; + } + break; + case SCSI_PERSISTENT_RESERVE_REGISTER_AND_MOVE: + /* XXX FIXME */ + free(task); + free(iov); + return NULL; + default: + free(task); + free(iov); + return NULL; + } + + + memset(task, 0, sizeof(struct scsi_task)); + task->cdb[0] = SCSI_OPCODE_PERSISTENT_RESERVE_OUT; + + task->cdb[1] |= sa & 0x1f; + task->cdb[2] = ((scope << 4) & 0xf0) | (type & 0x0f); + + scsi_set_uint32(&task->cdb[5], xferlen); + + task->cdb_size = 10; + task->xfer_dir = SCSI_XFER_WRITE; + task->expxferlen = xferlen; + + iov->iov_base = buf; + iov->iov_len = xferlen; + scsi_task_set_iov_out(task, iov, 1); + + return task; +} + /* * WRITE_SAME10 */ diff --git a/lib/sync.c b/lib/sync.c index ece187e..20b572a 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -818,6 +818,27 @@ iscsi_persistent_reserve_in_sync(struct iscsi_context *iscsi, int lun, return state.task; } +struct scsi_task * +iscsi_persistent_reserve_out_sync(struct iscsi_context *iscsi, int lun, + int sa, int scope, int type, void *param) +{ + struct iscsi_sync_state state; + + memset(&state, 0, sizeof(state)); + + if (iscsi_persistent_reserve_out_task(iscsi, lun, + sa, scope, type, param, + scsi_sync_cb, &state) == NULL) { + iscsi_set_error(iscsi, + "Failed to send PERSISTENT_RESERVE_OUT command"); + return NULL; + } + + event_loop(iscsi, &state); + + 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) diff --git a/test-tool/1120_persistent_reserve_out_clear_simple.c b/test-tool/1120_persistent_reserve_out_clear_simple.c new file mode 100644 index 0000000..a32297f --- /dev/null +++ b/test-tool/1120_persistent_reserve_out_clear_simple.c @@ -0,0 +1,97 @@ +/* + 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 +#include "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +int T1120_persistent_reserve_out_clear_simple(const char *initiator, const char *url, int data_loss _U_, int show_info) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_persistent_reserve_out_basic poc; + int ret, lun; + + printf("1120_persistent_reserve_out_clear_simple:\n"); + printf("============================================\n"); + if (show_info) { + printf("Test basic PERSISTENT_RESERVE_OUT/CLEAR functionality.\n"); + printf("1, Verify that CLEAR works.\n"), + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + if (!data_loss) { + printf("--dataloss flag is not set. Skipping test\n"); + ret = -2; + goto finished; + } + + ret = 0; + + /* Verify that PERSISTENT_RESERVE_OUT/CLEAR works */ + printf("Send PERSISTENT_RESERVE_OUT/CLEAR ... "); + poc.reservation_key = 1L; + poc.service_action_reservation_key = 2L; + poc.spec_i_pt = 0; + poc.all_tg_pt = 1; + poc.aptpl = 1; + task = iscsi_persistent_reserve_out_sync(iscsi, lun, + SCSI_PERSISTENT_RESERVE_CLEAR, + SCSI_PERSISTENT_RESERVE_SCOPE_LU, + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE, + &poc); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send PERSISTENT_RESERVE_OUT command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST + && task->sense.ascq == SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE) { + printf("[SKIPPED]\n"); + printf("PERSISTENT_RESERVE_OUT Not Supported\n"); + ret = -2; + scsi_free_scsi_task(task); + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("PERSISTENT_RESERVE_OUT 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 ab979ed..cb587bd 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -256,6 +256,7 @@ struct scsi_test tests[] = { /* PERSISTENT_RESERVE_IN */ { "T1100_persistent_reserve_in_read_keys_simple", T1100_persistent_reserve_in_read_keys_simple }, { "T1110_persistent_reserve_in_serviceaction_range", T1110_persistent_reserve_in_serviceaction_range }, +{ "T1120_persistent_reserve_out_clear_simple", T1120_persistent_reserve_out_clear_simple }, { NULL, NULL } }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 9b97474..33e3263 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -187,3 +187,4 @@ int T1041_unsolicited_immediate_data(const char *initiator, const char *url, int int T1042_unsolicited_nonimmediate_data(const char *initiator, const char *url, int data_loss, int show_info); int T1100_persistent_reserve_in_read_keys_simple(const char *initiator, const char *url, int data_loss, int show_info); int T1110_persistent_reserve_in_serviceaction_range(const char *initiator, const char *url, int data_loss, int show_info); +int T1120_persistent_reserve_out_clear_simple(const char *initiator, const char *url, int data_loss, int show_info);