@@ -130,9 +130,22 @@ struct scsi_persistent_reserve_out_basic {
|
||||
};
|
||||
|
||||
enum scsi_maintenance_in {
|
||||
SCSI_REPORT_TARGET_PORT_GROUPS = 0x0a,
|
||||
SCSI_REPORT_SUPPORTED_OP_CODES = 0x0c
|
||||
};
|
||||
|
||||
enum scsi_alue_state {
|
||||
SCSI_ALUA_ACTIVE_OPTIMIZED = 0x0,
|
||||
SCSI_ALUA_ACTIVE_NONOPTIMIZED = 0x1,
|
||||
SCSI_ALUA_STANDBY = 0x2,
|
||||
SCSI_ALUA_UNAVAILABLE = 0x3,
|
||||
SCSI_ALUA_LOGICAL_BLOCK_DEPENDENT = 0x4,
|
||||
SCSI_ALUA_OFFLINE = 0xe,
|
||||
SCSI_ALUA_TRANSITIONING = 0xf
|
||||
};
|
||||
|
||||
const char *scsi_alua_state_to_str(uint8_t state);
|
||||
|
||||
enum scsi_op_code_reporting_options {
|
||||
SCSI_REPORT_SUPPORTING_OPS_ALL = 0x00,
|
||||
SCSI_REPORT_SUPPORTING_OPCODE = 0x01,
|
||||
@@ -969,6 +982,32 @@ struct scsi_report_supported_op_codes_one_command {
|
||||
struct scsi_op_timeout_descriptor to;
|
||||
};
|
||||
|
||||
struct scsi_target_port_group {
|
||||
union {
|
||||
struct {
|
||||
uint8_t pref:1;
|
||||
uint8_t rtpg_fmt:3;
|
||||
uint8_t alua_state:4;
|
||||
};
|
||||
uint8_t byte0;
|
||||
};
|
||||
uint8_t flags;
|
||||
uint16_t port_group;
|
||||
uint8_t status_code;
|
||||
uint8_t vendor_specific;
|
||||
uint8_t port_count;
|
||||
/* retrieved_port_count may be less than port_count when RTPG output
|
||||
* was trimmed due to the buffer size */
|
||||
uint8_t retrieved_port_count;
|
||||
/* points to 'retrieved_port_count' relative port ids */
|
||||
uint16_t *ports;
|
||||
};
|
||||
|
||||
struct scsi_report_target_port_groups {
|
||||
int num_groups;
|
||||
struct scsi_target_port_group groups[0];
|
||||
};
|
||||
|
||||
struct scsi_persistent_reserve_in_read_keys {
|
||||
uint32_t prgeneration;
|
||||
uint32_t additional_length;
|
||||
@@ -1162,6 +1201,7 @@ EXTERN struct scsi_task *scsi_cdb_read16(uint64_t lba, uint32_t xferlen, int blo
|
||||
EXTERN struct scsi_task *scsi_cdb_readcapacity16(void);
|
||||
EXTERN struct scsi_task *scsi_cdb_readdefectdata10(int req_plist, int req_glist, int defect_list_format, uint16_t alloc_len);
|
||||
EXTERN struct scsi_task *scsi_cdb_readdefectdata12(int req_plist, int req_glist, int defect_list_format, uint32_t address_descriptor_index, uint32_t alloc_len);
|
||||
EXTERN struct scsi_task *scsi_cdb_report_target_port_groups(uint32_t alloc_len);
|
||||
EXTERN struct scsi_task *scsi_cdb_report_supported_opcodes(int rctd, int options, enum scsi_opcode opcode, int sa, uint32_t alloc_len);
|
||||
EXTERN struct scsi_task *scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen);
|
||||
EXTERN struct scsi_task *scsi_cdb_startstopunit(int immed, int pcm, int pc, int no_flush, int loej, int start);
|
||||
|
||||
@@ -210,6 +210,7 @@ iscsi_writeverify16_iov_sync
|
||||
iscsi_writeverify16_iov_task
|
||||
iscsi_writeverify16_sync
|
||||
iscsi_writeverify16_task
|
||||
scsi_alua_state_to_str
|
||||
scsi_association_to_str
|
||||
scsi_cdb_compareandwrite
|
||||
scsi_cdb_extended_copy
|
||||
@@ -237,6 +238,7 @@ scsi_cdb_readtoc
|
||||
scsi_cdb_receive_copy_results
|
||||
scsi_cdb_release6
|
||||
scsi_cdb_report_supported_opcodes
|
||||
scsi_cdb_report_target_port_groups
|
||||
scsi_cdb_reserve6
|
||||
scsi_cdb_sanitize
|
||||
scsi_cdb_serviceactionin16
|
||||
|
||||
@@ -1172,11 +1172,87 @@ scsi_maintenancein_datain_getfullsize(struct scsi_task *task)
|
||||
task_get_uint16(task, 2);
|
||||
}
|
||||
return -1;
|
||||
case SCSI_REPORT_TARGET_PORT_GROUPS:
|
||||
return task_get_uint32(task, 0) + 4;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct scsi_report_target_port_groups *
|
||||
scsi_report_target_port_groups_unmarshal(struct scsi_task *task)
|
||||
{
|
||||
int const group_descriptor_size = 8;
|
||||
int const port_descriptor_size = 4;
|
||||
|
||||
struct scsi_report_target_port_groups *rtpg = NULL;
|
||||
uint16_t *port = NULL;
|
||||
int group_count, port_count, i, j, k;
|
||||
|
||||
if (task->datain.size < 4) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port_count= 0;
|
||||
group_count = 0;
|
||||
for (j = 0; j < 2; ++j) {
|
||||
/* 1st pass counts groups and ports, then allocates data structs to fit those;
|
||||
* 2nd pass populates the allocated data structs.*/
|
||||
for (i = 4; i< task->datain.size; ) {
|
||||
uint8_t current_port_count;
|
||||
|
||||
if (task->datain.size - i < group_descriptor_size) {
|
||||
break;
|
||||
}
|
||||
current_port_count = task_get_uint8(task, i + 7);
|
||||
|
||||
if (j == 1) {
|
||||
rtpg->groups[group_count].port_count = current_port_count;
|
||||
rtpg->groups[group_count].byte0 = task_get_uint8(task, i);
|
||||
rtpg->groups[group_count].flags = task_get_uint8(task, i + 1);
|
||||
rtpg->groups[group_count].port_group = task_get_uint16(task, i + 2);
|
||||
rtpg->groups[group_count].status_code = task_get_uint8(task, i + 5);
|
||||
rtpg->groups[group_count].ports = port;
|
||||
}
|
||||
|
||||
i += group_descriptor_size;
|
||||
for (k = 0; k < current_port_count &&
|
||||
i + (k + 1) * port_descriptor_size <= task->datain.size; ++k) {
|
||||
if (j == 1) {
|
||||
rtpg->groups[group_count].ports[k] =
|
||||
task_get_uint16(task, i + k * port_descriptor_size + 2);
|
||||
}
|
||||
}
|
||||
if (j == 1) {
|
||||
rtpg->groups[group_count].retrieved_port_count = k;
|
||||
port += k;
|
||||
}
|
||||
++group_count;
|
||||
port_count += k;
|
||||
i += k * port_descriptor_size;
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
rtpg = scsi_malloc(
|
||||
task,
|
||||
sizeof(struct scsi_report_target_port_groups) +
|
||||
sizeof(struct scsi_target_port_group) * group_count +
|
||||
sizeof(uint16_t) * port_count);
|
||||
if (rtpg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
port = (uint16_t *)((uint8_t *)rtpg +
|
||||
sizeof(struct scsi_report_target_port_groups) +
|
||||
sizeof(struct scsi_target_port_group) * group_count);
|
||||
rtpg->num_groups = group_count;
|
||||
group_count = 0;
|
||||
port_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rtpg;
|
||||
}
|
||||
|
||||
/*
|
||||
* maintenance_in unmarshall
|
||||
*/
|
||||
@@ -1283,11 +1359,43 @@ scsi_maintenancein_datain_unmarshall(struct scsi_task *task)
|
||||
}
|
||||
return rsoc_one;
|
||||
}
|
||||
case SCSI_REPORT_TARGET_PORT_GROUPS:
|
||||
return scsi_report_target_port_groups_unmarshal(task);
|
||||
};
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* MAINTENANCE In / Report Target Port Groups
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_report_target_port_groups(uint32_t alloc_len)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(task, 0, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_MAINTENANCE_IN;
|
||||
task->cdb[1] = SCSI_REPORT_TARGET_PORT_GROUPS;
|
||||
|
||||
scsi_set_uint32(&task->cdb[6], alloc_len);
|
||||
|
||||
task->cdb_size = 12;
|
||||
if (alloc_len != 0) {
|
||||
task->xfer_dir = SCSI_XFER_READ;
|
||||
} else {
|
||||
task->xfer_dir = SCSI_XFER_NONE;
|
||||
}
|
||||
task->expxferlen = alloc_len;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* MAINTENANCE In / Read Supported Op Codes
|
||||
*/
|
||||
@@ -4287,6 +4395,29 @@ scsi_designator_type_to_str(int type)
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_alua_state_to_str(uint8_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case SCSI_ALUA_ACTIVE_OPTIMIZED:
|
||||
return "ACTIVE-OPTIMIZED";
|
||||
case SCSI_ALUA_ACTIVE_NONOPTIMIZED:
|
||||
return "ACTIVE-NONOPTIMIZED";
|
||||
case SCSI_ALUA_STANDBY:
|
||||
return "STANDBY";
|
||||
case SCSI_ALUA_UNAVAILABLE:
|
||||
return "UNAVAILABLE";
|
||||
case SCSI_ALUA_LOGICAL_BLOCK_DEPENDENT:
|
||||
return "BLOCK-DEPENDENT";
|
||||
case SCSI_ALUA_OFFLINE:
|
||||
return "OFFLINE";
|
||||
case SCSI_ALUA_TRANSITIONING:
|
||||
return "TRANSITIONING";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void
|
||||
scsi_set_task_private_ptr(struct scsi_task *task, void *ptr)
|
||||
{
|
||||
|
||||
@@ -125,6 +125,8 @@ iscsi_test_cu_SOURCES = iscsi-test-cu.c \
|
||||
test_reserve6_target_warm_reset.c \
|
||||
test_reserve6_target_cold_reset.c \
|
||||
test_reserve6_lun_reset.c \
|
||||
test_rtpg_alloc_length.c \
|
||||
test_rtpg_simple.c \
|
||||
test_sanitize_block_erase.c \
|
||||
test_sanitize_block_erase_reserved.c \
|
||||
test_sanitize_crypto_erase.c \
|
||||
|
||||
@@ -2711,6 +2711,29 @@ inquiry(struct scsi_device *sdev, struct scsi_task **out_task, int evpd, int pag
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtpg(struct scsi_device *sdev, struct scsi_task **out_task, int maxsize, int status,
|
||||
enum scsi_sense_key key, int *ascq, int num_ascq)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
logging(LOG_VERBOSE, "Send RTPG (expecting %s) alloc_len %d",
|
||||
scsi_status_str(status), maxsize);
|
||||
|
||||
task = scsi_cdb_report_target_port_groups(maxsize);
|
||||
assert (task != NULL);
|
||||
|
||||
task = send_scsi_command(sdev, task, NULL);
|
||||
|
||||
ret = check_result("RTPG", sdev, task, status, key, ascq, num_ascq);
|
||||
if (out_task) {
|
||||
*out_task = task;
|
||||
} else if (task) {
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct scsi_command_descriptor *
|
||||
get_command_descriptor(int opcode, int sa)
|
||||
{
|
||||
|
||||
@@ -890,6 +890,7 @@ int report_supported_opcodes(struct scsi_device *sdev, struct scsi_task **save_t
|
||||
int release6(struct scsi_device *sdev);
|
||||
int reserve6(struct scsi_device *sdev);
|
||||
int reserve6_conflict(struct scsi_device *sdev);
|
||||
int rtpg(struct scsi_device *sdev, struct scsi_task **out_task, int maxsize, int status, enum scsi_sense_key key, int *ascq, int num_ascq);
|
||||
int sanitize(struct scsi_device *sdev, int immed, int ause, int sa, int param_len, struct iscsi_data *data, int status, enum scsi_sense_key key, int *ascq, int num_ascq);
|
||||
int startstopunit(struct scsi_device *sdev, int immed, int pcm, int pc, int no_flush, int loej, int start, int status, enum scsi_sense_key key, int *ascq, int num_ascq);
|
||||
int synchronizecache10(struct scsi_device *sdev, uint32_t lba, int num_blocks, int sync_nv, int immed, int status, enum scsi_sense_key key, int *ascq, int num_ascq);
|
||||
|
||||
@@ -93,6 +93,12 @@ static CU_TestInfo tests_inquiry[] = {
|
||||
CU_TEST_INFO_NULL
|
||||
};
|
||||
|
||||
static CU_TestInfo test_rtpg[] = {
|
||||
{ "Simple", test_rtpg_simple },
|
||||
{ "AllocLength", test_rtpg_alloc_length },
|
||||
CU_TEST_INFO_NULL
|
||||
};
|
||||
|
||||
static CU_TestInfo tests_mandatory[] = {
|
||||
{ "MandatorySBC", test_mandatory_sbc },
|
||||
CU_TEST_INFO_NULL
|
||||
@@ -507,6 +513,7 @@ static libiscsi_suite_info scsi_suites[] = {
|
||||
{ "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy },
|
||||
{ "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status },
|
||||
{ "Inquiry", NON_PGR_FUNCS, tests_inquiry },
|
||||
{ "ReportTargetPortGroups", NON_PGR_FUNCS, test_rtpg },
|
||||
{ "Mandatory", NON_PGR_FUNCS, tests_mandatory },
|
||||
{ "ModeSense6", NON_PGR_FUNCS, tests_modesense6 },
|
||||
{ "NoMedia", NON_PGR_FUNCS, tests_nomedia },
|
||||
@@ -629,6 +636,7 @@ static libiscsi_suite_info all_suites[] = {
|
||||
{ "ExtendedCopy", NON_PGR_FUNCS, tests_extended_copy },
|
||||
{ "GetLBAStatus", NON_PGR_FUNCS, tests_get_lba_status },
|
||||
{ "Inquiry", NON_PGR_FUNCS, tests_inquiry },
|
||||
{ "ReportTargetPortGroups", NON_PGR_FUNCS, test_rtpg },
|
||||
{ "Mandatory", NON_PGR_FUNCS, tests_mandatory },
|
||||
{ "ModeSense6", NON_PGR_FUNCS, tests_modesense6 },
|
||||
{ "NoMedia", NON_PGR_FUNCS, tests_nomedia },
|
||||
|
||||
@@ -204,6 +204,8 @@ void test_reserve6_itnexus_loss(void);
|
||||
void test_reserve6_target_cold_reset(void);
|
||||
void test_reserve6_target_warm_reset(void);
|
||||
void test_reserve6_lun_reset(void);
|
||||
void test_rtpg_alloc_length(void);
|
||||
void test_rtpg_simple(void);
|
||||
|
||||
void test_sanitize_block_erase(void);
|
||||
void test_sanitize_block_erase_reserved(void);
|
||||
|
||||
145
test-tool/test_rtpg_alloc_length.c
Normal file
145
test-tool/test_rtpg_alloc_length.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
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 <CUnit/CUnit.h>
|
||||
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
#include "iscsi-support.h"
|
||||
#include "iscsi-test-cu.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
test_rtpg_alloc_length(void)
|
||||
{
|
||||
int ret, full_size, size, group;
|
||||
struct scsi_inquiry_standard *std_inq;
|
||||
struct scsi_report_target_port_groups *report;
|
||||
|
||||
logging(LOG_VERBOSE, LOG_BLANK_LINE);
|
||||
logging(LOG_VERBOSE, "Test of the RTPG command with insufficient buffers");
|
||||
|
||||
logging(LOG_VERBOSE, "Checking if the target supports RTPG");
|
||||
ret = inquiry(sd, &task, 0, 0, 260, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
std_inq = scsi_datain_unmarshall(task);
|
||||
CU_ASSERT_NOT_EQUAL(std_inq, NULL);
|
||||
if (std_inq->tpgs == 0) {
|
||||
logging(LOG_VERBOSE, "The target does not support RTPG. Skipping RTPG tests.");
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
return;
|
||||
}
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
|
||||
logging(LOG_VERBOSE, "Retrieving 4 bytes of RTPG data");
|
||||
ret = rtpg(sd, &task, 4, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
logging(LOG_VERBOSE, "Retrieving all RTPG data (%d bytes)", full_size);
|
||||
ret = rtpg(sd, &task, full_size, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
report = scsi_datain_unmarshall(task);
|
||||
CU_ASSERT_NOT_EQUAL(report, NULL);
|
||||
/* data size stays the same */
|
||||
CU_ASSERT_EQUAL(full_size, scsi_datain_getfullsize(task));
|
||||
|
||||
/* The test assumes that groups are reported in the same order for any buffer size. */
|
||||
size = 4; /* offset of the 1st target port group descriptor */
|
||||
for (group = 0; group < report->num_groups; ++group) {
|
||||
const int group_descriptor_size = 8;
|
||||
const int port_descriptor_size = 4;
|
||||
struct scsi_report_target_port_groups *report_partial = NULL;
|
||||
struct scsi_task *task_partial = NULL;
|
||||
int i;
|
||||
|
||||
logging(LOG_VERBOSE, "Buffer boundary cuts descriptor of group %d in half", group);
|
||||
size += group_descriptor_size / 2;
|
||||
ret = rtpg(sd, &task_partial, size, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
report_partial = scsi_datain_unmarshall(task_partial);
|
||||
CU_ASSERT_NOT_EQUAL(report_partial, NULL);
|
||||
/* cut group not unmarshalled */
|
||||
CU_ASSERT_EQUAL(group, report_partial->num_groups);
|
||||
/* previous groups unmarshalled along with their ports */
|
||||
for (i = 0; i < group; ++i) {
|
||||
CU_ASSERT_EQUAL(report_partial->groups[i].retrieved_port_count,
|
||||
report_partial->groups[i].port_count);
|
||||
CU_ASSERT_EQUAL(report_partial->groups[i].retrieved_port_count,
|
||||
report->groups[i].retrieved_port_count);
|
||||
}
|
||||
scsi_free_scsi_task(task_partial);
|
||||
task_partial = NULL;
|
||||
|
||||
logging(LOG_VERBOSE, "Buffer boundary at the end of descriptor of group %d", group);
|
||||
size += group_descriptor_size / 2;
|
||||
ret = rtpg(sd, &task_partial, size, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
report_partial = scsi_datain_unmarshall(task_partial);
|
||||
CU_ASSERT_NOT_EQUAL(report_partial, NULL);
|
||||
/* group unmarshalled */
|
||||
CU_ASSERT_EQUAL(group + 1, report_partial->num_groups);
|
||||
/* previous groups unmarshalled along with their ports */
|
||||
for (i = 0; i < group; ++i) {
|
||||
CU_ASSERT_EQUAL(report_partial->groups[i].retrieved_port_count,
|
||||
report_partial->groups[i].port_count);
|
||||
CU_ASSERT_EQUAL(report_partial->groups[i].retrieved_port_count,
|
||||
report->groups[i].retrieved_port_count);
|
||||
}
|
||||
/* no retrieved ports for the current group */
|
||||
CU_ASSERT_EQUAL(report_partial->groups[group].retrieved_port_count, 0);
|
||||
CU_ASSERT_EQUAL(report_partial->groups[group].port_count,
|
||||
report->groups[group].port_count);
|
||||
scsi_free_scsi_task(task_partial);
|
||||
task_partial = NULL;
|
||||
|
||||
if (report->groups[group].port_count == 0) {
|
||||
continue;
|
||||
}
|
||||
size += port_descriptor_size;
|
||||
if (report->groups[group].port_count > 1) {
|
||||
logging(LOG_VERBOSE, "Just one port of group %d fits the buffer", group);
|
||||
ret = rtpg(sd, &task_partial, size, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
report_partial = scsi_datain_unmarshall(task_partial);
|
||||
CU_ASSERT_NOT_EQUAL(report_partial, NULL);
|
||||
/* group unmarshalled */
|
||||
CU_ASSERT_EQUAL(group + 1, report_partial->num_groups);
|
||||
/* previous groups unmarshalled along with their ports */
|
||||
for (i = 0; i < group; ++i) {
|
||||
CU_ASSERT_EQUAL(report_partial->groups[i].retrieved_port_count,
|
||||
report_partial->groups[i].port_count);
|
||||
CU_ASSERT_EQUAL(report_partial->groups[i].retrieved_port_count,
|
||||
report->groups[i].retrieved_port_count);
|
||||
}
|
||||
/* 1 retrieved port for the current group */
|
||||
CU_ASSERT_EQUAL(report_partial->groups[group].retrieved_port_count, 1);
|
||||
CU_ASSERT_EQUAL(report_partial->groups[group].port_count,
|
||||
report->groups[group].port_count);
|
||||
scsi_free_scsi_task(task_partial);
|
||||
task_partial = NULL;
|
||||
size += port_descriptor_size * (report->groups[group].port_count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
};
|
||||
87
test-tool/test_rtpg_simple.c
Normal file
87
test-tool/test_rtpg_simple.c
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 <CUnit/CUnit.h>
|
||||
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
#include "iscsi-support.h"
|
||||
#include "iscsi-test-cu.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
test_rtpg_simple(void)
|
||||
{
|
||||
int ret, full_size, i, io_ready_groups;
|
||||
struct scsi_inquiry_standard *std_inq;
|
||||
struct scsi_report_target_port_groups *report;
|
||||
|
||||
logging(LOG_VERBOSE, LOG_BLANK_LINE);
|
||||
logging(LOG_VERBOSE, "Test of the RTPG command");
|
||||
|
||||
logging(LOG_VERBOSE, "Checking if the target supports RTPG");
|
||||
ret = inquiry(sd, &task, 0, 0, 260, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
std_inq = scsi_datain_unmarshall(task);
|
||||
CU_ASSERT_NOT_EQUAL(std_inq, NULL);
|
||||
if (std_inq->tpgs == 0) {
|
||||
logging(LOG_VERBOSE, "The target does not support RTPG. Skipping RTPG tests.");
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
return;
|
||||
}
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
|
||||
logging(LOG_VERBOSE, "Retrieving 4 bytes of RTPG data");
|
||||
ret = rtpg(sd, &task, 4, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
logging(LOG_VERBOSE, "Retrieving all RTPG data (%d bytes)", full_size);
|
||||
ret = rtpg(sd, &task, full_size, EXPECT_STATUS_GOOD);
|
||||
CU_ASSERT_EQUAL(ret, 0);
|
||||
report = scsi_datain_unmarshall(task);
|
||||
CU_ASSERT_NOT_EQUAL(report, NULL);
|
||||
/* data size stays the same */
|
||||
CU_ASSERT_EQUAL(full_size, scsi_datain_getfullsize(task));
|
||||
|
||||
logging(LOG_VERBOSE, "Validating %d target port groups", report->num_groups);
|
||||
io_ready_groups = 0;
|
||||
for (i = 0; i < report->num_groups; ++i) {
|
||||
/* Valid ALUA state */
|
||||
CU_ASSERT(report->groups[i].alua_state == SCSI_ALUA_ACTIVE_OPTIMIZED ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_ACTIVE_NONOPTIMIZED ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_STANDBY ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_UNAVAILABLE ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_LOGICAL_BLOCK_DEPENDENT ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_OFFLINE ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_TRANSITIONING);
|
||||
if (report->groups[i].alua_state == SCSI_ALUA_ACTIVE_OPTIMIZED ||
|
||||
report->groups[i].alua_state == SCSI_ALUA_ACTIVE_NONOPTIMIZED) {
|
||||
++io_ready_groups;
|
||||
}
|
||||
/* Since we retrieved full size, we get all port ids */
|
||||
CU_ASSERT_EQUAL(report->groups[i].port_count, report->groups[i].retrieved_port_count);
|
||||
}
|
||||
CU_ASSERT(io_ready_groups > 0);
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
task = NULL;
|
||||
};
|
||||
@@ -3,7 +3,7 @@ AM_CFLAGS = $(WARN_CFLAGS)
|
||||
AM_LDFLAGS = -no-undefined
|
||||
LIBS = ../lib/libiscsi.la
|
||||
|
||||
bin_PROGRAMS = iscsi-inq iscsi-ls iscsi-swp iscsi-pr iscsi-discard iscsi-md5sum
|
||||
bin_PROGRAMS = iscsi-inq iscsi-ls iscsi-swp iscsi-pr iscsi-discard iscsi-md5sum iscsi-rtpg
|
||||
if !TARGET_OS_IS_WIN32
|
||||
bin_PROGRAMS += iscsi-perf iscsi-readcapacity16
|
||||
endif
|
||||
|
||||
222
utils/iscsi-rtpg.c
Normal file
222
utils/iscsi-rtpg.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
void report_tpg(const struct scsi_report_target_port_groups *rtpg)
|
||||
{
|
||||
int group, port;
|
||||
|
||||
printf("RTPG retrieved %d groups\n", rtpg->num_groups);
|
||||
for(group = 0; group < rtpg->num_groups; ++group) {
|
||||
printf("Group 0x%04x: preferred %d, format 0x%02x, ALUA state %s,"
|
||||
"flags 0x%02x, status code 0x%02x, port count %d\n",
|
||||
rtpg->groups[group].port_group,
|
||||
rtpg->groups[group].pref,
|
||||
rtpg->groups[group].rtpg_fmt,
|
||||
scsi_alua_state_to_str(rtpg->groups[group].alua_state),
|
||||
rtpg->groups[group].flags,
|
||||
rtpg->groups[group].status_code,
|
||||
rtpg->groups[group].port_count);
|
||||
printf("Ports: [");
|
||||
for (port = 0; port < rtpg->groups[group].retrieved_port_count; ++port) {
|
||||
printf(" 0x%x", rtpg->groups[group].ports[port]);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void do_rtpg(struct iscsi_context *iscsi, int lun)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int alloc_size = 512, retries;
|
||||
struct scsi_report_target_port_groups *rtpg;
|
||||
|
||||
for (retries = 0; retries < 2; ++retries) {
|
||||
int full_size;
|
||||
|
||||
task = scsi_cdb_report_target_port_groups(alloc_size);
|
||||
if (task == NULL) {
|
||||
fprintf(stderr, "Failed to allocate CBD for RTPG (size %d)\n", alloc_size);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
task = iscsi_scsi_command_sync(iscsi, lun, task, NULL);
|
||||
if (task == NULL) {
|
||||
fprintf(stderr, "RTPG command failed\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
if (full_size > alloc_size) {
|
||||
alloc_size = full_size;
|
||||
scsi_free_scsi_task(task);
|
||||
continue;
|
||||
}
|
||||
|
||||
rtpg = scsi_datain_unmarshall(task);
|
||||
if (rtpg == NULL) {
|
||||
fprintf(stderr, "Failed to unmarshal RTPG data blob\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
report_tpg(rtpg);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Gave up after 2 RTPG attempts: the report did not fit in %d bytes\n",
|
||||
alloc_size);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: iscsi-rtpg [-?|--help] [--usage] "
|
||||
"[-i|--initiator-name=iqn-name] <iscsi-url>\n");
|
||||
}
|
||||
|
||||
void print_help(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: iscsi-rtpg [OPTION...] <iscsi-url>\n");
|
||||
fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n");
|
||||
fprintf(stderr, " -d, --debug=integer debug level (0=disabled)\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Help options:\n");
|
||||
fprintf(stderr, " -?, --help Show this help message\n");
|
||||
fprintf(stderr, " --usage Display brief usage message\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "iSCSI URL format : %s\n", ISCSI_URL_SYNTAX);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "<host> is either of:\n");
|
||||
fprintf(stderr, " \"hostname\" iscsi.example\n");
|
||||
fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n");
|
||||
fprintf(stderr, " \"ipv6-address\" [fce0::1]\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct iscsi_context *iscsi;
|
||||
char *url = NULL;
|
||||
struct iscsi_url *iscsi_url = NULL;
|
||||
const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-inq";
|
||||
int show_help = 0, show_usage = 0, debug = 0;
|
||||
int c;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"usage", no_argument, NULL, 'u'},
|
||||
{"debug", required_argument, NULL, 'd'},
|
||||
{"initiator-name", required_argument, NULL, 'i'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int option_index;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h?ud:i:", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
show_help = 1;
|
||||
break;
|
||||
case 'u':
|
||||
show_usage = 1;
|
||||
break;
|
||||
case 'd':
|
||||
debug = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'i':
|
||||
initiator = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unrecognized option '%c'\n\n", c);
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (show_help != 0) {
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (show_usage != 0) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
iscsi = iscsi_create_context(initiator);
|
||||
if (iscsi == NULL) {
|
||||
fprintf(stderr, "Failed to create context\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (debug > 0) {
|
||||
iscsi_set_log_level(iscsi, debug);
|
||||
iscsi_set_log_fn(iscsi, iscsi_log_to_stderr);
|
||||
}
|
||||
|
||||
if (argv[optind] != NULL) {
|
||||
url = strdup(argv[optind]);
|
||||
}
|
||||
if (url == NULL) {
|
||||
fprintf(stderr, "You must specify the URL\n");
|
||||
print_usage();
|
||||
exit(10);
|
||||
}
|
||||
iscsi_url = iscsi_parse_full_url(iscsi, url);
|
||||
|
||||
free(url);
|
||||
|
||||
if (iscsi_url == NULL) {
|
||||
fprintf(stderr, "Failed to parse URL: %s\n",
|
||||
iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
|
||||
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
|
||||
fprintf(stderr, "Login Failed. %s\n", iscsi_get_error(iscsi));
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
iscsi_destroy_context(iscsi);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
do_rtpg(iscsi, iscsi_url->lun);
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
|
||||
iscsi_logout_sync(iscsi);
|
||||
iscsi_destroy_context(iscsi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user