/*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By downloading, copying, installing or
* using the software you agree to this license. If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Intel License Agreement
*
* Copyright (c) 2000, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*
* -Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
*
* -The name of Intel Corporation may not be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "compat.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#include <unistd.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#include "scsi_cmd_codes.h"
#include "iscsi.h"
#include "initiator.h"
#include "tests.h"
#include "osd.h"
#include "osd_ops.h"
#define toSeconds(t) (t.tv_sec + (t.tv_usec/1000000.0))
typedef struct osd_device_t {
int target;
int lun;
} OSD_DEVICE_T;
int osd_command(void *dev, osd_args_t * args, OSD_OPS_MEM * mem);
/*
* SCSI Command Tests
*/
int
nop_out(uint64_t target, int lun, int length, int ping, const char *data)
{
initiator_cmd_t cmd;
iscsi_nop_out_args_t nop_cmd;
cmd.type = ISCSI_NOP_OUT;
cmd.ptr = &nop_cmd;
cmd.isid = target;
cmd.targetname[0] = 0x0;
(void) memset(&nop_cmd, 0x0, sizeof(iscsi_nop_out_args_t));
RETURN_GREATER("length", length, 4096, NO_CLEANUP, -1);
nop_cmd.length = length;
nop_cmd.data = (const uint8_t *) data;
nop_cmd.lun = lun;
if (!ping) {
nop_cmd.tag = 0xffffffff;
}
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
return 0;
}
static int
inquiry(uint64_t target, uint32_t lun, uint32_t *device_type)
{
uint8_t data[36], cdb[16];
initiator_cmd_t cmd;
iscsi_scsi_cmd_args_t args;
memset(cdb, 0, 16);
cdb[0] = INQUIRY;
cdb[1] = lun << 5;
cdb[4] = 35;
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.input = 1;
args.trans_len = 36;
args.cdb = cdb;
args.lun = lun;
args.recv_data = data;
memset(&cmd, 0, sizeof(initiator_cmd_t));
cmd.isid = target;
cmd.type = ISCSI_SCSI_CMD;
cmd.ptr = &args;
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "INQUIRY failed (status %#x)\n", args.status);
return -1;
}
*device_type = data[0] & 0x1f;
iscsi_trace(TRACE_SCSI_DEBUG, __FILE__, __LINE__, "Device Type %#x\n", *device_type);
return 0;
}
int
read_capacity(uint64_t target, uint32_t lun, uint32_t *max_lba, uint32_t *block_len)
{
uint8_t data[8], cdb[16];
initiator_cmd_t cmd;
iscsi_scsi_cmd_args_t args;
memset(cdb, 0, 16);
cdb[0] = READ_CAPACITY;
cdb[1] = lun << 5;
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.recv_data = data;
args.input = 1;
args.lun = lun;
args.trans_len = 8;
args.cdb = cdb;
cmd.isid = target;
cmd.type = ISCSI_SCSI_CMD;
cmd.ptr = &args;
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "READ_CAPACITY failed (status %#x)\n", args.status);
return -1;
}
*max_lba = ISCSI_NTOHL(*((uint32_t *) (data)));
*block_len = ISCSI_NTOHL(*((uint32_t *) (data + 4)));
iscsi_trace(TRACE_SCSI_DEBUG, __FILE__, __LINE__, "Max LBA (lun %u): %u\n", lun, *max_lba);
iscsi_trace(TRACE_SCSI_DEBUG, __FILE__, __LINE__, "Block Len (lun %u): %u\n", lun, *block_len);
if (*max_lba == 0) {
iscsi_trace_error(__FILE__, __LINE__, "Device returned Maximum LBA of zero\n");
return -1;
}
if (*block_len % 2) {
iscsi_trace_error(__FILE__, __LINE__, "Device returned strange block len: %u\n", *block_len);
return -1;
}
return 0;
}
/*
* write_read_test() writes a pattern to the first and last block of the
* target:lun
*/
/*
* specified and then reads back this pattern. <type> is either 6 or 10 ans
* specifies
*/
/* WRITE_6/READ_6 and WRITE_10/READ_10, respectively. */
int
write_read_test(uint64_t target, uint32_t lun, int type)
{
iscsi_scsi_cmd_args_t args;
initiator_cmd_t cmd;
uint32_t max_lba;
uint32_t block_len;
uint32_t i;
uint16_t len = 1;
uint8_t data[4096];
uint8_t cdb[16];
int j;
if ((type != 6) && (type != 10)) {
iscsi_trace_error(__FILE__, __LINE__, "bad type, select 6 or 10\n");
return -1;
}
/* determine last block on device */
if (read_capacity(target, lun, &max_lba, &block_len) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_capacity() failed\n");
return -1;
}
/* write pattern into first and last block on device */
for (i = 0; i <= max_lba; i += max_lba) {
for (j = 0; j < block_len; j++)
data[j] = (uint8_t) (i + j);
memset(cdb, 0, 16);
if (type == 10) {
cdb[0] = WRITE_10;
cdb[1] = lun << 5;
lba2cdb(cdb, &i, &len);
} else {
*((uint32_t *) (cdb + 0)) = ISCSI_HTONL(i);
cdb[0] = WRITE_6;
cdb[1] = (cdb[1] & 0x1f) | lun << 5;
cdb[4] = len;
}
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.send_data = data;
args.output = 1;
args.length = block_len;
args.lun = lun;
args.trans_len = block_len;
args.cdb = cdb;
cmd.isid = target;
cmd.ptr = &args;
cmd.type = ISCSI_SCSI_CMD;
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
}
/* read pattern back from first and last block */
for (i = 0; i <= max_lba; i += max_lba) {
memset(cdb, 0, 16);
if (type == 10) {
cdb[0] = READ_10;
cdb[1] = lun << 5;
lba2cdb(cdb, &i, &len);
} else {
*((uint32_t *) (cdb + 0)) = ISCSI_HTONL(i);
cdb[0] = READ_6;
cdb[1] = (cdb[1] & 0x1f) | lun << 5;
cdb[4] = len;
}
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.recv_data = data;
args.input = 1;
args.lun = lun;
args.trans_len = block_len;
args.cdb = cdb;
cmd.isid = target;
cmd.type = ISCSI_SCSI_CMD;
cmd.ptr = &args;
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
for (j = 0; j < block_len; j++) {
if (data[j] != (uint8_t) (i + j)) {
iscsi_trace_error(__FILE__, __LINE__, "Bad byte. data[%d] = %u, expected %u.\n",
j, data[j], (uint8_t) (i + j));
return -1;
}
}
}
return 0;
}
/*
* WRITE_10|READ_10
*/
int
read_or_write(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
uint32_t block_len, uint8_t *data, int sg_len, int writing)
{
initiator_cmd_t cmd;
iscsi_scsi_cmd_args_t args;
uint8_t cdb[16];
/* Build CDB */
cdb[0] = writing ? WRITE_10 : READ_10;
cdb[1] = lun << 5;
cdb[2] = ((uint8_t *) &lba)[3];
cdb[3] = ((uint8_t *) &lba)[2];
cdb[4] = ((uint8_t *) &lba)[1];
cdb[5] = ((uint8_t *) &lba)[0];
cdb[7] = ((uint8_t *) &len)[1];
cdb[8] = ((uint8_t *) &len)[0];
/* Build iSCSI command */
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.lun = lun;
args.output = writing ? 1 : 0;
args.input = writing ? 0 : 1;
args.trans_len = len * block_len;
args.send_data = writing ? data : NULL;
args.send_sg_len = writing ? sg_len : 0;
args.recv_data = writing ? NULL : data;
args.recv_sg_len = writing ? 0 : sg_len;
args.cdb = cdb;
cmd.isid = target;
cmd.ptr = &args;
cmd.type = ISCSI_SCSI_CMD;
/* Execute iSCSI command */
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
return -1;
}
return 0;
}
int
throughput_test(uint32_t target, uint32_t lun, uint32_t length, uint32_t request, uint32_t verbose, int writing, int sg_factor)
{
uint32_t max_lba, block_len;
uint8_t **data;
uint32_t iters;
uint32_t num_blocks;
uint32_t block_offset;
struct iovec *sg;
int i, j;
struct timeval t_start, t_stop;
double seconds;
/* Get device block len & capacity */
if (read_capacity(target, lun, &max_lba, &block_len) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_capacity() failed\n");
return -1;
}
if (request % block_len) {
iscsi_trace_error(__FILE__, __LINE__, "request must be a multiple of %u\n", block_len);
return -1;
}
if (!sg_factor) {
iscsi_trace_error(__FILE__, __LINE__, "sg_factor must be at least 1\n");
return -1;
}
if (request % sg_factor) {
iscsi_trace_error(__FILE__, __LINE__, "request must be a multiple of sg_factor\n");
return -1;
}
if (length % request) {
iscsi_trace_error(__FILE__, __LINE__, "length must be a multiple of request\n");
return -1;
}
if (length > ((max_lba + 1) * block_len)) {
iscsi_trace_error(__FILE__, __LINE__, "attempt to read past device (max length %u)\n", max_lba * block_len);
return -1;
}
if ((sg = iscsi_malloc(sg_factor * sizeof(struct iovec))) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
if ((data = iscsi_malloc(sg_factor * sizeof(uint8_t *))) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
for (i = 0; i < sg_factor; i++) {
if ((data[i] = iscsi_malloc(request / sg_factor)) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
}
gettimeofday(&t_start, 0);
iters = length / request;
num_blocks = request / block_len;
for (i = 0; i < iters; i++) {
block_offset = i * num_blocks;
/* The iSCSI initiator may modify our sg list */
for (j = 0; j < sg_factor; j++) {
sg[j].iov_base = data[j];
sg[j].iov_len = request / sg_factor;
}
if (read_or_write(target, lun, block_offset, num_blocks, block_len, (uint8_t *) sg, sg_factor, writing) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_10() failed\n");
goto done;
}
if (verbose && !((i + 1) % verbose)) {
printf("%u total bytes %s: this request (lba %u, len %u)\n",
(i + 1) * request, writing ? "written" : "read", block_offset, num_blocks);
}
}
gettimeofday(&t_stop, 0);
seconds = toSeconds(t_stop) - toSeconds(t_start);
/* Output results */
printf("%u MB %s in %.2f seconds --> %.2f MB/sec\n", length / 1048576, writing ? "written" : "read", (double) seconds,
(double) (length / seconds) / 1048576);
done: for (i = 0; i < sg_factor; i++) {
if (data[i])
iscsi_free(data[i]);
}
iscsi_free(data);
iscsi_free(sg);
return 0;
}
int
integrity_test(uint32_t target, uint32_t lun, uint32_t length, int sg_factor)
{
uint32_t max_lba, block_len;
uint8_t **data;
struct iovec *sg;
int i, j;
/* Get device block len & capacity; and check args */
if (read_capacity(target, lun, &max_lba, &block_len) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_capacity() failed\n");
return -1;
}
if (length % block_len) {
iscsi_trace_error(__FILE__, __LINE__, "length must be a multiple of block len %u\n", block_len);
return -1;
}
if (!sg_factor) {
iscsi_trace_error(__FILE__, __LINE__, "sg_factor must be at least 1\n");
return -1;
}
if (length % sg_factor) {
iscsi_trace_error(__FILE__, __LINE__, "length must be a multiple of sg_factor\n");
return -1;
}
if (length > ((max_lba + 1) * block_len)) {
iscsi_trace_error(__FILE__, __LINE__, "attempt to read past device (max length %u)\n", max_lba * block_len);
return -1;
}
/* Allocate sg and data buffers; fill data buffers with pattern */
if ((sg = iscsi_malloc(sg_factor * sizeof(struct iovec))) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
if ((data = iscsi_malloc(sg_factor * sizeof(uint8_t *))) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
for (i = 0; i < sg_factor; i++) {
if ((data[i] = iscsi_malloc(length / sg_factor)) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
for (j = 0; j < (length / sg_factor); j++) {
/* data[i][j] = i+j; */
data[i][j] = i + 1;
}
}
/* Write blocks */
for (j = 0; j < sg_factor; j++) {
sg[j].iov_base = data[j];
sg[j].iov_len = length / sg_factor;
}
if (read_or_write(target, lun, 0, length / block_len, block_len, (uint8_t *) sg, sg_factor, 1) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_or_write() failed\n");
goto done;
}
for (i = 0; i < sg_factor; i++) {
for (j = 0; j < (length / sg_factor); j++) {
/* if (data[i][j] != (uint8_t)(i+j)) { */
if (data[i][j] != (uint8_t) (i + 1)) {
iscsi_trace_error(__FILE__, __LINE__, "Bad byte data[%d][%d]: got %u, expected %u\n", i, j, data[i][j], (uint8_t) (i + j));
goto done;
}
}
}
/* Read blocks */
for (j = 0; j < sg_factor; j++) {
memset(data[j], 0, length / sg_factor);
sg[j].iov_base = data[j];
sg[j].iov_len = length / sg_factor;
}
if (read_or_write(target, lun, 0, length / block_len, block_len, (uint8_t *) sg, sg_factor, 0) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_or_write() failed\n");
goto done;
}
for (i = 0; i < sg_factor; i++) {
for (j = 0; j < (length / sg_factor); j++) {
/* if (data[i][j] != (uint8_t)(i+j)) { */
if (data[i][j] != (uint8_t) (i + 1)) {
iscsi_trace_error(__FILE__, __LINE__, "Bad byte data[%d][%d]: got %u, expected %u\n", i, j, data[i][j], (uint8_t) (i + j));
goto done;
}
}
}
done:
for (i = 0; i < sg_factor; i++) {
if (data[i])
iscsi_free(data[i]);
}
iscsi_free(data);
iscsi_free(sg);
return 0;
}
int
nop_test(uint32_t target, uint32_t lun, uint32_t iters)
{
struct timeval t_start, t_stop;
char *data;
int i, j, k;
if ((data = iscsi_malloc(4096)) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "iscsi_malloc() failed\n");
return -1;
}
/* Fill with some pattern */
for (i = 0; i < 4096; i++)
data[i] = (uint8_t) i;
for (k = 0; k <= 1; k++) { /* 0 = no ping, 1= ping */
for (j = 0; j <= 4096; j *= 4) { /* payload between 0 and
* 4K */
gettimeofday(&t_start, 0);
for (i = 0; i < iters; i++) {
if (nop_out(target, lun, j, k, data) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_out() failed\n");
return -1;
}
}
gettimeofday(&t_stop, 0);
/* Output results */
printf("NOP_OUT (%4i bytes, ping = %d): %u iters in %.2f sec --> %.2f usec\n", j, k,
iters, toSeconds(t_stop) - toSeconds(t_start),
((toSeconds(t_stop) - toSeconds(t_start)) * 1e6) / iters);
if (!j)
j = 1;
}
}
iscsi_free(data);
return 0;
}
static const char *
humanise(uint8_t op)
{
switch(op) {
case TEST_UNIT_READY:
return "TEST_UNIT_READY";
case INQUIRY:
return "INQUIRY";
case READ_10:
return "READ_10";
case WRITE_10:
return "WRITE_10";
case READ_CAPACITY:
return "READ CAPACITY";
default:
return "unknown";
}
}
/* latency_test() performs <op> for a number of iterations and outputs */
/* the average latency. <op> can be any of WRITE_10, READ_10, */
/* TEST_UNIT_READY, READ_CAPACITY or INQUIRY. */
int
latency_test(uint64_t target, uint32_t lun, uint8_t op, uint32_t iters)
{
uint32_t length, trans_len;
uint32_t max_lba, block_len;
int input, output;
uint8_t *data, cdb[16];
initiator_cmd_t cmd;
iscsi_scsi_cmd_args_t args;
struct timeval t_start, t_stop;
int i, rc = -1;
uint32_t lba = 0;
uint16_t len = 1;
/* Get device block len */
if ((op == WRITE_10) || (op == READ_10)) {
if (read_capacity(target, lun, &max_lba, &block_len) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_capacity() failed\n");
return -1;
}
if ((data = iscsi_malloc(block_len)) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
} else {
if ((data = iscsi_malloc(1024)) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
}
/* Build CDB: */
memset(cdb, 0, 16);
input = output = length = trans_len = 0;
cdb[1] = lun << 5;
switch (op) {
case TEST_UNIT_READY:
cdb[0] = TEST_UNIT_READY;
break;
case INQUIRY:
cdb[0] = INQUIRY;
cdb[4] = 35;
input = 1;
trans_len = 36;
break;
case READ_CAPACITY:
cdb[0] = READ_CAPACITY;
input = 1;
trans_len = 8;
break;
case READ_10:
input = 1;
trans_len = block_len;
cdb[0] = READ_10;
cdb[1] = lun << 5;
lba2cdb(cdb, &lba, &len);
break;
case WRITE_10:
output = 1;
trans_len = block_len;
cdb[0] = WRITE_10;
cdb[1] = lun << 5;
lba2cdb(cdb, &lba, &len);
break;
default:
iscsi_trace_error(__FILE__, __LINE__, "op %#x not implemented\n", op);
return -1;
}
/* Build iSCSI command */
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.lun = lun;
args.cdb = cdb;
args.input = input;
args.output = output;
args.trans_len = trans_len;
args.send_data = output ? data : NULL;
args.recv_data = input ? data : NULL;
cmd.isid = target;
cmd.type = ISCSI_SCSI_CMD;
cmd.ptr = &args;
/* Run test */
gettimeofday(&t_start, 0);
for (i = 0; i < iters; i++) {
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
goto done;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
goto done;
}
}
gettimeofday(&t_stop, 0);
/* Output results */
printf("SCSI op 0x%2x (%s): %u iters in %.2f sec --> %.2f usec\n",
op, humanise(op), iters, toSeconds(t_stop) - toSeconds(t_start),
((toSeconds(t_stop) - toSeconds(t_start)) * 1e6) / iters);
rc = 0;
done:
iscsi_free(data);
return rc;
}
/*
* scatter_gather() tests scatter/gather performance for WRITE_10 and
* READ_10. Instead of specifying a data buffer in args.send_data and
* arg.recv_data, we specify a scatter/gather list. */
int
scatter_gather_test(uint64_t target, uint32_t lun, uint8_t op)
{
uint32_t length, trans_len;
uint8_t cdb[16];
uint32_t lba = 0;
struct iovec *sg;
uint8_t **buff;
uint32_t block_len, max_lba;
uint16_t len;
int i, n;
int input, output;
initiator_cmd_t cmd;
iscsi_scsi_cmd_args_t args;
int xfer_size = 100 * 1048576;
int xfer_chunk = 1048576;
int rc = -1;
struct timeval t_start, t_stop;
/* Number of iterations (xfer_chunk bytes read/written per iteration) */
if (xfer_size % xfer_chunk) {
iscsi_trace_error(__FILE__, __LINE__, "xfer_size (%d) is not a multiple of xfer_chunk (%d)\n", xfer_size, xfer_chunk);
return -1;
}
n = xfer_size / xfer_chunk;
/* Number of blocks per iteration */
if (read_capacity(target, lun, &max_lba, &block_len) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_capacity() failed\n");
return -1;
}
if (xfer_chunk % block_len) {
iscsi_trace_error(__FILE__, __LINE__, "xfer_chunk (%d) is not a multiple of block_len (%d)\n", xfer_chunk, block_len);
return -1;
}
len = xfer_chunk / block_len;
/* Build CDB */
memset(cdb, 0, 16);
cdb[1] = lun << 5;
trans_len = block_len * len;
length = input = output = 0;
switch (op) {
case WRITE_10:
cdb[0] = WRITE_10;
output = 1;
length = trans_len;
break;
case READ_10:
cdb[0] = READ_10;
input = 1;
break;
default:
iscsi_trace_error(__FILE__, __LINE__, "scatter/gather test not implemented for SCSI op %#x\n", op);
return -1;
}
/* Allocate buffers for scatter/gather */
if ((buff = iscsi_malloc(len * sizeof(uint8_t *))) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
for (i = 0; i < len; i++) {
buff[i] = iscsi_malloc(block_len);
if (buff[i] == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
}
}
if ((sg = iscsi_malloc(len * sizeof(struct iovec))) == NULL) {
iscsi_trace_error(__FILE__, __LINE__, "out of memory\n");
return -1;
}
for (i = 0; i < len; i++) {
sg[i].iov_base = buff[i];
sg[i].iov_len = block_len;
}
lba2cdb(cdb, &lba, &len);
gettimeofday(&t_start, 0);
/* Begin I/O */
for (i = 0; i < n; i++) {
#if 0
for (j = 0; j < len; j++) {
sg[j].iov_base = buff[j];
sg[j].iov_len = block_len;
}
#endif
memset(&args, 0, sizeof(iscsi_scsi_cmd_args_t));
args.send_data = output ? ((uint8_t *) sg) : NULL;
args.send_sg_len = output ? len : 0;
args.recv_data = input ? ((uint8_t *) sg) : NULL;
args.recv_sg_len = input ? len : 0;
args.input = input;
args.output = output;
args.length = length;
args.lun = lun;
args.trans_len = trans_len;
args.cdb = cdb;
cmd.isid = target;
cmd.type = ISCSI_SCSI_CMD;
cmd.ptr = &args;
if (initiator_command(&cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
goto done;
}
if (args.status) {
iscsi_trace_error(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
goto done;
}
}
gettimeofday(&t_stop, 0);
printf("SCSI op %#x (%s): %u bytes (%s) in %.2f secs --> %.2f MB/sec\n",
op, humanise(op), xfer_size, (op == WRITE_10) ? "gathered" : "scattered",
toSeconds(t_stop) - toSeconds(t_start),
(xfer_size / 1048576) / (toSeconds(t_stop) - toSeconds(t_start)));
rc = 0;
done:
for (i = 0; i < len; i++) {
iscsi_free(buff[i]);
}
iscsi_free(buff);
iscsi_free(sg);
return rc;
}
/*
* OSD Tests
*/
static int
osd_tests(int target, int lun)
{
uint32_t GroupID;
uint64_t UserID;
char buffer[1024];
OSD_DEVICE_T dev = {0, 0};
uint16_t len;
if (osd_create_group((void *) &dev, &osd_command, &GroupID) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_create_group() failed\n");
return -1;
}
printf("OSD_CREATE_GROUP: PASSED\n");
if (osd_create((void *) &dev, GroupID, &osd_command, &UserID) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_create() failed\n");
return -1;
}
printf("OSD_CREATE: PASSED\n");
if (osd_write((void *) &dev, GroupID, UserID, 0, 13, "Hello, World!", 0, &osd_command) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_write() failed\n");
return -1;
}
printf("OSD_WRITE: PASSED\n");
if (osd_read((void *) &dev, GroupID, UserID, 0, 13, buffer, 0, &osd_command) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_write() failed\n");
}
if (strncmp(buffer, "Hello, World!", 13)) {
iscsi_trace_error(__FILE__, __LINE__, "osd_read() failed\n");
return -1;
}
printf("OSD_READ: PASSED\n");
if (osd_set_one_attr((void *) &dev, GroupID, UserID, 0x30000000, 0x1, 480, buffer, &osd_command) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_write() failed\n");
}
printf("OSD_SET_ATTR: PASSED\n");
if (osd_get_one_attr((void *) &dev, GroupID, UserID, 0x30000000, 0, 480, &osd_command, &len, buffer) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_get_one_attr() failed\n");
}
if (strncmp(buffer, "Hello, World!", 13)) {
iscsi_trace_error(__FILE__, __LINE__, "osd_read() failed\n");
return -1;
}
printf("OSD_GET_ATTR: PASSED\n");
if (osd_remove((void *) &dev, GroupID, UserID, &osd_command) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_remove() failed\n");
return -1;
}
printf("OSD_REMOVE: PASSED\n");
if (osd_remove_group((void *) &dev, GroupID, &osd_command) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_remove_group() failed\n");
return -1;
}
printf("OSD_REMOVE: PASSED\n");
return 0;
}
static int
disk_tests(int target, int lun)
{
uint32_t request_size;
uint32_t max_lba, block_len;
uint32_t min_request_size = 8192;
uint32_t max_request_size = 262144;
uint32_t xfer_size = 32 * 262144;
/* Initial Tests */
if (read_capacity(target, lun, &max_lba, &block_len) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "read_capacity() failed\n");
return -1;
}
printf("read_capacity PASSED\n");
if (write_read_test(target, lun, 10) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "write_read_test() failed\n");
return -1;
}
printf("write_read_test PASSED\n");
if (integrity_test(target, lun, max_request_size, 1024) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "integrity_test() failed\n");
return -1;
}
printf("integrity_test PASSED\n");
/* Latency Tests */
if (latency_test(target, lun, READ_10, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(READ_10) failed\n");
return -1;
}
if (latency_test(target, lun, WRITE_10, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(WRITE_10) failed\n");
return -1;
}
if (latency_test(target, lun, READ_CAPACITY, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(READ_CAPACITY) failed\n");
return -1;
}
/* Throughput Tests */
for (request_size = min_request_size; request_size <= max_request_size; request_size *= 2) {
printf("%u bytes/request: ", request_size);
if (throughput_test(target, lun, xfer_size, request_size, (xfer_size / request_size) + 1, 1, 1) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "througput_test() failed\n");
return -1;
}
}
for (request_size = min_request_size; request_size <= max_request_size; request_size *= 2) {
printf("%u bytes/request: ", request_size);
if (throughput_test(target, lun, xfer_size, request_size, (xfer_size / request_size) + 1, 0, 1) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "througput_test() failed\n");
return -1;
}
}
return 0;
}
int
test_all(int target, int lun)
{
uint32_t device_type = 0;
const char *data = "Hello, world!";
/* Initial Tests */
printf("##BEGIN INITIAL TESTS[%d:%d]##\n", target, lun);
if (nop_out(target, lun, 13, 0, data) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_out() failed\n");
return -1;
}
printf("nop_out() PASSED\n");
if (nop_out(target, lun, 13, 1, data) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_out() w/ ping failed\n");
return -1;
}
printf("nop_out() w/ ping PASSED\n");
if (inquiry(target, lun, &device_type) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "inquiry() failed\n");
return -1;
}
printf("inquiry() PASSED: device type %#x\n", device_type);
printf("##END INITIAL TESTS[%d:%d]##\n\n", target, lun);
/* iSCSI Latency Tests */
printf("##BEGIN iSCSI LATENCY TESTS[%d:%d]##\n", target, lun);
if (nop_test(target, lun, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_test() failed\n");
return -1;
}
printf("##END iSCSI LATENCY TESTS[%d:%d]##\n\n", target, lun);
/* SCSI Latency Tests */
printf("##BEGIN SCSI LATENCY TESTS[%d:%d]##\n", target, lun);
if (latency_test(target, lun, TEST_UNIT_READY, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(TEST_UNIT_READY) failed\n");
return -1;
}
if (latency_test(target, lun, INQUIRY, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(INQUIRY) failed\n");
return -1;
}
printf("##END SCSI LATENCY TESTS[%d:%d]##\n\n", target, lun);
/* Device-specific tests */
printf("##BEGIN DEVICE-SPECIFIC TESTS[%d:%d]##\n", target, lun);
switch (device_type) {
case 0x00:
if (disk_tests(target, lun) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "disk_tests() failed\n");
return -1;
}
break;
case 0x0e:
if (osd_tests(target, lun) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_tests() failed\n");
return -1;
}
break;
default:
break;
}
printf("##END DEVICE-SPECIFIC TESTS[%d:%d]##\n\n", target, lun);
return 0;
}
int
ii_test_all(void)
{
uint32_t device_type = 0;
const char *data = "Hello, world!";
int target = 0;
int lun = 0;
/* Initial Tests */
printf("##BEGIN INITIAL TESTS[%d:%d]##\n", target, lun);
if (nop_out(target, lun, 13, 0, data) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_out() failed\n");
return -1;
}
printf("nop_out() PASSED\n");
if (nop_out(target, lun, 13, 1, data) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_out() w/ ping failed\n");
return -1;
}
printf("nop_out() w/ ping PASSED\n");
if (inquiry(target, lun, &device_type) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "inquiry() failed\n");
return -1;
}
printf("inquiry() PASSED: device type %#x\n", device_type);
printf("##END INITIAL TESTS[%d:%d]##\n\n", target, lun);
/* iSCSI Latency Tests */
printf("##BEGIN iSCSI LATENCY TESTS[%d:%d]##\n", target, lun);
if (nop_test(target, lun, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "nop_test() failed\n");
return -1;
}
printf("##END iSCSI LATENCY TESTS[%d:%d]##\n\n", target, lun);
/* SCSI Latency Tests */
printf("##BEGIN SCSI LATENCY TESTS[%d:%d]##\n", target, lun);
if (latency_test(target, lun, TEST_UNIT_READY, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(TEST_UNIT_READY) failed\n");
return -1;
}
if (latency_test(target, lun, INQUIRY, 1000) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "latency_test(INQUIRY) failed\n");
return -1;
}
printf("##END SCSI LATENCY TESTS[%d:%d]##\n\n", target, lun);
/* Device-specific tests */
printf("##BEGIN DEVICE-SPECIFIC TESTS[%d:%d]##\n", target, lun);
switch (device_type) {
case 0x00:
if (disk_tests(target, lun) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "disk_tests() failed\n");
return -1;
}
break;
case 0x0e:
if (osd_tests(target, lun) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "osd_tests() failed\n");
return -1;
}
break;
default:
break;
}
printf("##END DEVICE-SPECIFIC TESTS[%d:%d]##\n\n", target, lun);
return 0;
}
int
osd_command(void *dev, osd_args_t * args, OSD_OPS_MEM * m)
{
initiator_cmd_t initiator_cmd;
iscsi_scsi_cmd_args_t scsi_cmd;
uint8_t ahs[1024], *ahs_ptr = ahs;
int ahs_len = 0;
uint8_t cdb[CONFIG_OSD_CDB_LEN];
uint64_t target = ((OSD_DEVICE_T *) dev)->target;
uint16_t len;
/* Build AHS for expected bidi read length */
if (m->send_len && m->recv_len) {
memset(ahs_ptr, 0, 8);
len = 8;
*((uint32_t *) ahs_ptr) = ISCSI_HTONS(len); /* AHS length */
ahs_ptr[2] = 0x02; /* Type */
*((uint32_t *) (ahs_ptr + 4)) = ISCSI_HTONL(m->recv_len); /* Expected Read length */
ahs_ptr += 8;
ahs_len += 8;
}
/* Build AHS for extended CDB */
OSD_ENCAP_CDB(args, cdb);
/* OSD_PRINT_CDB(cdb, cdb+16); */
memset(ahs_ptr, 0, 4);
len = (CONFIG_OSD_CDB_LEN - 15); /* AHS length */
*((uint16_t *) ahs_ptr) = ISCSI_HTONS(len); /* AHS length */
ahs_ptr[2] = 0x01; /* Type */
memcpy(ahs_ptr + 4, cdb + 16, CONFIG_OSD_CDB_LEN - 16); /* Copy remaining CDB */
ahs_ptr += CONFIG_OSD_CDB_LEN - 15;
ahs_len += CONFIG_OSD_CDB_LEN - 15;
/* Build iscsi_scsi_cmd_args_t */
memset(&scsi_cmd, 0, sizeof(iscsi_scsi_cmd_args_t));
scsi_cmd.cdb = cdb;
scsi_cmd.ahs_len = ahs_len;
scsi_cmd.ahs = ahs;
if (m->send_len && m->recv_len) {
scsi_cmd.input = 1;
scsi_cmd.output = 1;
scsi_cmd.length = m->send_len;
scsi_cmd.trans_len = m->send_len;
scsi_cmd.bidi_trans_len = m->recv_len;
scsi_cmd.send_data = __UNCONST(m->send_data); /* XXX - agc */
scsi_cmd.send_sg_len = m->send_sg;
scsi_cmd.recv_data = m->recv_data;
scsi_cmd.recv_sg_len = m->recv_sg;
} else if (m->send_len) {
scsi_cmd.output = 1;
scsi_cmd.length = m->send_len;
scsi_cmd.trans_len = m->send_len;
scsi_cmd.send_data = __UNCONST(m->send_data); /* XXX - agc */
scsi_cmd.send_sg_len = m->send_sg;
} else if (m->recv_len) {
scsi_cmd.input = 1;
scsi_cmd.trans_len = m->recv_len;
scsi_cmd.recv_data = m->recv_data;
scsi_cmd.recv_sg_len = m->recv_sg;
}
/* Build initiator_cmd_t */
memset(&initiator_cmd, 0, sizeof(initiator_cmd_t));
initiator_cmd.isid = target;
initiator_cmd.type = ISCSI_SCSI_CMD;
initiator_cmd.ptr = &scsi_cmd;
/* Execute initiator_cmd_t */
if (initiator_command(&initiator_cmd) != 0) {
iscsi_trace_error(__FILE__, __LINE__, "initiator_command() failed\n");
return -1;
}
if (scsi_cmd.status != 0) {
iscsi_trace_error(__FILE__, __LINE__, "SCSI command failed\n");
return -1;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1