|
|
|
|
@@ -23,34 +23,39 @@
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include "iscsi.h"
|
|
|
|
|
#include "scsi-lowlevel.h"
|
|
|
|
|
|
|
|
|
|
const char *initiator = "iqn.2010-11.ronnie:iscsi-inq";
|
|
|
|
|
int max_in_flight = 50;
|
|
|
|
|
int blocks_per_io = 200;
|
|
|
|
|
uint32_t max_in_flight = 50;
|
|
|
|
|
uint32_t blocks_per_io = 200;
|
|
|
|
|
|
|
|
|
|
struct client {
|
|
|
|
|
int finished;
|
|
|
|
|
int in_flight;
|
|
|
|
|
uint32_t in_flight;
|
|
|
|
|
|
|
|
|
|
struct iscsi_context *src_iscsi;
|
|
|
|
|
int src_lun;
|
|
|
|
|
int src_blocksize;
|
|
|
|
|
uint64_t src_num_blocks;
|
|
|
|
|
struct scsi_inquiry_device_designator src_tgt_desig;
|
|
|
|
|
uint64_t pos;
|
|
|
|
|
|
|
|
|
|
struct iscsi_context *dst_iscsi;
|
|
|
|
|
int dst_lun;
|
|
|
|
|
int dst_blocksize;
|
|
|
|
|
uint64_t dst_num_blocks;
|
|
|
|
|
struct scsi_inquiry_device_designator dst_tgt_desig;
|
|
|
|
|
int use_16_for_rw;
|
|
|
|
|
int use_xcopy;
|
|
|
|
|
int progress;
|
|
|
|
|
int ignore_errors;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void fill_read_queue(struct client *client);
|
|
|
|
|
void fill_xcopy_queue(struct client *client);
|
|
|
|
|
|
|
|
|
|
struct write_task {
|
|
|
|
|
struct scsi_task *rt;
|
|
|
|
|
@@ -153,7 +158,7 @@ void read_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, vo
|
|
|
|
|
|
|
|
|
|
void fill_read_queue(struct client *client)
|
|
|
|
|
{
|
|
|
|
|
int num_blocks;
|
|
|
|
|
uint32_t num_blocks;
|
|
|
|
|
|
|
|
|
|
while(client->in_flight < max_in_flight && client->pos < client->src_num_blocks) {
|
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
@@ -185,14 +190,343 @@ void fill_read_queue(struct client *client)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int populate_tgt_desc(unsigned char *desc,
|
|
|
|
|
struct scsi_inquiry_device_designator *tgt_desig,
|
|
|
|
|
int rel_init_port_id, uint32_t block_size)
|
|
|
|
|
{
|
|
|
|
|
desc[0] = IDENT_DESCR_TGT_DESCR;
|
|
|
|
|
desc[1] = 0; /* peripheral type */
|
|
|
|
|
desc[2] = (rel_init_port_id >> 8) & 0xFF;
|
|
|
|
|
desc[3] = rel_init_port_id & 0xFF;
|
|
|
|
|
desc[4] = tgt_desig->code_set;
|
|
|
|
|
desc[5] = (tgt_desig->designator_type & 0xF)
|
|
|
|
|
| ((tgt_desig->association & 3) << 4);
|
|
|
|
|
desc[7] = tgt_desig->designator_length;
|
|
|
|
|
memcpy(desc + 8, tgt_desig->designator, tgt_desig->designator_length);
|
|
|
|
|
|
|
|
|
|
desc[28] = 0;
|
|
|
|
|
desc[29] = (block_size >> 16) & 0xFF;
|
|
|
|
|
desc[30] = (block_size >> 8) & 0xFF;
|
|
|
|
|
desc[31] = block_size & 0xFF;
|
|
|
|
|
|
|
|
|
|
return 32;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int populate_seg_desc_hdr(unsigned char *hdr, int dc, int cat, int src_index,
|
|
|
|
|
int dst_index)
|
|
|
|
|
{
|
|
|
|
|
int desc_len = 28;
|
|
|
|
|
|
|
|
|
|
hdr[0] = BLK_TO_BLK_SEG_DESCR;
|
|
|
|
|
hdr[1] = ((dc << 1) | cat) & 0xFF;
|
|
|
|
|
hdr[2] = (desc_len >> 8) & 0xFF;
|
|
|
|
|
hdr[3] = (desc_len - SEG_DESC_SRC_INDEX_OFFSET) & 0xFF; /* don't account for the first 4 bytes in descriptor header*/
|
|
|
|
|
hdr[4] = (src_index >> 8) & 0xFF;
|
|
|
|
|
hdr[5] = src_index & 0xFF;
|
|
|
|
|
hdr[6] = (dst_index >> 8) & 0xFF;
|
|
|
|
|
hdr[7] = dst_index & 0xFF;
|
|
|
|
|
|
|
|
|
|
return desc_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat,
|
|
|
|
|
int src_index, int dst_index, int num_blks,
|
|
|
|
|
uint64_t src_lba, uint64_t dst_lba)
|
|
|
|
|
{
|
|
|
|
|
int desc_len = populate_seg_desc_hdr(desc, dc, cat,
|
|
|
|
|
src_index, dst_index);
|
|
|
|
|
|
|
|
|
|
desc[10] = (num_blks >> 8) & 0xFF;
|
|
|
|
|
desc[11] = num_blks & 0xFF;
|
|
|
|
|
desc[12] = (src_lba >> 56) & 0xFF;
|
|
|
|
|
desc[13] = (src_lba >> 48) & 0xFF;
|
|
|
|
|
desc[14] = (src_lba >> 40) & 0xFF;
|
|
|
|
|
desc[15] = (src_lba >> 32) & 0xFF;
|
|
|
|
|
desc[16] = (src_lba >> 24) & 0xFF;
|
|
|
|
|
desc[17] = (src_lba >> 16) & 0xFF;
|
|
|
|
|
desc[18] = (src_lba >> 8) & 0xFF;
|
|
|
|
|
desc[19] = src_lba & 0xFF;
|
|
|
|
|
desc[20] = (dst_lba >> 56) & 0xFF;
|
|
|
|
|
desc[21] = (dst_lba >> 48) & 0xFF;
|
|
|
|
|
desc[22] = (dst_lba >> 40) & 0xFF;
|
|
|
|
|
desc[23] = (dst_lba >> 32) & 0xFF;
|
|
|
|
|
desc[24] = (dst_lba >> 24) & 0xFF;
|
|
|
|
|
desc[25] = (dst_lba >> 16) & 0xFF;
|
|
|
|
|
desc[26] = (dst_lba >> 8) & 0xFF;
|
|
|
|
|
desc[27] = dst_lba & 0xFF;
|
|
|
|
|
|
|
|
|
|
return desc_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len)
|
|
|
|
|
{
|
|
|
|
|
buf[0] = list_id;
|
|
|
|
|
buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
|
|
|
|
|
buf[2] = (tgt_desc_len >> 8) & 0xFF;
|
|
|
|
|
buf[3] = tgt_desc_len & 0xFF;
|
|
|
|
|
buf[8] = (seg_desc_len >> 24) & 0xFF;
|
|
|
|
|
buf[9] = (seg_desc_len >> 16) & 0xFF;
|
|
|
|
|
buf[10] = (seg_desc_len >> 8) & 0xFF;
|
|
|
|
|
buf[11] = seg_desc_len & 0xFF;
|
|
|
|
|
buf[12] = (inline_data_len >> 24) & 0xFF;
|
|
|
|
|
buf[13] = (inline_data_len >> 16) & 0xFF;
|
|
|
|
|
buf[14] = (inline_data_len >> 8) & 0xFF;
|
|
|
|
|
buf[15] = inline_data_len & 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void xcopy_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data)
|
|
|
|
|
{
|
|
|
|
|
struct client *client = (struct client *)private_data;
|
|
|
|
|
struct scsi_task *task = command_data;
|
|
|
|
|
|
|
|
|
|
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
|
|
|
|
printf("XCOPY failed with sense key:%d ascq:%04x\n",
|
|
|
|
|
task->sense.key, task->sense.ascq);
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status != SCSI_STATUS_GOOD) {
|
|
|
|
|
printf("XCOPY failed with %s\n", iscsi_get_error(iscsi));
|
|
|
|
|
if (!client->ignore_errors) {
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->in_flight--;
|
|
|
|
|
fill_xcopy_queue(client);
|
|
|
|
|
|
|
|
|
|
if (client->progress) {
|
|
|
|
|
printf("\r%"PRIu64" of %"PRIu64" blocks transferred.",
|
|
|
|
|
client->pos, client->src_num_blocks);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((client->in_flight == 0) && (client->pos == client->src_num_blocks)) {
|
|
|
|
|
client->finished = 1;
|
|
|
|
|
if (client->progress) {
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void fill_xcopy_queue(struct client *client)
|
|
|
|
|
{
|
|
|
|
|
while (client->in_flight < max_in_flight && client->pos < client->src_num_blocks) {
|
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
struct iscsi_data data;
|
|
|
|
|
unsigned char *xcopybuf;
|
|
|
|
|
int offset;
|
|
|
|
|
uint32_t num_blocks;
|
|
|
|
|
int tgt_desc_len;
|
|
|
|
|
int seg_desc_len;
|
|
|
|
|
|
|
|
|
|
client->in_flight++;
|
|
|
|
|
|
|
|
|
|
num_blocks = client->src_num_blocks - client->pos;
|
|
|
|
|
if (num_blocks > blocks_per_io) {
|
|
|
|
|
num_blocks = blocks_per_io;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data.size = XCOPY_DESC_OFFSET +
|
|
|
|
|
32 * 2 + /* IDENT_DESCR_TGT_DESCR */
|
|
|
|
|
28; /* BLK_TO_BLK_SEG_DESCR */
|
|
|
|
|
data.data = malloc(data.size);
|
|
|
|
|
if (data.data == NULL) {
|
|
|
|
|
printf("failed to alloc XCOPY buffer\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xcopybuf = data.data;
|
|
|
|
|
memset(xcopybuf, 0, data.size);
|
|
|
|
|
|
|
|
|
|
/* Initialise CSCD list with one src + one dst descriptor */
|
|
|
|
|
offset = XCOPY_DESC_OFFSET;
|
|
|
|
|
offset += populate_tgt_desc(xcopybuf + offset,
|
|
|
|
|
&client->src_tgt_desig,
|
|
|
|
|
0, client->src_blocksize);
|
|
|
|
|
offset += populate_tgt_desc(xcopybuf + offset,
|
|
|
|
|
&client->dst_tgt_desig,
|
|
|
|
|
0, client->dst_blocksize);
|
|
|
|
|
tgt_desc_len = offset - XCOPY_DESC_OFFSET;
|
|
|
|
|
|
|
|
|
|
/* Initialise one segment descriptor */
|
|
|
|
|
seg_desc_len = populate_seg_desc_b2b(xcopybuf + offset, 0, 0,
|
|
|
|
|
0, 1, num_blocks, client->pos, client->pos);
|
|
|
|
|
offset += seg_desc_len;
|
|
|
|
|
|
|
|
|
|
/* Initialise the parameter list header */
|
|
|
|
|
populate_param_header(xcopybuf, 1, 0, LIST_ID_USAGE_DISCARD, 0,
|
|
|
|
|
tgt_desc_len, seg_desc_len, 0);
|
|
|
|
|
|
|
|
|
|
task = iscsi_extended_copy_task(client->src_iscsi,
|
|
|
|
|
client->src_lun,
|
|
|
|
|
&data, xcopy_cb, client);
|
|
|
|
|
if (task == NULL) {
|
|
|
|
|
printf("failed to send XCOPY command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client->pos += num_blocks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cscd_ident_inq(struct iscsi_context *iscsi,
|
|
|
|
|
int lun,
|
|
|
|
|
struct scsi_inquiry_device_designator *_tgt_desig)
|
|
|
|
|
{
|
|
|
|
|
struct scsi_task *task = NULL;
|
|
|
|
|
struct scsi_inquiry_device_identification *inq_di = NULL;
|
|
|
|
|
struct scsi_inquiry_device_designator *desig, *tgt_desig = NULL;
|
|
|
|
|
enum scsi_designator_type prev_type = 0;
|
|
|
|
|
|
|
|
|
|
/* check what type of lun we have */
|
|
|
|
|
task = iscsi_inquiry_sync(iscsi, lun, 1,
|
|
|
|
|
SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 255);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr, "failed to send inquiry command: %s\n",
|
|
|
|
|
iscsi_get_error(iscsi));
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inq_di = scsi_datain_unmarshall(task);
|
|
|
|
|
if (inq_di == NULL) {
|
|
|
|
|
fprintf(stderr, "failed to unmarshall inquiry datain blob\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (desig = inq_di->designators; desig; desig = desig->next) {
|
|
|
|
|
switch (desig->designator_type) {
|
|
|
|
|
case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
|
|
|
|
|
case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
|
|
|
|
|
case SCSI_DESIGNATOR_TYPE_EUI_64:
|
|
|
|
|
case SCSI_DESIGNATOR_TYPE_NAA:
|
|
|
|
|
if (prev_type <= desig->designator_type) {
|
|
|
|
|
tgt_desig = desig;
|
|
|
|
|
prev_type = desig->designator_type;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tgt_desig == NULL) {
|
|
|
|
|
fprintf(stderr, "No suitalble target descriptor format found");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* copy what's needed for XCOPY */
|
|
|
|
|
_tgt_desig->code_set = tgt_desig->code_set;
|
|
|
|
|
_tgt_desig->association = tgt_desig->association;
|
|
|
|
|
_tgt_desig->designator_type = tgt_desig->designator_type;
|
|
|
|
|
_tgt_desig->designator_length = tgt_desig->designator_length;
|
|
|
|
|
_tgt_desig->designator = malloc(tgt_desig->designator_length);
|
|
|
|
|
memcpy(_tgt_desig->designator, tgt_desig->designator, tgt_desig->designator_length);
|
|
|
|
|
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cscd_param_check(struct iscsi_context *iscsi,
|
|
|
|
|
int lun,
|
|
|
|
|
uint32_t blocksize)
|
|
|
|
|
{
|
|
|
|
|
struct scsi_task *task = NULL;
|
|
|
|
|
struct scsi_copy_results_op_params *opp;
|
|
|
|
|
uint32_t io_segment_bytes;
|
|
|
|
|
|
|
|
|
|
task = iscsi_receive_copy_results_sync(iscsi, lun,
|
|
|
|
|
SCSI_COPY_RESULTS_OP_PARAMS, 0, 1024);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr, "XCOPY RECEIVE COPY RESULTS failed: %s\n",
|
|
|
|
|
iscsi_get_error(iscsi));
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opp = scsi_datain_unmarshall(task);
|
|
|
|
|
if (opp == NULL) {
|
|
|
|
|
fprintf(stderr, "failed to unmarshall XCOPY RCR datain blob\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opp->max_target_desc_count < 2) {
|
|
|
|
|
fprintf(stderr, "XCOPY max CSCD desc count %d too small\n",
|
|
|
|
|
opp->max_target_desc_count);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
if (opp->max_segment_desc_count < 1) {
|
|
|
|
|
fprintf(stderr, "XCOPY max segment desc count %d too small\n",
|
|
|
|
|
opp->max_segment_desc_count);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
io_segment_bytes = blocks_per_io * blocksize;
|
|
|
|
|
if (io_segment_bytes > opp->max_segment_length) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"%u bytes per I/O exceeds XCOPY max segment len %u\n",
|
|
|
|
|
io_segment_bytes, opp->max_segment_length);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
if (blocks_per_io > USHRT_MAX) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"%u blocks per I/O exceeds XCOPY field width max %u\n",
|
|
|
|
|
blocks_per_io, USHRT_MAX);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void readcap(struct iscsi_context *iscsi, int lun, int use_16,
|
|
|
|
|
int *_blocksize, uint64_t *_num_blocks)
|
|
|
|
|
{
|
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
|
|
|
|
|
if (use_16) {
|
|
|
|
|
struct scsi_readcapacity16 *rc16;
|
|
|
|
|
|
|
|
|
|
task = iscsi_readcapacity16_sync(iscsi, lun);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"failed to send readcapacity command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
rc16 = scsi_datain_unmarshall(task);
|
|
|
|
|
if (rc16 == NULL) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"failed to unmarshall readcapacity16 data\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
*_blocksize = rc16->block_length;
|
|
|
|
|
*_num_blocks = rc16->returned_lba + 1;
|
|
|
|
|
} else {
|
|
|
|
|
struct scsi_readcapacity10 *rc10;
|
|
|
|
|
|
|
|
|
|
task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"failed to send readcapacity command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
rc10 = scsi_datain_unmarshall(task);
|
|
|
|
|
if (rc10 == NULL) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"failed to unmarshall readcapacity10 data\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
*_blocksize = rc10->block_size;
|
|
|
|
|
*_num_blocks = rc10->lba;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
char *src_url = NULL;
|
|
|
|
|
char *dst_url = NULL;
|
|
|
|
|
struct iscsi_url *iscsi_url;
|
|
|
|
|
struct scsi_task *task;
|
|
|
|
|
struct scsi_readcapacity10 *rc10;
|
|
|
|
|
struct scsi_readcapacity16 *rc16;
|
|
|
|
|
int c;
|
|
|
|
|
struct pollfd pfd[2];
|
|
|
|
|
struct client client;
|
|
|
|
|
@@ -203,6 +537,7 @@ int main(int argc, char *argv[])
|
|
|
|
|
{"initiator-name", required_argument, NULL, 'i'},
|
|
|
|
|
{"progress", no_argument, NULL, 'p'},
|
|
|
|
|
{"16", no_argument, NULL, '6'},
|
|
|
|
|
{"xcopy", no_argument, NULL, 'x'},
|
|
|
|
|
{"max", required_argument, NULL, 'm'},
|
|
|
|
|
{"blocks", required_argument, NULL, 'b'},
|
|
|
|
|
{"ignore-errors", no_argument, NULL, 'n'},
|
|
|
|
|
@@ -214,6 +549,8 @@ int main(int argc, char *argv[])
|
|
|
|
|
|
|
|
|
|
while ((c = getopt_long(argc, argv, "d:s:i:m:b:p6n", long_options,
|
|
|
|
|
&option_index)) != -1) {
|
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
case 'd':
|
|
|
|
|
dst_url = optarg;
|
|
|
|
|
@@ -230,11 +567,24 @@ int main(int argc, char *argv[])
|
|
|
|
|
case '6':
|
|
|
|
|
client.use_16_for_rw = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'x':
|
|
|
|
|
client.use_xcopy = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 'm':
|
|
|
|
|
max_in_flight = atoi(optarg);
|
|
|
|
|
max_in_flight = strtoul(optarg, &endptr, 10);
|
|
|
|
|
if (*endptr != '\0' || max_in_flight == UINT_MAX) {
|
|
|
|
|
fprintf(stderr, "Invalid max in flight: %s\n",
|
|
|
|
|
optarg);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'b':
|
|
|
|
|
blocks_per_io = atoi(optarg);
|
|
|
|
|
blocks_per_io = strtoul(optarg, &endptr, 10);
|
|
|
|
|
if (*endptr != '\0' || blocks_per_io == UINT_MAX) {
|
|
|
|
|
fprintf(stderr, "Invalid blocks per I/O: %s\n",
|
|
|
|
|
optarg);
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'n':
|
|
|
|
|
client.ignore_errors = 1;
|
|
|
|
|
@@ -278,34 +628,14 @@ int main(int argc, char *argv[])
|
|
|
|
|
client.src_lun = iscsi_url->lun;
|
|
|
|
|
iscsi_destroy_url(iscsi_url);
|
|
|
|
|
|
|
|
|
|
if (client.use_16_for_rw) {
|
|
|
|
|
task = iscsi_readcapacity16_sync(client.src_iscsi, client.src_lun);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr, "failed to send readcapacity command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
rc16 = scsi_datain_unmarshall(task);
|
|
|
|
|
if (rc16 == NULL) {
|
|
|
|
|
fprintf(stderr, "failed to unmarshall readcapacity16 data\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
client.src_blocksize = rc16->block_length;
|
|
|
|
|
client.src_num_blocks = rc16->returned_lba + 1;
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
} else {
|
|
|
|
|
task = iscsi_readcapacity10_sync(client.src_iscsi, client.src_lun, 0, 0);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr, "failed to send readcapacity command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
rc10 = scsi_datain_unmarshall(task);
|
|
|
|
|
if (rc10 == NULL) {
|
|
|
|
|
fprintf(stderr, "failed to unmarshall readcapacity10 data\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
client.src_blocksize = rc10->block_size;
|
|
|
|
|
client.src_num_blocks = rc10->lba;
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
readcap(client.src_iscsi, client.src_lun, client.use_16_for_rw,
|
|
|
|
|
&client.src_blocksize, &client.src_num_blocks);
|
|
|
|
|
|
|
|
|
|
if (client.use_xcopy) {
|
|
|
|
|
cscd_ident_inq(client.src_iscsi, client.src_lun,
|
|
|
|
|
&client.src_tgt_desig);
|
|
|
|
|
cscd_param_check(client.src_iscsi, client.src_lun,
|
|
|
|
|
client.src_blocksize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client.dst_iscsi = iscsi_create_context(initiator);
|
|
|
|
|
@@ -330,34 +660,14 @@ int main(int argc, char *argv[])
|
|
|
|
|
client.dst_lun = iscsi_url->lun;
|
|
|
|
|
iscsi_destroy_url(iscsi_url);
|
|
|
|
|
|
|
|
|
|
if (client.use_16_for_rw) {
|
|
|
|
|
task = iscsi_readcapacity16_sync(client.dst_iscsi, client.dst_lun);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr, "failed to send readcapacity command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
rc16 = scsi_datain_unmarshall(task);
|
|
|
|
|
if (rc16 == NULL) {
|
|
|
|
|
fprintf(stderr, "failed to unmarshall readcapacity16 data\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
client.dst_blocksize = rc16->block_length;
|
|
|
|
|
client.dst_num_blocks = rc16->returned_lba + 1;
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
} else {
|
|
|
|
|
task = iscsi_readcapacity10_sync(client.dst_iscsi, client.dst_lun, 0, 0);
|
|
|
|
|
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
|
|
|
|
fprintf(stderr, "failed to send readcapacity command\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
rc10 = scsi_datain_unmarshall(task);
|
|
|
|
|
if (rc10 == NULL) {
|
|
|
|
|
fprintf(stderr, "failed to unmarshall readcapacity10 data\n");
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
client.dst_blocksize = rc10->block_size;
|
|
|
|
|
client.dst_num_blocks = rc10->lba;
|
|
|
|
|
scsi_free_scsi_task(task);
|
|
|
|
|
readcap(client.dst_iscsi, client.dst_lun, client.use_16_for_rw,
|
|
|
|
|
&client.dst_blocksize, &client.dst_num_blocks);
|
|
|
|
|
|
|
|
|
|
if (client.use_xcopy) {
|
|
|
|
|
cscd_ident_inq(client.dst_iscsi, client.dst_lun,
|
|
|
|
|
&client.dst_tgt_desig);
|
|
|
|
|
cscd_param_check(client.dst_iscsi, client.dst_lun,
|
|
|
|
|
client.dst_blocksize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client.src_blocksize != client.dst_blocksize) {
|
|
|
|
|
@@ -370,7 +680,11 @@ int main(int argc, char *argv[])
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fill_read_queue(&client);
|
|
|
|
|
if (client.use_xcopy) {
|
|
|
|
|
fill_xcopy_queue(&client);
|
|
|
|
|
} else {
|
|
|
|
|
fill_read_queue(&client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (client.finished == 0) {
|
|
|
|
|
pfd[0].fd = iscsi_get_fd(client.src_iscsi);
|
|
|
|
|
|