diff --git a/include/scsi-lowlevel.h b/include/scsi-lowlevel.h
index a7144f1..adb1be1 100644
--- a/include/scsi-lowlevel.h
+++ b/include/scsi-lowlevel.h
@@ -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;
diff --git a/test-tool/Makefile.am b/test-tool/Makefile.am
index 8b2e2e0..cc12803 100644
--- a/test-tool/Makefile.am
+++ b/test-tool/Makefile.am
@@ -69,6 +69,7 @@ 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 \
diff --git a/test-tool/iscsi-test-cu.c b/test-tool/iscsi-test-cu.c
index 5646b74..826061d 100644
--- a/test-tool/iscsi-test-cu.c
+++ b/test-tool/iscsi-test-cu.c
@@ -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
@@ -482,6 +487,7 @@ 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 },
@@ -568,6 +574,7 @@ 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 },
diff --git a/test-tool/iscsi-test-cu.h b/test-tool/iscsi-test-cu.h
index fa9a0c0..e9330dc 100644
--- a/test-tool/iscsi-test-cu.h
+++ b/test-tool/iscsi-test-cu.h
@@ -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);
diff --git a/test-tool/test_prin_report_caps.c b/test-tool/test_prin_report_caps.c
new file mode 100644
index 0000000..688c3b2
--- /dev/null
+++ b/test-tool/test_prin_report_caps.c
@@ -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 .
+*/
+
+#include
+#include
+
+#include
+
+#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);
+}