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