Merge pull request #184 from ddiss/pr_report_caps_and_preempt_tests
Persistent Reservation REPORT CAPS and PREEMPT tests
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -69,11 +69,13 @@ 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 \
|
||||
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 \
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <fnmatch.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef HAVE_SG_IO
|
||||
#include <fcntl.h>
|
||||
@@ -1022,6 +1023,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)
|
||||
@@ -1155,6 +1208,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)
|
||||
{
|
||||
@@ -2845,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;
|
||||
}
|
||||
|
||||
@@ -274,9 +274,14 @@ 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);
|
||||
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);
|
||||
@@ -331,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_ */
|
||||
|
||||
@@ -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
|
||||
@@ -198,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
|
||||
@@ -482,9 +493,11 @@ 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 },
|
||||
{ "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 },
|
||||
@@ -568,9 +581,11 @@ 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 },
|
||||
{ "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 },
|
||||
|
||||
@@ -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);
|
||||
@@ -130,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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
114
test-tool/test_prin_report_caps.c
Normal file
114
test-tool/test_prin_report_caps.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
121
test-tool/test_prout_preempt.c
Normal file
121
test-tool/test_prout_preempt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user