#ifdef FIO_HAVE_SGIO
+#ifndef SGV4_FLAG_HIPRI
+#define SGV4_FLAG_HIPRI 0x800
+#endif
+
enum {
FIO_SG_WRITE = 1,
- FIO_SG_WRITE_VERIFY = 2,
- FIO_SG_WRITE_SAME = 3
+ 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,
};
struct sg_options {
void *pad;
+ unsigned int hipri;
unsigned int readfua;
unsigned int writefua;
unsigned int write_mode;
+ uint16_t stream_id;
};
static struct fio_option options[] = {
+ {
+ .name = "hipri",
+ .lname = "High Priority",
+ .type = FIO_OPT_STR_SET,
+ .off1 = offsetof(struct sg_options, hipri),
+ .help = "Use polled IO completions",
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_SG,
+ },
{
.name = "readfua",
.lname = "sg engine read fua flag support",
.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",
+ },
+ { .ival = "verify_bytchk_00",
+ .oval = FIO_SG_VERIFY_BYTCHK_00,
+ .help = "Issue SCSI VERIFY commands with BYTCHK set to 00",
+ },
+ { .ival = "verify_bytchk_01",
+ .oval = FIO_SG_VERIFY_BYTCHK_01,
+ .help = "Issue SCSI VERIFY commands with BYTCHK set to 01",
+ },
+ { .ival = "verify_bytchk_11",
+ .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,
},
};
struct sgio_trim {
- char *unmap_param;
+ uint8_t *unmap_param;
unsigned int unmap_range_count;
struct io_u **trim_io_us;
};
#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));
+}
+
+static inline uint64_t sgio_get_be64(uint8_t *buf)
+{
+ return be64_to_cpu(*((uint64_t *) buf));
+}
+
+static inline void sgio_set_be16(uint16_t val, uint8_t *buf)
+{
+ uint16_t t = cpu_to_be16(val);
+
+ memcpy(buf, &t, sizeof(uint16_t));
+}
+
+static inline void sgio_set_be32(uint32_t val, uint8_t *buf)
+{
+ uint32_t t = cpu_to_be32(val);
+
+ memcpy(buf, &t, sizeof(uint32_t));
+}
+
+static inline void sgio_set_be64(uint64_t val, uint8_t *buf)
+{
+ uint64_t t = cpu_to_be64(val);
+
+ memcpy(buf, &t, sizeof(uint64_t));
+}
+
static inline bool sgio_unbuffered(struct thread_data *td)
{
return (td->o.odirect || td->o.sync_io);
return FIO_Q_COMPLETED;
}
-static enum fio_q_status fio_sgio_rw_doio(struct fio_file *f,
+static enum fio_q_status fio_sgio_rw_doio(struct thread_data *td,
+ struct fio_file *f,
struct io_u *io_u, int do_sync)
{
struct sg_io_hdr *hdr = &io_u->hdr;
return ret;
if (do_sync) {
- ret = read(f->fd, hdr, sizeof(*hdr));
- if (ret < 0)
- return ret;
+ /*
+ * We can't just read back the first command that completes
+ * and assume it's the one we need, it could be any command
+ * that is inflight.
+ */
+ do {
+ struct io_u *__io_u;
- /* record if an io error occurred */
- if (hdr->info & SG_INFO_CHECK)
- io_u->error = EIO;
+ ret = read(f->fd, hdr, sizeof(*hdr));
+ if (ret < 0)
+ return ret;
+
+ __io_u = hdr->usr_ptr;
+
+ /* record if an io error occurred */
+ if (hdr->info & SG_INFO_CHECK)
+ __io_u->error = EIO;
+
+ if (__io_u == io_u)
+ break;
+
+ if (io_u_sync_complete(td, __io_u))
+ break;
+
+ } while (1);
return FIO_Q_COMPLETED;
}
if (f->filetype == FIO_TYPE_BLOCK) {
ret = fio_sgio_ioctl_doio(td, f, io_u);
- td_verror(td, io_u->error, __func__);
+ if (io_u->error)
+ td_verror(td, io_u->error, __func__);
} else {
- ret = fio_sgio_rw_doio(f, io_u, do_sync);
- if (do_sync)
+ ret = fio_sgio_rw_doio(td, f, io_u, do_sync);
+ if (io_u->error && do_sync)
td_verror(td, io_u->error, __func__);
}
}
static void fio_sgio_rw_lba(struct sg_io_hdr *hdr, unsigned long long lba,
- unsigned long long nr_blocks)
+ unsigned long long nr_blocks, bool override16)
{
- if (lba < MAX_10B_LBA) {
- hdr->cmdp[2] = (unsigned char) ((lba >> 24) & 0xff);
- hdr->cmdp[3] = (unsigned char) ((lba >> 16) & 0xff);
- hdr->cmdp[4] = (unsigned char) ((lba >> 8) & 0xff);
- hdr->cmdp[5] = (unsigned char) (lba & 0xff);
- hdr->cmdp[7] = (unsigned char) ((nr_blocks >> 8) & 0xff);
- hdr->cmdp[8] = (unsigned char) (nr_blocks & 0xff);
+ if (lba < MAX_10B_LBA && !override16) {
+ sgio_set_be32((uint32_t) lba, &hdr->cmdp[2]);
+ sgio_set_be16((uint16_t) nr_blocks, &hdr->cmdp[7]);
} else {
- hdr->cmdp[2] = (unsigned char) ((lba >> 56) & 0xff);
- hdr->cmdp[3] = (unsigned char) ((lba >> 48) & 0xff);
- hdr->cmdp[4] = (unsigned char) ((lba >> 40) & 0xff);
- hdr->cmdp[5] = (unsigned char) ((lba >> 32) & 0xff);
- hdr->cmdp[6] = (unsigned char) ((lba >> 24) & 0xff);
- hdr->cmdp[7] = (unsigned char) ((lba >> 16) & 0xff);
- hdr->cmdp[8] = (unsigned char) ((lba >> 8) & 0xff);
- hdr->cmdp[9] = (unsigned char) (lba & 0xff);
- hdr->cmdp[10] = (unsigned char) ((nr_blocks >> 32) & 0xff);
- hdr->cmdp[11] = (unsigned char) ((nr_blocks >> 16) & 0xff);
- hdr->cmdp[12] = (unsigned char) ((nr_blocks >> 8) & 0xff);
- hdr->cmdp[13] = (unsigned char) (nr_blocks & 0xff);
+ sgio_set_be64(lba, &hdr->cmdp[2]);
+ sgio_set_be32((uint32_t) nr_blocks, &hdr->cmdp[10]);
}
return;
else
hdr->cmdp[0] = 0x88; // read(16)
+ if (o->hipri)
+ hdr->flags |= SGV4_FLAG_HIPRI;
if (o->readfua)
hdr->cmdp[1] |= 0x08;
- fio_sgio_rw_lba(hdr, lba, nr_blocks);
+ fio_sgio_rw_lba(hdr, lba, nr_blocks, false);
} else if (io_u->ddir == DDIR_WRITE) {
sgio_hdr_init(sd, hdr, io_u, 1);
hdr->cmdp[0] = 0x2a; // write(10)
else
hdr->cmdp[0] = 0x8a; // write(16)
+ if (o->hipri)
+ hdr->flags |= SGV4_FLAG_HIPRI;
if (o->writefua)
hdr->cmdp[1] |= 0x08;
break;
else
hdr->cmdp[0] = 0x93; // write same(16)
break;
+ case FIO_SG_WRITE_SAME_NDOB:
+ hdr->cmdp[0] = 0x93; // write same(16)
+ 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)
+ else
+ hdr->cmdp[0] = 0x8f; // VERIFY(16)
+ hdr->dxfer_len = 0;
+ break;
+ case FIO_SG_VERIFY_BYTCHK_01:
+ if (lba < MAX_10B_LBA)
+ hdr->cmdp[0] = 0x2f; // VERIFY(10)
+ else
+ hdr->cmdp[0] = 0x8f; // VERIFY(16)
+ hdr->cmdp[1] |= 0x02; // BYTCHK = 01b
+ break;
+ case FIO_SG_VERIFY_BYTCHK_11:
+ if (lba < MAX_10B_LBA)
+ hdr->cmdp[0] = 0x2f; // VERIFY(10)
+ else
+ hdr->cmdp[0] = 0x8f; // VERIFY(16)
+ hdr->cmdp[1] |= 0x06; // BYTCHK = 11b
+ hdr->dxfer_len = sd->bs;
+ break;
};
- fio_sgio_rw_lba(hdr, lba, nr_blocks);
+ 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;
#endif
offset = 8 + 16 * st->unmap_range_count;
- st->unmap_param[offset] = (unsigned char) ((lba >> 56) & 0xff);
- st->unmap_param[offset+1] = (unsigned char) ((lba >> 48) & 0xff);
- st->unmap_param[offset+2] = (unsigned char) ((lba >> 40) & 0xff);
- st->unmap_param[offset+3] = (unsigned char) ((lba >> 32) & 0xff);
- st->unmap_param[offset+4] = (unsigned char) ((lba >> 24) & 0xff);
- st->unmap_param[offset+5] = (unsigned char) ((lba >> 16) & 0xff);
- st->unmap_param[offset+6] = (unsigned char) ((lba >> 8) & 0xff);
- st->unmap_param[offset+7] = (unsigned char) (lba & 0xff);
- st->unmap_param[offset+8] = (unsigned char) ((nr_blocks >> 32) & 0xff);
- st->unmap_param[offset+9] = (unsigned char) ((nr_blocks >> 16) & 0xff);
- st->unmap_param[offset+10] = (unsigned char) ((nr_blocks >> 8) & 0xff);
- st->unmap_param[offset+11] = (unsigned char) (nr_blocks & 0xff);
+ sgio_set_be64(lba, &st->unmap_param[offset]);
+ sgio_set_be32((uint32_t) nr_blocks, &st->unmap_param[offset + 8]);
st->unmap_range_count++;
static void fio_sgio_unmap_setup(struct sg_io_hdr *hdr, struct sgio_trim *st)
{
- hdr->dxfer_len = st->unmap_range_count * 16 + 8;
- hdr->cmdp[7] = (unsigned char) (((st->unmap_range_count * 16 + 8) >> 8) & 0xff);
- hdr->cmdp[8] = (unsigned char) ((st->unmap_range_count * 16 + 8) & 0xff);
+ uint16_t cnt = st->unmap_range_count * 16;
- st->unmap_param[0] = (unsigned char) (((16 * st->unmap_range_count + 6) >> 8) & 0xff);
- st->unmap_param[1] = (unsigned char) ((16 * st->unmap_range_count + 6) & 0xff);
- st->unmap_param[2] = (unsigned char) (((16 * st->unmap_range_count) >> 8) & 0xff);
- st->unmap_param[3] = (unsigned char) ((16 * st->unmap_range_count) & 0xff);
+ hdr->dxfer_len = cnt + 8;
+ sgio_set_be16(cnt + 8, &hdr->cmdp[7]);
+ sgio_set_be16(cnt + 6, st->unmap_param);
+ sgio_set_be16(cnt, &st->unmap_param[2]);
return;
}
sd->current_queue = -1;
- ret = fio_sgio_rw_doio(io_u->file, io_u, 0);
+ ret = fio_sgio_rw_doio(td, io_u->file, io_u, 0);
- if (ret < 0)
- for (i = 0; i < st->unmap_range_count; i++)
- st->trim_io_us[i]->error = errno;
- else if (hdr->status)
- for (i = 0; i < st->unmap_range_count; i++) {
- st->trim_io_us[i]->resid = hdr->resid;
- st->trim_io_us[i]->error = EIO;
+ if (ret < 0 || hdr->status) {
+ int error;
+
+ if (ret < 0)
+ error = errno;
+ else {
+ error = EIO;
+ ret = -EIO;
}
- else {
- if (fio_fill_issue_time(td)) {
- fio_gettime(&now, NULL);
- for (i = 0; i < st->unmap_range_count; i++) {
- struct io_u *io_u = st->trim_io_us[i];
- memcpy(&io_u->issue_time, &now, sizeof(now));
- io_u_queued(td, io_u);
- }
+ for (i = 0; i < st->unmap_range_count; i++) {
+ st->trim_io_us[i]->error = error;
+ clear_io_u(td, st->trim_io_us[i]);
+ if (hdr->status)
+ st->trim_io_us[i]->resid = hdr->resid;
}
- io_u_mark_submit(td, st->unmap_range_count);
+
+ td_verror(td, error, "xfer");
+ return ret;
}
- if (io_u->error) {
- td_verror(td, io_u->error, "xfer");
- return 0;
+ if (fio_fill_issue_time(td)) {
+ fio_gettime(&now, NULL);
+ for (i = 0; i < st->unmap_range_count; i++) {
+ memcpy(&st->trim_io_us[i]->issue_time, &now, sizeof(now));
+ io_u_queued(td, io_u);
+ }
}
+ io_u_mark_submit(td, st->unmap_range_count);
- if (ret == FIO_Q_QUEUED)
- return 0;
- else
- return ret;
+ return 0;
}
static struct io_u *fio_sgio_event(struct thread_data *td, int event)
* io_u structures, which are not initialized until later.
*/
struct sg_io_hdr hdr;
+ unsigned long long hlba;
+ unsigned int blksz = 0;
unsigned char cmd[16];
unsigned char sb[64];
unsigned char buf[32]; // read capacity return
return ret;
}
- *bs = ((unsigned long) buf[4] << 24) | ((unsigned long) buf[5] << 16) |
- ((unsigned long) buf[6] << 8) | (unsigned long) buf[7];
- *max_lba = ((unsigned long) buf[0] << 24) | ((unsigned long) buf[1] << 16) |
- ((unsigned long) buf[2] << 8) | (unsigned long) buf[3];
+ if (hdr.info & SG_INFO_CHECK) {
+ /* RCAP(10) might be unsupported by device. Force RCAP(16) */
+ hlba = MAX_10B_LBA;
+ } else {
+ blksz = sgio_get_be32(&buf[4]);
+ hlba = sgio_get_be32(buf);
+ }
/*
* If max lba masked by MAX_10B_LBA equals MAX_10B_LBA,
* then need to retry with 16 byte Read Capacity command.
*/
- if (*max_lba == MAX_10B_LBA) {
+ if (hlba == MAX_10B_LBA) {
hdr.cmd_len = 16;
hdr.cmdp[0] = 0x9e; // service action
hdr.cmdp[1] = 0x10; // Read Capacity(16)
- hdr.cmdp[10] = (unsigned char) ((sizeof(buf) >> 24) & 0xff);
- hdr.cmdp[11] = (unsigned char) ((sizeof(buf) >> 16) & 0xff);
- hdr.cmdp[12] = (unsigned char) ((sizeof(buf) >> 8) & 0xff);
- hdr.cmdp[13] = (unsigned char) (sizeof(buf) & 0xff);
+ sgio_set_be32(sizeof(buf), &hdr.cmdp[10]);
hdr.dxfer_direction = SG_DXFER_FROM_DEV;
hdr.dxferp = buf;
if (hdr.info & SG_INFO_CHECK)
td_verror(td, EIO, "fio_sgio_read_capacity");
- *bs = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
- *max_lba = ((unsigned long long)buf[0] << 56) |
- ((unsigned long long)buf[1] << 48) |
- ((unsigned long long)buf[2] << 40) |
- ((unsigned long long)buf[3] << 32) |
- ((unsigned long long)buf[4] << 24) |
- ((unsigned long long)buf[5] << 16) |
- ((unsigned long long)buf[6] << 8) |
- (unsigned long long)buf[7];
+ blksz = sgio_get_be32(&buf[8]);
+ hlba = sgio_get_be64(buf);
+ }
+
+ if (blksz) {
+ *bs = blksz;
+ *max_lba = hlba;
+ ret = 0;
+ } else {
+ ret = EIO;
}
close(fd);
- return 0;
+ return ret;
}
static void fio_sgio_cleanup(struct thread_data *td)
{
struct sgio_data *sd;
struct sgio_trim *st;
+ struct sg_io_hdr *h3p;
int i;
sd = calloc(1, sizeof(*sd));
#ifdef FIO_SGIO_DEBUG
sd->trim_queue_map = calloc(td->o.iodepth, sizeof(int));
#endif
- for (i = 0; i < td->o.iodepth; i++) {
+ for (i = 0, h3p = sd->sgbuf; i < td->o.iodepth; i++, ++h3p) {
sd->trim_queues[i] = calloc(1, sizeof(struct sgio_trim));
st = sd->trim_queues[i];
st->unmap_param = calloc(td->o.iodepth + 1, sizeof(char[16]));
st->unmap_range_count = 0;
st->trim_io_us = calloc(td->o.iodepth, sizeof(struct io_u *));
+ h3p->interface_id = 'S';
}
td->io_ops_data = sd;
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);
if (sd && !sd->type_checked && fio_sgio_type_check(td, f)) {
ret = generic_close_file(td, f);
- return 1;
+ 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.
}
if (!(hdr->info & SG_INFO_CHECK) && !strlen(msg))
- strncpy(msg, "SG Driver did not report a Host, Driver or Device check",
- MAXERRDETAIL - 1);
+ snprintf(msg, MAXERRDETAIL, "%s",
+ "SG Driver did not report a Host, Driver or Device check");
return msg;
}
.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,