FIO_SG_WRITE_VERIFY,
FIO_SG_WRITE_SAME,
FIO_SG_WRITE_SAME_NDOB,
+ FIO_SG_WRITE_STREAM,
FIO_SG_VERIFY_BYTCHK_00,
FIO_SG_VERIFY_BYTCHK_01,
FIO_SG_VERIFY_BYTCHK_11,
unsigned int readfua;
unsigned int writefua;
unsigned int write_mode;
+ uint16_t stream_id;
};
static struct fio_option options[] = {
.oval = FIO_SG_WRITE,
.help = "Issue standard SCSI WRITE commands",
},
- { .ival = "verify",
+ { .ival = "write_and_verify",
.oval = FIO_SG_WRITE_VERIFY,
.help = "Issue SCSI WRITE AND VERIFY commands",
},
- { .ival = "same",
+ { .ival = "verify",
+ .oval = FIO_SG_WRITE_VERIFY,
+ .help = "Issue SCSI WRITE AND VERIFY commands. This "
+ "option is deprecated. Use write_and_verify instead.",
+ },
+ { .ival = "write_same",
.oval = FIO_SG_WRITE_SAME,
.help = "Issue SCSI WRITE SAME commands",
},
+ { .ival = "same",
+ .oval = FIO_SG_WRITE_SAME,
+ .help = "Issue SCSI WRITE SAME commands. This "
+ "option is deprecated. Use write_same instead.",
+ },
{ .ival = "write_same_ndob",
.oval = FIO_SG_WRITE_SAME_NDOB,
.help = "Issue SCSI WRITE SAME(16) commands with NDOB flag set",
.oval = FIO_SG_VERIFY_BYTCHK_11,
.help = "Issue SCSI VERIFY commands with BYTCHK set to 11",
},
+ { .ival = "write_stream",
+ .oval = FIO_SG_WRITE_STREAM,
+ .help = "Issue SCSI WRITE STREAM(16) commands",
+ },
},
.category = FIO_OPT_C_ENGINE,
.group = FIO_OPT_G_SG,
},
+ {
+ .name = "stream_id",
+ .lname = "stream id for WRITE STREAM(16) commands",
+ .type = FIO_OPT_INT,
+ .off1 = offsetof(struct sg_options, stream_id),
+ .help = "Stream ID for WRITE STREAM(16) commands",
+ .def = "0",
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_SG,
+ },
{
.name = NULL,
},
#endif
};
+static inline uint16_t sgio_get_be16(uint8_t *buf)
+{
+ return be16_to_cpu(*((uint16_t *) buf));
+}
+
static inline uint32_t sgio_get_be32(uint8_t *buf)
{
return be32_to_cpu(*((uint32_t *) buf));
hdr->cmdp[1] |= 0x1; // no data output buffer
hdr->dxfer_len = 0;
break;
+ case FIO_SG_WRITE_STREAM:
+ hdr->cmdp[0] = 0x9a; // write stream (16)
+ if (o->writefua)
+ hdr->cmdp[1] |= 0x08;
+ sgio_set_be64(lba, &hdr->cmdp[2]);
+ sgio_set_be16((uint16_t) io_u->file->engine_pos, &hdr->cmdp[10]);
+ sgio_set_be16((uint16_t) nr_blocks, &hdr->cmdp[12]);
+ break;
case FIO_SG_VERIFY_BYTCHK_00:
if (lba < MAX_10B_LBA)
hdr->cmdp[0] = 0x2f; // VERIFY(10)
break;
};
- fio_sgio_rw_lba(hdr, lba, nr_blocks,
- o->write_mode == FIO_SG_WRITE_SAME_NDOB);
+ if (o->write_mode != FIO_SG_WRITE_STREAM)
+ fio_sgio_rw_lba(hdr, lba, nr_blocks,
+ o->write_mode == FIO_SG_WRITE_SAME_NDOB);
} else if (io_u->ddir == DDIR_TRIM) {
struct sgio_trim *st;
return 0;
}
+static int fio_sgio_stream_control(struct fio_file *f, bool open_stream, uint16_t *stream_id)
+{
+ struct sg_io_hdr hdr;
+ unsigned char cmd[16];
+ unsigned char sb[64];
+ unsigned char buf[8];
+ int ret;
+
+ memset(&hdr, 0, sizeof(hdr));
+ memset(cmd, 0, sizeof(cmd));
+ memset(sb, 0, sizeof(sb));
+ memset(buf, 0, sizeof(buf));
+
+ hdr.interface_id = 'S';
+ hdr.cmdp = cmd;
+ hdr.cmd_len = 16;
+ hdr.sbp = sb;
+ hdr.mx_sb_len = sizeof(sb);
+ hdr.timeout = SCSI_TIMEOUT_MS;
+ hdr.cmdp[0] = 0x9e;
+ hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ hdr.dxferp = buf;
+ hdr.dxfer_len = sizeof(buf);
+ sgio_set_be32(sizeof(buf), &hdr.cmdp[10]);
+
+ if (open_stream)
+ hdr.cmdp[1] = 0x34;
+ else {
+ hdr.cmdp[1] = 0x54;
+ sgio_set_be16(*stream_id, &hdr.cmdp[4]);
+ }
+
+ ret = ioctl(f->fd, SG_IO, &hdr);
+
+ if (ret < 0)
+ return ret;
+
+ if (hdr.info & SG_INFO_CHECK)
+ return 1;
+
+ if (open_stream) {
+ *stream_id = sgio_get_be16(&buf[4]);
+ dprint(FD_FILE, "sgio_stream_control: opened stream %u\n", (unsigned int) *stream_id);
+ assert(*stream_id != 0);
+ } else
+ dprint(FD_FILE, "sgio_stream_control: closed stream %u\n", (unsigned int) *stream_id);
+
+ return 0;
+}
+
static int fio_sgio_open(struct thread_data *td, struct fio_file *f)
{
struct sgio_data *sd = td->io_ops_data;
+ struct sg_options *o = td->eo;
int ret;
ret = generic_open_file(td, f);
return ret;
}
+ if (o->write_mode == FIO_SG_WRITE_STREAM) {
+ if (o->stream_id)
+ f->engine_pos = o->stream_id;
+ else {
+ ret = fio_sgio_stream_control(f, true, (uint16_t *) &f->engine_pos);
+ if (ret)
+ return ret;
+ }
+ }
+
return 0;
}
+int fio_sgio_close(struct thread_data *td, struct fio_file *f)
+{
+ struct sg_options *o = td->eo;
+ int ret;
+
+ if (!o->stream_id && o->write_mode == FIO_SG_WRITE_STREAM) {
+ ret = fio_sgio_stream_control(f, false, (uint16_t *) &f->engine_pos);
+ if (ret)
+ return ret;
+ }
+
+ return generic_close_file(td, f);
+}
+
/*
* Build an error string with details about the driver, host or scsi
* error contained in the sg header Caller will use as necessary.
.event = fio_sgio_event,
.cleanup = fio_sgio_cleanup,
.open_file = fio_sgio_open,
- .close_file = generic_close_file,
+ .close_file = fio_sgio_close,
.get_file_size = fio_sgio_get_file_size,
.flags = FIO_SYNCIO | FIO_RAWIO,
.options = options,