@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
104
test-tool/test_multipathio_compareandwrite.c
Normal file
104
test-tool/test_multipathio_compareandwrite.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
102
test-tool/test_multipathio_reset.c
Normal file
102
test-tool/test_multipathio_reset.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
87
test-tool/test_prout_clear_simple.c
Normal file
87
test-tool/test_prout_clear_simple.c
Normal 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 */
|
||||
}
|
||||
Reference in New Issue
Block a user