TESTS: Add support to test against a /dev/sg device on linux
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
iscsi-test tool support
|
iscsi-test tool support
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@
|
|||||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
@@ -31,6 +34,13 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SG_IO
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <scsi/sg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "slist.h"
|
#include "slist.h"
|
||||||
#include "iscsi.h"
|
#include "iscsi.h"
|
||||||
#include "scsi-lowlevel.h"
|
#include "scsi-lowlevel.h"
|
||||||
@@ -170,16 +180,119 @@ static int check_result(const char *opcode, struct scsi_device *sdev,
|
|||||||
|
|
||||||
static struct scsi_task *send_scsi_command(struct scsi_device *sdev, struct scsi_task *task, struct iscsi_data *d)
|
static struct scsi_task *send_scsi_command(struct scsi_device *sdev, struct scsi_task *task, struct iscsi_data *d)
|
||||||
{
|
{
|
||||||
if (sdev->error_str != NULL) {
|
if (sdev->iscsi_url) {
|
||||||
free(discard_const(sdev->error_str));
|
if (sdev->error_str != NULL) {
|
||||||
sdev->error_str = NULL;
|
free(discard_const(sdev->error_str));
|
||||||
}
|
sdev->error_str = NULL;
|
||||||
task = iscsi_scsi_command_sync(sdev->iscsi_ctx, sdev->iscsi_lun, task, d);
|
}
|
||||||
if (task == NULL) {
|
task = iscsi_scsi_command_sync(sdev->iscsi_ctx, sdev->iscsi_lun, task, d);
|
||||||
sdev->error_str = strdup(iscsi_get_error(sdev->iscsi_ctx));
|
if (task == NULL) {
|
||||||
|
sdev->error_str = strdup(iscsi_get_error(sdev->iscsi_ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
return task;
|
#ifdef HAVE_SG_IO
|
||||||
|
if (sdev->sgio_dev) {
|
||||||
|
sg_io_hdr_t io_hdr;
|
||||||
|
unsigned int sense_len=32;
|
||||||
|
unsigned char sense[sense_len];
|
||||||
|
char buf[1024];
|
||||||
|
|
||||||
|
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
|
||||||
|
io_hdr.interface_id = 'S';
|
||||||
|
|
||||||
|
/* CDB */
|
||||||
|
io_hdr.cmdp = task->cdb;
|
||||||
|
io_hdr.cmd_len = task->cdb_size;
|
||||||
|
|
||||||
|
/* Where to store the sense_data, if there was an error */
|
||||||
|
io_hdr.sbp = sense;
|
||||||
|
io_hdr.mx_sb_len = sense_len;
|
||||||
|
sense_len=0;
|
||||||
|
|
||||||
|
/* Transfer direction, either in or out. Linux does not yet
|
||||||
|
support bidirectional SCSI transfers ?
|
||||||
|
*/
|
||||||
|
switch (task->xfer_dir) {
|
||||||
|
case SCSI_XFER_WRITE:
|
||||||
|
io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
|
||||||
|
io_hdr.dxferp = d->data;
|
||||||
|
io_hdr.dxfer_len = d->size;
|
||||||
|
break;
|
||||||
|
case SCSI_XFER_READ:
|
||||||
|
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
|
||||||
|
task->datain.size = task->expxferlen;
|
||||||
|
task->datain.data = malloc(task->expxferlen);
|
||||||
|
io_hdr.dxferp = task->datain.data;
|
||||||
|
io_hdr.dxfer_len = task->datain.size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SCSI timeout in ms */
|
||||||
|
io_hdr.timeout = 5000;
|
||||||
|
|
||||||
|
if(ioctl(sdev->sgio_fd, SG_IO, &io_hdr) < 0){
|
||||||
|
sdev->error_str = strdup("SG_IO ioctl failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now for the error processing */
|
||||||
|
if(io_hdr.sb_len_wr > 0){
|
||||||
|
task->status = SCSI_STATUS_CHECK_CONDITION;
|
||||||
|
task->sense.error_type = sense[0] & 0x7f;
|
||||||
|
switch (task->sense.error_type) {
|
||||||
|
case 0x70:
|
||||||
|
case 0x71:
|
||||||
|
task->sense.key = sense[2] & 0x0f;
|
||||||
|
task->sense.ascq = scsi_get_uint16(&sense[12]);
|
||||||
|
break;
|
||||||
|
case 0x72:
|
||||||
|
case 0x73:
|
||||||
|
task->sense.key = sense[1] & 0x0f;
|
||||||
|
task->sense.ascq = scsi_get_uint16(&sense[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sense_len=io_hdr.sb_len_wr;
|
||||||
|
snprintf(buf, sizeof(buf), "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)",
|
||||||
|
scsi_sense_key_str(task->sense.key),
|
||||||
|
task->sense.key,
|
||||||
|
scsi_sense_ascq_str(task->sense.ascq),
|
||||||
|
task->sense.ascq);
|
||||||
|
sdev->error_str = strdup(buf);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(io_hdr.masked_status){
|
||||||
|
task->status = SCSI_STATUS_ERROR;
|
||||||
|
task->sense.key = 0x0f;
|
||||||
|
task->sense.ascq = 0xffff;
|
||||||
|
|
||||||
|
sdev->error_str = strdup("SCSI masked error");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(io_hdr.host_status){
|
||||||
|
task->status = SCSI_STATUS_ERROR;
|
||||||
|
task->sense.key = 0x0f;
|
||||||
|
task->sense.ascq = 0xffff;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "SCSI host error. Status=0x%x", io_hdr.host_status);
|
||||||
|
sdev->error_str = strdup(buf);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
if(io_hdr.driver_status){
|
||||||
|
task->status = SCSI_STATUS_ERROR;
|
||||||
|
task->sense.key = 0x0f;
|
||||||
|
task->sense.ascq = 0xffff;
|
||||||
|
|
||||||
|
sdev->error_str = strdup("SCSI driver error");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void logging(int level, const char *format, ...)
|
void logging(int level, const char *format, ...)
|
||||||
@@ -1714,7 +1827,7 @@ int report_supported_opcodes(struct scsi_device *sdev, struct scsi_task **out_ta
|
|||||||
|
|
||||||
task = send_scsi_command(sdev, task, NULL);
|
task = send_scsi_command(sdev, task, NULL);
|
||||||
|
|
||||||
ret = check_result("INQUIRY", sdev, task, status, key, ascq, num_ascq);
|
ret = check_result("REPORT_SUPPORTED_OPCODES", sdev, task, status, key, ascq, num_ascq);
|
||||||
if (out_task) {
|
if (out_task) {
|
||||||
*out_task = task;
|
*out_task = task;
|
||||||
} else if (task) {
|
} else if (task) {
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ struct scsi_device {
|
|||||||
const char *iscsi_url;
|
const char *iscsi_url;
|
||||||
|
|
||||||
const char *sgio_dev;
|
const char *sgio_dev;
|
||||||
|
int sgio_fd;
|
||||||
};
|
};
|
||||||
extern struct scsi_device *sd;
|
extern struct scsi_device *sd;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
@@ -30,6 +32,12 @@
|
|||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SG_IO
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <scsi/sg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <CUnit/CUnit.h>
|
#include <CUnit/CUnit.h>
|
||||||
#include <CUnit/Basic.h>
|
#include <CUnit/Basic.h>
|
||||||
#include <CUnit/Automated.h>
|
#include <CUnit/Automated.h>
|
||||||
@@ -673,15 +681,17 @@ test_teardown(void)
|
|||||||
int
|
int
|
||||||
suite_init(void)
|
suite_init(void)
|
||||||
{
|
{
|
||||||
if (sd->iscsi_ctx) {
|
if (sd->iscsi_url) {
|
||||||
iscsi_logout_sync(sd->iscsi_ctx);
|
if (sd->iscsi_ctx) {
|
||||||
iscsi_destroy_context(sd->iscsi_ctx);
|
iscsi_logout_sync(sd->iscsi_ctx);
|
||||||
}
|
iscsi_destroy_context(sd->iscsi_ctx);
|
||||||
sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun);
|
}
|
||||||
if (sd->iscsi_ctx == NULL) {
|
sd->iscsi_ctx = iscsi_context_login(initiatorname1, sd->iscsi_url, &sd->iscsi_lun);
|
||||||
fprintf(stderr,
|
if (sd->iscsi_ctx == NULL) {
|
||||||
"error: Failed to login to target for test set-up\n");
|
fprintf(stderr,
|
||||||
return 1;
|
"error: Failed to login to target for test set-up\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifndef HAVE_CU_SUITEINFO_PSETUPFUNC
|
#ifndef HAVE_CU_SUITEINFO_PSETUPFUNC
|
||||||
/* libcunit version 1 */
|
/* libcunit version 1 */
|
||||||
@@ -697,10 +707,12 @@ suite_cleanup(void)
|
|||||||
/* libcunit version 1 */
|
/* libcunit version 1 */
|
||||||
test_teardown();
|
test_teardown();
|
||||||
#endif
|
#endif
|
||||||
if (sd->iscsi_ctx) {
|
if (sd->iscsi_url) {
|
||||||
iscsi_logout_sync(sd->iscsi_ctx);
|
if (sd->iscsi_ctx) {
|
||||||
iscsi_destroy_context(sd->iscsi_ctx);
|
iscsi_logout_sync(sd->iscsi_ctx);
|
||||||
sd->iscsi_ctx = NULL;
|
iscsi_destroy_context(sd->iscsi_ctx);
|
||||||
|
sd->iscsi_ctx = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -863,11 +875,31 @@ static void parse_and_add_tests(char *testname_re)
|
|||||||
|
|
||||||
static int connect_scsi_device(struct scsi_device *sdev, const char *initiatorname)
|
static int connect_scsi_device(struct scsi_device *sdev, const char *initiatorname)
|
||||||
{
|
{
|
||||||
sdev->iscsi_ctx = iscsi_context_login(initiatorname, sdev->iscsi_url, &sdev->iscsi_lun);
|
if (sdev->iscsi_url) {
|
||||||
if (sdev->iscsi_ctx == NULL) {
|
sdev->iscsi_ctx = iscsi_context_login(initiatorname, sdev->iscsi_url, &sdev->iscsi_lun);
|
||||||
return -1;
|
if (sdev->iscsi_ctx == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return 0;
|
#ifdef HAVE_SG_IO
|
||||||
|
if (sdev->sgio_dev) {
|
||||||
|
int version;
|
||||||
|
|
||||||
|
if ((sdev->sgio_fd = open(sdev->sgio_dev, O_RDWR)) == -1) {
|
||||||
|
fprintf(stderr, "Failed to open SG_IO device %s. Error:%s\n", sdev->sgio_dev,
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((ioctl(sdev->sgio_fd, SG_GET_VERSION_NUM, &version) < 0) || (version < 30000)) {
|
||||||
|
fprintf(stderr, "%s is not a SCSI device node\n", sdev->sgio_dev);
|
||||||
|
close(sdev->sgio_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_scsi_device(struct scsi_device *sdev)
|
static void free_scsi_device(struct scsi_device *sdev)
|
||||||
@@ -885,6 +917,15 @@ static void free_scsi_device(struct scsi_device *sdev)
|
|||||||
iscsi_destroy_context(sdev->iscsi_ctx);
|
iscsi_destroy_context(sdev->iscsi_ctx);
|
||||||
sdev->iscsi_ctx = NULL;
|
sdev->iscsi_ctx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sdev->sgio_dev) {
|
||||||
|
free(discard_const(sdev->sgio_dev));
|
||||||
|
sdev->sgio_dev = NULL;
|
||||||
|
}
|
||||||
|
if (sdev->sgio_fd != -1) {
|
||||||
|
close(sdev->sgio_fd);
|
||||||
|
sdev->sgio_fd = -1;
|
||||||
|
}
|
||||||
free(sd);
|
free(sd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,6 +970,7 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
sd = malloc(sizeof(struct scsi_device));
|
sd = malloc(sizeof(struct scsi_device));
|
||||||
memset(sd, '\0', sizeof(struct scsi_device));
|
memset(sd, '\0', sizeof(struct scsi_device));
|
||||||
|
sd->sgio_fd = -1;
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnuvxV", long_opts,
|
while ((c = getopt_long(argc, argv, "?hli:I:t:sdgfAsSnuvxV", long_opts,
|
||||||
&opt_idx)) > 0) {
|
&opt_idx)) > 0) {
|
||||||
@@ -991,7 +1033,13 @@ main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
sd->iscsi_url = strdup(argv[optind++]);
|
if (!strncmp(argv[optind], "iscsi://", 8)) {
|
||||||
|
sd->iscsi_url = strdup(argv[optind++]);
|
||||||
|
#ifdef HAVE_SG_IO
|
||||||
|
} else if (!strncmp(argv[optind], "/dev/sg", 7)) {
|
||||||
|
sd->sgio_dev = strdup(argv[optind++]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
fprintf(stderr, "error: too many arguments\n");
|
fprintf(stderr, "error: too many arguments\n");
|
||||||
@@ -1002,8 +1050,12 @@ main(int argc, char *argv[])
|
|||||||
/* XXX why is this done? */
|
/* XXX why is this done? */
|
||||||
real_iscsi_queue_pdu = dlsym(RTLD_NEXT, "iscsi_queue_pdu");
|
real_iscsi_queue_pdu = dlsym(RTLD_NEXT, "iscsi_queue_pdu");
|
||||||
|
|
||||||
if (sd->iscsi_url == NULL) {
|
if (sd->iscsi_url == NULL && sd->sgio_dev== NULL ) {
|
||||||
fprintf(stderr, "You must specify the URL\n");
|
#ifdef HAVE_SG_IO
|
||||||
|
fprintf(stderr, "You must specify either an iSCSI URL or a /dev/sg device\n");
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "You must specify either an iSCSI URL\n");
|
||||||
|
#endif
|
||||||
print_usage();
|
print_usage();
|
||||||
if (testname_re)
|
if (testname_re)
|
||||||
free(testname_re);
|
free(testname_re);
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ test_read10_beyond_eol(void)
|
|||||||
CU_ASSERT_EQUAL(ret, 0);
|
CU_ASSERT_EQUAL(ret, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
logging(LOG_VERBOSE, "Test READ10 1-256 blocks at LBA==2^31");
|
logging(LOG_VERBOSE, "Test READ10 1-256 blocks at LBA==2^31");
|
||||||
for (i = 1; i <= 256; i++) {
|
for (i = 1; i <= 256; i++) {
|
||||||
if (maximum_transfer_length && maximum_transfer_length < i) {
|
if (maximum_transfer_length && maximum_transfer_length < i) {
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ test_read16_beyond_eol(void)
|
|||||||
CU_ASSERT_EQUAL(ret, 0);
|
CU_ASSERT_EQUAL(ret, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==2^63");
|
logging(LOG_VERBOSE, "Test READ16 1-256 blocks at LBA==2^63");
|
||||||
for (i = 1; i <= 256; i++) {
|
for (i = 1; i <= 256; i++) {
|
||||||
if (maximum_transfer_length && maximum_transfer_length < i) {
|
if (maximum_transfer_length && maximum_transfer_length < i) {
|
||||||
|
|||||||
Reference in New Issue
Block a user