/* * 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; }