From ef8e59a24d03742310a2aaba8e33c14f1aaec5d1 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Thu, 24 Sep 2015 16:10:57 +0200 Subject: [PATCH 1/6] lib/scsi: fix PRin REPORT CAPS allow_cmds unmarshalling The Persistent Reserve In REPORT CAPABILITIES response carries the ALLOW COMMANDS field in bits 4-6 at byte offset 3. Bit 7 (TMV) should be masked out during ALLOW COMMANDS unmarshalling. Signed-off-by: David Disseldorp --- lib/scsi-lowlevel.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index f39b0fb..42d2106 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -947,7 +947,7 @@ scsi_persistentreservein_datain_unmarshall(struct scsi_task *task) rc->atp_c = !!(task_get_uint8(task, 2) & 0x04); rc->ptpl_c = !!(task_get_uint8(task, 2) & 0x01); rc->tmv = !!(task_get_uint8(task, 3) & 0x80); - rc->allow_commands = task_get_uint8(task, 3) >> 4; + rc->allow_commands = (task_get_uint8(task, 3) & 0x70) >> 4; rc->persistent_reservation_type_mask = task_get_uint16(task, 4); return rc; @@ -3396,7 +3396,7 @@ scsi_datain_getfullsize(struct scsi_task *task) case SCSI_OPCODE_INQUIRY: return scsi_inquiry_datain_getfullsize(task); case SCSI_OPCODE_MODESENSE6: - return scsi_modesense_datain_getfullsize(task, 1); + return scsi_modesense_datain_getfullsize(task, 1); case SCSI_OPCODE_READCAPACITY10: return scsi_readcapacity10_datain_getfullsize(task); case SCSI_OPCODE_SYNCHRONIZECACHE10: @@ -3425,7 +3425,7 @@ scsi_datain_unmarshall(struct scsi_task *task) case SCSI_OPCODE_MODESENSE6: return scsi_modesense_datain_unmarshall(task, 1); case SCSI_OPCODE_MODESENSE10: - return scsi_modesense_datain_unmarshall(task, 0); + return scsi_modesense_datain_unmarshall(task, 0); case SCSI_OPCODE_READCAPACITY10: return scsi_readcapacity10_datain_unmarshall(task); case SCSI_OPCODE_READTOC: From 9c524b37fa1052d7f94639cb7d7a831303c8bc8b Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Thu, 24 Sep 2015 16:38:48 +0200 Subject: [PATCH 2/6] test: add prin_report_caps() helper function Issue a Persistent Reservation In REPORT CAPABILITIES request and parse the response. Callers can obtain the unmarshalled response data via the _rcaps parameter. Signed-off-by: David Disseldorp --- test-tool/iscsi-support.c | 46 +++++++++++++++++++++++++++++++++++++++ test-tool/iscsi-support.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index ce11612..33619d1 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -1155,6 +1155,52 @@ prin_verify_not_reserved(struct scsi_device *sdev) return ret; } +int +prin_report_caps(struct scsi_device *sdev, struct scsi_task **tp, + struct scsi_persistent_reserve_in_report_capabilities **_rcaps) +{ + const int buf_sz = 16384; + struct scsi_persistent_reserve_in_report_capabilities *rcaps = NULL; + + logging(LOG_VERBOSE, "Send PRIN/REPORT_CAPABILITIES"); + + *tp = scsi_cdb_persistent_reserve_in( + SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES, + buf_sz); + assert(*tp != NULL); + + *tp = send_scsi_command(sdev, *tp, NULL); + if (*tp == NULL) { + logging(LOG_NORMAL, + "[FAILED] Failed to send PRIN command: %s", + iscsi_get_error(sdev->iscsi_ctx)); + return -1; + } + if (status_is_invalid_opcode(*tp)) { + logging(LOG_NORMAL, + "[SKIPPED] PERSISTENT RESERVE IN is not implemented."); + return -2; + } + if ((*tp)->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, + "[FAILED] PRIN command: failed with sense. %s", + iscsi_get_error(sdev->iscsi_ctx)); + return -1; + } + + rcaps = scsi_datain_unmarshall(*tp); + if (rcaps == NULL) { + logging(LOG_NORMAL, + "[FAIL] failed to unmarshall PRIN/REPORT_CAPABILITIES " + "data. %s", iscsi_get_error(sdev->iscsi_ctx)); + return -1; + } + if (_rcaps != NULL) + *_rcaps = rcaps; + + return 0; +} + int verify_read_works(struct scsi_device *sdev, unsigned char *buf) { diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index 4c43b14..e2c55a9 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -277,6 +277,8 @@ int prout_clear(struct scsi_device *sdev, unsigned long long key); int prin_verify_not_reserved(struct scsi_device *sdev); int prin_verify_reserved_as(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type); +int prin_report_caps(struct scsi_device *sdev, struct scsi_task **tp, + struct scsi_persistent_reserve_in_report_capabilities **_rcaps); int verify_read_works(struct scsi_device *sdev, unsigned char *buf); int verify_write_works(struct scsi_device *sdev, unsigned char *buf); int verify_read_fails(struct scsi_device *sdev, unsigned char *buf); From 9aa5b55b41942527d762741f764145e2faa0d1c8 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Thu, 24 Sep 2015 16:47:23 +0200 Subject: [PATCH 3/6] test/pr: simple PRin REPORT CAPABILITIES test Check that the Persistent Reserve In REPORT CAPABILITIES response carries valid LENGTH, ALLOW COMMANDS, and PERSISTENT RESERVATION TYPE MASK fields. Signed-off-by: David Disseldorp --- include/scsi-lowlevel.h | 16 +++++ test-tool/Makefile.am | 1 + test-tool/iscsi-test-cu.c | 7 ++ test-tool/iscsi-test-cu.h | 1 + test-tool/test_prin_report_caps.c | 114 ++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 test-tool/test_prin_report_caps.c diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h index a7144f1..adb1be1 100644 --- a/include/scsi-lowlevel.h +++ b/include/scsi-lowlevel.h @@ -945,6 +945,22 @@ struct scsi_persistent_reserve_in_read_reservation { unsigned char pr_type; }; +enum scsi_persistent_reservation_type_mask { + SCSI_PR_TYPE_MASK_EX_AC_AR = (1 << 0), + SCSI_PR_TYPE_MASK_WR_EX = (1 << 9), + SCSI_PR_TYPE_MASK_EX_AC = (1 << 11), + SCSI_PR_TYPE_MASK_WR_EX_RO = (1 << 13), + SCSI_PR_TYPE_MASK_EX_AC_RO = (1 << 14), + SCSI_PR_TYPE_MASK_WR_EX_AR = (1 << 15), + + SCSI_PR_TYPE_MASK_ALL = (SCSI_PR_TYPE_MASK_EX_AC_AR + | SCSI_PR_TYPE_MASK_WR_EX + | SCSI_PR_TYPE_MASK_EX_AC + | SCSI_PR_TYPE_MASK_WR_EX_RO + | SCSI_PR_TYPE_MASK_EX_AC_RO + | SCSI_PR_TYPE_MASK_WR_EX_AR) +}; + struct scsi_persistent_reserve_in_report_capabilities { uint16_t length; uint8_t crh; diff --git a/test-tool/Makefile.am b/test-tool/Makefile.am index 8b2e2e0..cc12803 100644 --- a/test-tool/Makefile.am +++ b/test-tool/Makefile.am @@ -69,6 +69,7 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \ test_preventallow_2_itnexuses.c \ test_prin_read_keys_simple.c \ test_prin_serviceaction_range.c \ + test_prin_report_caps.c \ test_prout_register_simple.c \ test_prout_reserve_simple.c \ test_prout_reserve_access.c \ diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index 5646b74..826061d 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -157,6 +157,11 @@ static CU_TestInfo tests_prin_read_keys[] = { CU_TEST_INFO_NULL }; +static CU_TestInfo tests_prin_report_caps[] = { + { (char *)"Simple", test_prin_report_caps_simple }, + CU_TEST_INFO_NULL +}; + static CU_TestInfo tests_prout_register[] = { { (char *)"Simple", test_prout_register_simple }, CU_TEST_INFO_NULL @@ -482,6 +487,7 @@ static libiscsi_suite_info scsi_suites[] = { { "PreventAllow", NON_PGR_FUNCS, tests_preventallow }, { "PrinReadKeys", NON_PGR_FUNCS, tests_prin_read_keys }, { "PrinServiceactionRange", NON_PGR_FUNCS, tests_prin_serviceaction_range }, + { "PrinReportCapabilities", NON_PGR_FUNCS, tests_prin_report_caps }, { "ProutRegister", NON_PGR_FUNCS, tests_prout_register }, { "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve }, { "ProutClear", NON_PGR_FUNCS, tests_prout_clear }, @@ -568,6 +574,7 @@ static libiscsi_suite_info all_suites[] = { { "PrinReadKeys", NON_PGR_FUNCS, tests_prin_read_keys }, { "PrinServiceactionRange", NON_PGR_FUNCS, tests_prin_serviceaction_range }, + { "PrinReportCapabilities", NON_PGR_FUNCS, tests_prin_report_caps }, { "ProutRegister", NON_PGR_FUNCS, tests_prout_register }, { "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve }, { "ProutClear", NON_PGR_FUNCS, tests_prout_clear }, diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h index fa9a0c0..e9330dc 100644 --- a/test-tool/iscsi-test-cu.h +++ b/test-tool/iscsi-test-cu.h @@ -114,6 +114,7 @@ void test_preventallow_2_itnexuses(void); void test_prin_read_keys_simple(void); void test_prin_serviceaction_range(void); +void test_prin_report_caps_simple(void); void test_prout_register_simple(void); void test_prout_reserve_simple(void); diff --git a/test-tool/test_prin_report_caps.c b/test-tool/test_prin_report_caps.c new file mode 100644 index 0000000..688c3b2 --- /dev/null +++ b/test-tool/test_prin_report_caps.c @@ -0,0 +1,114 @@ +/* + Copyright (C) 2015 David Disseldorp + + 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 "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-support.h" +#include "iscsi-test-cu.h" + +static struct test_prin_report_caps_types { + enum scsi_persistent_reservation_type_mask mask; + enum scsi_persistent_out_type op; +} report_caps_types_array[] = { + { SCSI_PR_TYPE_MASK_WR_EX_AR, + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS }, + { SCSI_PR_TYPE_MASK_EX_AC_RO, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY }, + { SCSI_PR_TYPE_MASK_WR_EX_RO, + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY }, + { SCSI_PR_TYPE_MASK_EX_AC, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS }, + { SCSI_PR_TYPE_MASK_WR_EX, + SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE }, + { SCSI_PR_TYPE_MASK_EX_AC_AR, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS }, + { 0, 0 } +}; + +void +test_prin_report_caps_simple(void) +{ + int ret = 0; + const unsigned long long key = rand_key(); + struct scsi_task *tsk; + struct scsi_persistent_reserve_in_report_capabilities *rcaps; + struct test_prin_report_caps_types *type; + + CHECK_FOR_DATALOSS; + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, + "Test Persistent Reserve In REPORT CAPABILITIES works."); + + /* register our reservation key with the target */ + ret = prout_register_and_ignore(sd, key); + if (ret == -2) { + logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); + CU_PASS("PERSISTENT RESERVE OUT is not implemented."); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + ret = prin_report_caps(sd, &tsk, &rcaps); + CU_ASSERT_EQUAL(ret, 0); + + logging(LOG_VERBOSE, + "Checking PERSISTENT RESERVE IN REPORT CAPABILITIES fields."); + CU_ASSERT_EQUAL(rcaps->length, 8); + CU_ASSERT_TRUE(rcaps->allow_commands <= 5); + CU_ASSERT_EQUAL(rcaps->persistent_reservation_type_mask + & ~SCSI_PR_TYPE_MASK_ALL, 0); + + for (type = &report_caps_types_array[0]; type->mask != 0; type++) { + if (!(rcaps->persistent_reservation_type_mask & type->mask)) { + logging(LOG_NORMAL, + "PERSISTENT RESERVE op 0x%x not supported", + type->op); + continue; + } + + logging(LOG_VERBOSE, + "PERSISTENT RESERVE OUT op 0x%x supported, testing", + type->op); + + /* reserve the target */ + ret = prout_reserve(sd, key, type->op); + CU_ASSERT_EQUAL(ret, 0); + + /* verify target reservation */ + ret = prin_verify_reserved_as(sd, + pr_type_is_all_registrants(type->op) ? 0 : key, + type->op); + CU_ASSERT_EQUAL(0, ret); + + /* release the target */ + ret = prout_release(sd, key, type->op); + CU_ASSERT_EQUAL(ret, 0); + } + + scsi_free_scsi_task(tsk); + rcaps = NULL; /* freed with tsk */ + + /* drop registration */ + ret = prout_register_key(sd, 0, key); + CU_ASSERT_EQUAL(ret, 0); +} From 760ae95d9c9bd3941a8d1334f0272e5348bb07ba Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 25 Sep 2015 12:09:58 +0200 Subject: [PATCH 4/6] test: add prout_preempt() helper function Issue a Persistent Reservation Out PREEMPT request, with keys and type provided. PREEMPT allows for the removal of registrations, and replacement of reservations. Signed-off-by: David Disseldorp --- test-tool/iscsi-support.c | 52 +++++++++++++++++++++++++++++++++++++++ test-tool/iscsi-support.h | 3 +++ 2 files changed, 55 insertions(+) diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 33619d1..3aa4cb8 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -1022,6 +1022,58 @@ prout_clear(struct scsi_device *sdev, unsigned long long key) return ret; } +int +prout_preempt(struct scsi_device *sdev, + unsigned long long sark, unsigned long long rk, + enum scsi_persistent_out_type pr_type) +{ + struct scsi_persistent_reserve_out_basic poc; + struct scsi_task *task; + int ret = 0; + + /* reserve the target using specified reservation type */ + logging(LOG_VERBOSE, + "Send PROUT/PREEMPT to preempt reservation and/or " + "registration"); + + if (!data_loss) { + printf("--dataloss flag is not set in. Skipping PROUT\n"); + return -1; + } + + memset(&poc, 0, sizeof (poc)); + poc.reservation_key = rk; + poc.service_action_reservation_key = sark; + task = scsi_cdb_persistent_reserve_out( + SCSI_PERSISTENT_RESERVE_PREEMPT, + SCSI_PERSISTENT_RESERVE_SCOPE_LU, + pr_type, &poc); + assert(task != NULL); + + task = send_scsi_command(sdev, task, NULL); + if (task == NULL) { + logging(LOG_NORMAL, + "[FAILED] Failed to send PROUT command: %s", + iscsi_get_error(sdev->iscsi_ctx)); + return -1; + } + if (status_is_invalid_opcode(task)) { + scsi_free_scsi_task(task); + logging(LOG_NORMAL, "[SKIPPED] PERSISTENT RESERVE OUT is not implemented."); + return -2; + } + + if (task->status != SCSI_STATUS_GOOD) { + logging(LOG_NORMAL, + "[FAILED] PROUT command: failed with sense. %s", + iscsi_get_error(sdev->iscsi_ctx)); + ret = -1; + } + + scsi_free_scsi_task(task); + return ret; +} + int prin_verify_reserved_as(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type) diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index e2c55a9..34ff57a 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -274,6 +274,9 @@ int prout_reserve(struct scsi_device *sdev, int prout_release(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type); int prout_clear(struct scsi_device *sdev, unsigned long long key); +int prout_preempt(struct scsi_device *sdev, + unsigned long long sark, unsigned long long rk, + enum scsi_persistent_out_type pr_type); int prin_verify_not_reserved(struct scsi_device *sdev); int prin_verify_reserved_as(struct scsi_device *sdev, unsigned long long key, enum scsi_persistent_out_type pr_type); From a1d11eb0f9b5f65317fd7c61c74fbb95c6651d3c Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Mon, 28 Sep 2015 14:33:14 +0200 Subject: [PATCH 5/6] test: move UA drain helper function into iscsi-support test_iscsi_tur_until_good() dispatches TUR requests until the target has cleared all UAs for the given sd, or the maximum number of retries is reached. This helper function is useful for any test that needs to deal with UAs (e.g. PRs), so should be moved out into common code. Signed-off-by: David Disseldorp --- test-tool/iscsi-support.c | 35 ++++++++++++++++++++++++++++++ test-tool/iscsi-support.h | 1 + test-tool/test_multipathio_reset.c | 30 ------------------------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 3aa4cb8..97fa594 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef HAVE_SG_IO #include @@ -2943,3 +2944,37 @@ int receive_copy_results(struct scsi_device *sdev, enum scsi_copy_results_sa sa, return ret; } + +#define TEST_ISCSI_TUR_MAX_RETRIES 5 + +int +test_iscsi_tur_until_good(struct scsi_device *iscsi_sd, int *num_uas) +{ + int num_turs; + + if (iscsi_sd->iscsi_ctx == NULL) { + logging(LOG_NORMAL, "invalid sd for tur_until_good"); + return -EINVAL; + } + + *num_uas = 0; + for (num_turs = 0; num_turs < TEST_ISCSI_TUR_MAX_RETRIES; num_turs++) { + struct scsi_task *tsk; + tsk = iscsi_testunitready_sync(iscsi_sd->iscsi_ctx, + iscsi_sd->iscsi_lun); + if (tsk->status == SCSI_STATUS_GOOD) { + logging(LOG_VERBOSE, "TUR good after %d retries", + num_turs); + return 0; + } else if ((tsk->status == SCSI_STATUS_CHECK_CONDITION) + && (tsk->sense.key == SCSI_SENSE_UNIT_ATTENTION)) { + logging(LOG_VERBOSE, "Got UA for TUR"); + (*num_uas)++; + } else { + logging(LOG_NORMAL, "unexpected non-UA failure: %d,%d", + tsk->status, tsk->sense.key); + } + } + + return -ETIMEDOUT; +} diff --git a/test-tool/iscsi-support.h b/test-tool/iscsi-support.h index 34ff57a..df7fb11 100644 --- a/test-tool/iscsi-support.h +++ b/test-tool/iscsi-support.h @@ -336,4 +336,5 @@ int populate_seg_desc_hdr(unsigned char *hdr, enum ec_descr_type_code desc_type, int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat, int src_index, int dst_index, int num_blks, uint64_t src_lba, uint64_t dst_lba); void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len); int receive_copy_results(struct scsi_device *sdev, enum scsi_copy_results_sa sa, int list_id, void **datap, int status, enum scsi_sense_key key, int *ascq, int num_ascq); +int test_iscsi_tur_until_good(struct scsi_device *iscsi_sd, int *num_uas); #endif /* _ISCSI_SUPPORT_H_ */ diff --git a/test-tool/test_multipathio_reset.c b/test-tool/test_multipathio_reset.c index e7b78ad..f2c8da3 100644 --- a/test-tool/test_multipathio_reset.c +++ b/test-tool/test_multipathio_reset.c @@ -27,36 +27,6 @@ #include "iscsi-test-cu.h" #include "iscsi-multipath.h" -#define MPATH_MAX_TUR_RETRIES 5 - -static int -test_iscsi_tur_until_good(struct scsi_device *iscsi_sd, - int *num_uas) -{ - int num_turs; - - *num_uas = 0; - for (num_turs = 0; num_turs < MPATH_MAX_TUR_RETRIES; num_turs++) { - struct scsi_task *tsk; - tsk = iscsi_testunitready_sync(iscsi_sd->iscsi_ctx, - iscsi_sd->iscsi_lun); - if (tsk->status == SCSI_STATUS_GOOD) { - logging(LOG_VERBOSE, "TUR good after %d retries", - num_turs); - return 0; - } else if ((tsk->status == SCSI_STATUS_CHECK_CONDITION) - && (tsk->sense.key == SCSI_SENSE_UNIT_ATTENTION)) { - logging(LOG_VERBOSE, "Got UA for TUR"); - (*num_uas)++; - } else { - logging(LOG_NORMAL, "unexpected non-UA failure: %d,%d", - tsk->status, tsk->sense.key); - } - } - - return -ETIMEDOUT; -} - void test_multipathio_reset(void) { From b9b414981c0710b9891ad73fb1148de60f671525 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 25 Sep 2015 12:37:30 +0200 Subject: [PATCH 6/6] test/pr: PRout PREEMPT remove registration test Check that the Persistent Reserve Out PREEMT request removes the registration referenced by the service action key. Signed-off-by: David Disseldorp --- test-tool/Makefile.am | 1 + test-tool/iscsi-test-cu.c | 8 +++ test-tool/iscsi-test-cu.h | 1 + test-tool/test_prout_preempt.c | 121 +++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 test-tool/test_prout_preempt.c diff --git a/test-tool/Makefile.am b/test-tool/Makefile.am index cc12803..f026a60 100644 --- a/test-tool/Makefile.am +++ b/test-tool/Makefile.am @@ -75,6 +75,7 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \ test_prout_reserve_access.c \ test_prout_reserve_ownership.c \ test_prout_clear_simple.c \ + test_prout_preempt.c \ test_read6_simple.c \ test_read6_beyond_eol.c \ test_read10_simple.c \ diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c index 826061d..cb34f78 100644 --- a/test-tool/iscsi-test-cu.c +++ b/test-tool/iscsi-test-cu.c @@ -203,6 +203,12 @@ static CU_TestInfo tests_prout_clear[] = { CU_TEST_INFO_NULL }; +static CU_TestInfo tests_prout_preempt[] = { + { (char *)"RemoveRegistration", + test_prout_preempt_rm_reg }, + CU_TEST_INFO_NULL +}; + static CU_TestInfo tests_prin_serviceaction_range[] = { { (char *)"Range", test_prin_serviceaction_range }, CU_TEST_INFO_NULL @@ -491,6 +497,7 @@ static libiscsi_suite_info scsi_suites[] = { { "ProutRegister", NON_PGR_FUNCS, tests_prout_register }, { "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve }, { "ProutClear", NON_PGR_FUNCS, tests_prout_clear }, + { "ProutPreempt", NON_PGR_FUNCS, tests_prout_preempt }, { "Read6", NON_PGR_FUNCS, tests_read6 }, { "Read10", NON_PGR_FUNCS, tests_read10 }, { "Read12", NON_PGR_FUNCS, tests_read12 }, @@ -578,6 +585,7 @@ static libiscsi_suite_info all_suites[] = { { "ProutRegister", NON_PGR_FUNCS, tests_prout_register }, { "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve }, { "ProutClear", NON_PGR_FUNCS, tests_prout_clear }, + { "ProutPreempt", NON_PGR_FUNCS, tests_prout_preempt }, { "Read6", NON_PGR_FUNCS, tests_read6 }, { "Read10", NON_PGR_FUNCS, tests_read10 }, { "Read12", NON_PGR_FUNCS, tests_read12 }, diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h index e9330dc..dc75c6e 100644 --- a/test-tool/iscsi-test-cu.h +++ b/test-tool/iscsi-test-cu.h @@ -131,6 +131,7 @@ void test_prout_reserve_ownership_wero(void); void test_prout_reserve_ownership_eaar(void); void test_prout_reserve_ownership_wear(void); void test_prout_clear_simple(void); +void test_prout_preempt_rm_reg(void); void test_read6_simple(void); void test_read6_beyond_eol(void); diff --git a/test-tool/test_prout_preempt.c b/test-tool/test_prout_preempt.c new file mode 100644 index 0000000..47953bb --- /dev/null +++ b/test-tool/test_prout_preempt.c @@ -0,0 +1,121 @@ +/* + Copyright (C) 2015 David Disseldorp + + 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 "iscsi.h" +#include "scsi-lowlevel.h" +#include "iscsi-support.h" +#include "iscsi-test-cu.h" +#include "iscsi-multipath.h" + +void +test_prout_preempt_rm_reg(void) +{ + int ret = 0; + const unsigned long long k1 = rand_key(); + const unsigned long long k2 = rand_key(); + struct scsi_device *sd2; + struct scsi_task *tsk; + uint32_t old_gen; + int num_uas; + struct scsi_persistent_reserve_in_read_keys *rk; + + CHECK_FOR_DATALOSS; + + if (sd->iscsi_ctx == NULL) { + const char *err = "[SKIPPED] This PERSISTENT RESERVE test is " + "only supported for iSCSI backends"; + logging(LOG_NORMAL, "%s", err); + CU_PASS(err); + return; + } + + logging(LOG_VERBOSE, LOG_BLANK_LINE); + logging(LOG_VERBOSE, "Test Persistent Reserve IN PREEMPT works."); + + ret = prout_register_and_ignore(sd, k1); + if (ret == -2) { + logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE OUT is not implemented."); + CU_PASS("PERSISTENT RESERVE OUT is not implemented."); + return; + } + CU_ASSERT_EQUAL(ret, 0); + + /* clear all PR state */ + ret = prout_clear(sd, k1); + CU_ASSERT_EQUAL(ret, 0); + + /* need to reregister cleared key */ + ret = prout_register_and_ignore(sd, k1); + CU_ASSERT_EQUAL(ret, 0); + + ret = mpath_sd2_get_or_clone(sd, &sd2); + CU_ASSERT_EQUAL(ret, 0); + + /* register secondary key */ + ret = prout_register_and_ignore(sd2, k2); + CU_ASSERT_EQUAL(ret, 0); + + /* confirm that k1 and k2 are registered */ + ret = prin_read_keys(sd, &tsk, &rk); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + CU_ASSERT_EQUAL(rk->num_keys, 2); + /* retain PR generation number to check for increments */ + old_gen = rk->prgeneration; + + scsi_free_scsi_task(tsk); + rk = NULL; /* freed with tsk */ + + /* use second connection to clear k1 registration */ + ret = prout_preempt(sd2, k1, k2, + SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS); + CU_ASSERT_EQUAL(ret, 0); + + /* clear any UAs generated by preempt */ + ret = test_iscsi_tur_until_good(sd, &num_uas); + CU_ASSERT_EQUAL(ret, 0); + ret = test_iscsi_tur_until_good(sd2, &num_uas); + CU_ASSERT_EQUAL(ret, 0); + + ret = prin_read_keys(sd, &tsk, &rk); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + CU_ASSERT_EQUAL(rk->num_keys, 1); + /* ensure preempt bumped generation number */ + CU_ASSERT_EQUAL(rk->prgeneration, old_gen + 1); + /* ensure k2 is retained */ + CU_ASSERT_EQUAL(rk->keys[0], k2); + + /* unregister k2 */ + ret = prout_register_key(sd2, 0, k2); + CU_ASSERT_EQUAL(ret, 0); + + CU_ASSERT_EQUAL(rk->num_keys, 1); + /* ensure preempt bumped generation number */ + CU_ASSERT_EQUAL(rk->prgeneration, old_gen + 1); + /* ensure k2 is retained */ + CU_ASSERT_EQUAL(rk->keys[0], k2); + + /* unregister k2 */ + ret = prout_register_key(sd2, 0, k2); + CU_ASSERT_EQUAL(ret, 0); +}