Merge pull request #181 from ddiss/mp_reset_v2

Mp reset v2
This commit is contained in:
Ronnie Sahlberg
2015-09-19 14:12:14 -07:00
10 changed files with 393 additions and 4 deletions

View File

@@ -73,6 +73,7 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \
test_prout_reserve_simple.c \
test_prout_reserve_access.c \
test_prout_reserve_ownership.c \
test_prout_clear_simple.c \
test_read6_simple.c \
test_read6_beyond_eol.c \
test_read10_simple.c \
@@ -215,7 +216,9 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \
test_writeverify16_flags.c \
test_writeverify16_dpo.c \
test_writeverify16_residuals.c \
test_multipathio_simple.c
test_multipathio_simple.c \
test_multipathio_reset.c \
test_multipathio_compareandwrite.c
endif

View File

@@ -369,3 +369,19 @@ mpath_check_matching_ids(int num_sds,
ret = mpath_check_matching_ids_serial_vpd(num_sds, sds);
return ret;
}
int
mpath_count_iscsi(int num_sds,
struct scsi_device **sds)
{
int i;
int found = 0;
for (i = 0; i < num_sds; i++) {
if (sds[i]->iscsi_ctx != NULL) {
found++;
}
}
return found;
}

View File

@@ -24,6 +24,13 @@
extern int mp_num_sds;
extern struct scsi_device *mp_sds[MPATH_MAX_DEVS];
int
mpath_check_matching_ids(int num_sds,
struct scsi_device **sds);
int
mpath_count_iscsi(int num_sds,
struct scsi_device **sds);
#define MPATH_SKIP_IF_UNAVAILABLE(_sds, _num_sds) \
do { \
if (_num_sds <= 1) { \
@@ -35,8 +42,15 @@ do { \
} \
} while (0);
int
mpath_check_matching_ids(int num_sds,
struct scsi_device **sds);
#define MPATH_SKIP_UNLESS_ISCSI(_sds, _num_sds) \
do { \
if (mpath_count_iscsi(_num_sds, _sds) != _num_sds) { \
logging(LOG_NORMAL, "[SKIPPED] Non-iSCSI multipath." \
" Skipping test"); \
CU_PASS("[SKIPPED] Non-iSCSI multipath." \
" Skipping test"); \
return; \
} \
} while (0);
#endif /* _ISCSI_MULTIPATH_H_ */

View File

@@ -973,6 +973,55 @@ prout_release(struct scsi_device *sdev,
return ret;
}
int
prout_clear(struct scsi_device *sdev, unsigned long long key)
{
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/CLEAR to clear all registrations and any PR "
"reservation");
if (!data_loss) {
printf("--dataloss flag is not set in. Skipping PROUT\n");
return -1;
}
memset(&poc, 0, sizeof (poc));
poc.reservation_key = key;
task = scsi_cdb_persistent_reserve_out(
SCSI_PERSISTENT_RESERVE_CLEAR,
SCSI_PERSISTENT_RESERVE_SCOPE_LU,
0, &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)

View File

@@ -273,6 +273,7 @@ int prout_reserve(struct scsi_device *sdev,
unsigned long long key, enum scsi_persistent_out_type pr_type);
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 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);

View File

@@ -192,6 +192,12 @@ static CU_TestInfo tests_prout_reserve[] = {
CU_TEST_INFO_NULL
};
static CU_TestInfo tests_prout_clear[] = {
{ (char *)"Simple",
test_prout_clear_simple },
CU_TEST_INFO_NULL
};
static CU_TestInfo tests_prin_serviceaction_range[] = {
{ (char *)"Range", test_prin_serviceaction_range },
CU_TEST_INFO_NULL
@@ -445,6 +451,8 @@ static CU_TestInfo tests_writeverify16[] = {
static CU_TestInfo tests_multipathio[] = {
{ (char *)"Simple", test_multipathio_simple },
{ (char *)"Reset", test_multipathio_reset },
{ (char *)"CompareAndWrite", test_multipathio_compareandwrite },
CU_TEST_INFO_NULL
};
@@ -476,6 +484,7 @@ static libiscsi_suite_info scsi_suites[] = {
{ "PrinServiceactionRange", NON_PGR_FUNCS, tests_prin_serviceaction_range },
{ "ProutRegister", NON_PGR_FUNCS, tests_prout_register },
{ "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve },
{ "ProutClear", NON_PGR_FUNCS, tests_prout_clear },
{ "Read6", NON_PGR_FUNCS, tests_read6 },
{ "Read10", NON_PGR_FUNCS, tests_read10 },
{ "Read12", NON_PGR_FUNCS, tests_read12 },
@@ -561,6 +570,7 @@ static libiscsi_suite_info all_suites[] = {
tests_prin_serviceaction_range },
{ "ProutRegister", NON_PGR_FUNCS, tests_prout_register },
{ "ProutReserve", NON_PGR_FUNCS, tests_prout_reserve },
{ "ProutClear", NON_PGR_FUNCS, tests_prout_clear },
{ "Read6", NON_PGR_FUNCS, tests_read6 },
{ "Read10", NON_PGR_FUNCS, tests_read10 },
{ "Read12", NON_PGR_FUNCS, tests_read12 },

View File

@@ -129,6 +129,7 @@ void test_prout_reserve_ownership_earo(void);
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_read6_simple(void);
void test_read6_beyond_eol(void);
@@ -301,5 +302,7 @@ void test_writeverify16_dpo(void);
void test_writeverify16_residuals(void);
void test_multipathio_simple(void);
void test_multipathio_reset(void);
void test_multipathio_compareandwrite(void);
#endif /* _ISCSI_TEST_CU_H_ */

View File

@@ -0,0 +1,104 @@
/*
Copyright (C) 2013 Ronnie Sahlberg <ronniesahlberg@gmail.com>
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 <inttypes.h>
#include <stdlib.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_multipathio_compareandwrite(void)
{
int io_bl = 1; /* 1 block CAW IOs */
int path;
int i, ret;
unsigned char *buf = alloca(2 * io_bl * block_size);
int maxbl;
CHECK_FOR_DATALOSS;
CHECK_FOR_SBC;
MPATH_SKIP_IF_UNAVAILABLE(mp_sds, mp_num_sds);
if (inq_bl) {
maxbl = inq_bl->max_cmp;
} else {
/* Assume we are not limited */
maxbl = 256;
}
if (maxbl < io_bl) {
CU_PASS("[SKIPPED] MAXIMUM_COMPARE_AND_WRITE_LENGTH too small");
return;
}
logging(LOG_VERBOSE, LOG_BLANK_LINE);
logging(LOG_VERBOSE, "Initialising data prior to COMPARE_AND_WRITE");
memset(buf, 0, io_bl * block_size);
ret = writesame10(mp_sds[0], 0,
block_size, 256, 0, 0, 0, 0, buf,
EXPECT_STATUS_GOOD);
if (ret == -2) {
CU_PASS("[SKIPPED] Target does not support WRITESAME10. Skipping test");
return;
}
CU_ASSERT_EQUAL(ret, 0);
logging(LOG_VERBOSE, "Test multipath COMPARE_AND_WRITE");
for (i = 0; i < 256; i++) {
for (path = 0; path < mp_num_sds; path++) {
logging(LOG_VERBOSE,
"Test COMPARE_AND_WRITE(%d->%d) using path %d",
path, path + 1, path);
/* compare data is first half */
memset(buf, path, io_bl * block_size);
/* write data is the second half, wrap around */
memset(buf + io_bl * block_size, path + 1,
io_bl * block_size);
ret = compareandwrite(mp_sds[path], i,
buf, 2 * io_bl * block_size,
block_size, 0, 0, 0, 0,
EXPECT_STATUS_GOOD);
if (ret == -2) {
CU_PASS("[SKIPPED] Target does not support "
"COMPARE_AND_WRITE. Skipping test");
return;
}
CU_ASSERT_EQUAL(ret, 0);
logging(LOG_VERBOSE,
"Test bad COMPARE_AND_WRITE(%d->%d)",
path, path + 1);
ret = compareandwrite(mp_sds[path], i,
buf, 2 * io_bl * block_size,
block_size, 0, 0, 0, 0,
EXPECT_MISCOMPARE);
CU_ASSERT_EQUAL(ret, 0);
}
}
}

View File

@@ -0,0 +1,102 @@
/*
Copyright (C) 2013 Ronnie Sahlberg <ronniesahlberg@gmail.com>
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 <stdlib.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"
#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)
{
int reset_path;
CHECK_FOR_DATALOSS;
CHECK_FOR_SBC;
MPATH_SKIP_IF_UNAVAILABLE(mp_sds, mp_num_sds);
MPATH_SKIP_UNLESS_ISCSI(mp_sds, mp_num_sds);
logging(LOG_VERBOSE, LOG_BLANK_LINE);
for (reset_path = 0; reset_path < mp_num_sds; reset_path++) {
int num_uas;
int ret;
int tur_path;
struct scsi_device *reset_sd = mp_sds[reset_path];
logging(LOG_VERBOSE, "Awaiting good TUR");
ret = test_iscsi_tur_until_good(reset_sd, &num_uas);
CU_ASSERT_EQUAL(ret, 0);
logging(LOG_VERBOSE,
"Test multipath LUN Reset using path %d", reset_path);
ret = iscsi_task_mgmt_lun_reset_sync(reset_sd->iscsi_ctx,
reset_sd->iscsi_lun);
if (ret != 0) {
logging(LOG_NORMAL, "LUN reset failed. %s",
iscsi_get_error(reset_sd->iscsi_ctx));
}
CU_ASSERT_EQUAL(ret, 0);
/* check for and clear LU reset UA on all paths */
for (tur_path = 0; tur_path < mp_num_sds; tur_path++) {
logging(LOG_VERBOSE, "check for LU reset unit "
"attention via TUR on path %d", tur_path);
ret = test_iscsi_tur_until_good(mp_sds[tur_path], &num_uas);
CU_ASSERT_EQUAL(ret, 0);
CU_ASSERT_NOT_EQUAL(num_uas, 0);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
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"
void
test_prout_clear_simple(void)
{
int ret = 0;
uint32_t old_gen;
const unsigned long long key = rand_key();
struct scsi_task *tsk;
struct scsi_persistent_reserve_in_read_keys *rk;
CHECK_FOR_DATALOSS;
logging(LOG_VERBOSE, LOG_BLANK_LINE);
logging(LOG_VERBOSE, "Test Persistent Reserve OUT CLEAR 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_read_keys(sd, &tsk, &rk);
CU_ASSERT_EQUAL(ret, 0);
CU_ASSERT_NOT_EQUAL(rk->num_keys, 0);
/* retain PR generation number to check for increments */
old_gen = rk->prgeneration;
scsi_free_scsi_task(tsk);
rk = NULL; /* freed with tsk */
/* reserve the target */
ret = prout_reserve(sd, key,
SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS);
CU_ASSERT_EQUAL(ret, 0);
/* verify target reservation */
ret = prin_verify_reserved_as(sd, key,
SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS);
CU_ASSERT_EQUAL(ret, 0);
/* clear reservation and registration */
ret = prout_clear(sd, key);
CU_ASSERT_EQUAL(ret, 0);
ret = prin_verify_not_reserved(sd);
CU_ASSERT_EQUAL(ret, 0);
ret = prin_read_keys(sd, &tsk, &rk);
CU_ASSERT_EQUAL(ret, 0);
CU_ASSERT_EQUAL(rk->num_keys, 0);
/* generation incremented once for CLEAR (not for RESERVE) */
CU_ASSERT_EQUAL(rk->prgeneration, old_gen + 1);
scsi_free_scsi_task(tsk);
rk = NULL; /* freed with tsk */
}