Merge pull request #184 from ddiss/pr_report_caps_and_preempt_tests

Persistent Reservation REPORT CAPS and PREEMPT tests
This commit is contained in:
Ronnie Sahlberg
2015-10-07 07:41:16 -07:00
10 changed files with 412 additions and 33 deletions

View File

@@ -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;

View File

@@ -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:

View File

@@ -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 \

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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 },

View File

@@ -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);

View File

@@ -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)
{

View 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);
}

View 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);
}