#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
-#include <assert.h>
-#include <sys/poll.h>
+#include <poll.h>
#include "../fio.h"
+#include "../optgroup.h"
#ifdef FIO_HAVE_SGIO
+enum {
+ FIO_SG_WRITE = 1,
+ FIO_SG_WRITE_VERIFY = 2,
+ FIO_SG_WRITE_SAME = 3
+};
+
+struct sg_options {
+ void *pad;
+ unsigned int readfua;
+ unsigned int writefua;
+ unsigned int write_mode;
+};
+
+static struct fio_option options[] = {
+ {
+ .name = "readfua",
+ .lname = "sg engine read fua flag support",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct sg_options, readfua),
+ .help = "Set FUA flag (force unit access) for all Read operations",
+ .def = "0",
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_SG,
+ },
+ {
+ .name = "writefua",
+ .lname = "sg engine write fua flag support",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct sg_options, writefua),
+ .help = "Set FUA flag (force unit access) for all Write operations",
+ .def = "0",
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_SG,
+ },
+ {
+ .name = "sg_write_mode",
+ .lname = "specify sg write mode",
+ .type = FIO_OPT_STR,
+ .off1 = offsetof(struct sg_options, write_mode),
+ .help = "Specify SCSI WRITE mode",
+ .def = "write",
+ .posval = {
+ { .ival = "write",
+ .oval = FIO_SG_WRITE,
+ .help = "Issue standard SCSI WRITE commands",
+ },
+ { .ival = "verify",
+ .oval = FIO_SG_WRITE_VERIFY,
+ .help = "Issue SCSI WRITE AND VERIFY commands",
+ },
+ { .ival = "same",
+ .oval = FIO_SG_WRITE_SAME,
+ .help = "Issue SCSI WRITE SAME commands",
+ },
+ },
+ .category = FIO_OPT_C_ENGINE,
+ .group = FIO_OPT_G_SG,
+ },
+ {
+ .name = NULL,
+ },
+};
+
#define MAX_10B_LBA 0xFFFFFFFFULL
#define SCSI_TIMEOUT_MS 30000 // 30 second timeout; currently no method to override
#define MAX_SB 64 // sense block maximum return size
struct sgio_cmd {
- unsigned char cdb[16]; // increase to support 16 byte commands
+ unsigned char cdb[16]; // enhanced from 10 to support 16 byte commands
unsigned char sb[MAX_SB]; // add sense block to commands
int nr;
};
int *fd_flags;
void *sgbuf;
unsigned int bs;
- long long max_lba;
int type_checked;
};
}
while (left) {
- void *p;
+ char *p;
dprint(FD_IO, "sgio_getevents: sd %p: left=%d\n", sd, left);
if (hdr->info & SG_INFO_CHECK) {
struct io_u *io_u;
io_u = (struct io_u *)(hdr->usr_ptr);
- memcpy((void*)&(io_u->hdr), (void*)hdr, sizeof(struct sg_io_hdr));
+ memcpy(&io_u->hdr, hdr, sizeof(struct sg_io_hdr));
sd->events[i]->error = EIO;
}
}
return r;
}
-static int fio_sgio_ioctl_doio(struct thread_data *td,
- struct fio_file *f, struct io_u *io_u)
+static enum fio_q_status fio_sgio_ioctl_doio(struct thread_data *td,
+ struct fio_file *f,
+ struct io_u *io_u)
{
struct sgio_data *sd = td->io_ops_data;
struct sg_io_hdr *hdr = &io_u->hdr;
struct fio_file *f = io_u->file;
int ret;
- if (f->filetype == FIO_TYPE_BD) {
+ if (f->filetype == FIO_TYPE_BLOCK) {
ret = fio_sgio_ioctl_doio(td, f, io_u);
- td->error = io_u->error;
+ td_verror(td, io_u->error, __func__);
} else {
ret = fio_sgio_rw_doio(f, io_u, do_sync);
if (do_sync)
- td->error = io_u->error;
+ td_verror(td, io_u->error, __func__);
}
return ret;
static int fio_sgio_prep(struct thread_data *td, struct io_u *io_u)
{
struct sg_io_hdr *hdr = &io_u->hdr;
+ struct sg_options *o = td->eo;
struct sgio_data *sd = td->io_ops_data;
long long nr_blocks, lba;
hdr->cmdp[0] = 0x28; // read(10)
else
hdr->cmdp[0] = 0x88; // read(16)
+
+ if (o->readfua)
+ hdr->cmdp[1] |= 0x08;
+
} else if (io_u->ddir == DDIR_WRITE) {
sgio_hdr_init(sd, hdr, io_u, 1);
hdr->dxfer_direction = SG_DXFER_TO_DEV;
- if (lba < MAX_10B_LBA)
- hdr->cmdp[0] = 0x2a; // write(10)
- else
- hdr->cmdp[0] = 0x8a; // write(16)
+ switch(o->write_mode) {
+ case FIO_SG_WRITE:
+ if (lba < MAX_10B_LBA)
+ hdr->cmdp[0] = 0x2a; // write(10)
+ else
+ hdr->cmdp[0] = 0x8a; // write(16)
+ if (o->writefua)
+ hdr->cmdp[1] |= 0x08;
+ break;
+ case FIO_SG_WRITE_VERIFY:
+ if (lba < MAX_10B_LBA)
+ hdr->cmdp[0] = 0x2e; // write and verify(10)
+ else
+ hdr->cmdp[0] = 0x8e; // write and verify(16)
+ break;
+ // BYTCHK is disabled by virtue of the memset in sgio_hdr_init
+ case FIO_SG_WRITE_SAME:
+ hdr->dxfer_len = sd->bs;
+ if (lba < MAX_10B_LBA)
+ hdr->cmdp[0] = 0x41; // write same(10)
+ else
+ hdr->cmdp[0] = 0x93; // write same(16)
+ break;
+ };
} else {
sgio_hdr_init(sd, hdr, io_u, 0);
hdr->dxfer_direction = SG_DXFER_NONE;
* blocks on medium.
*/
if (hdr->dxfer_direction != SG_DXFER_NONE) {
-
if (lba < MAX_10B_LBA) {
hdr->cmdp[2] = (unsigned char) ((lba >> 24) & 0xff);
hdr->cmdp[3] = (unsigned char) ((lba >> 16) & 0xff);
return 0;
}
-static int fio_sgio_queue(struct thread_data *td, struct io_u *io_u)
+static enum fio_q_status fio_sgio_queue(struct thread_data *td,
+ struct io_u *io_u)
{
struct sg_io_hdr *hdr = &io_u->hdr;
int ret, do_sync = 0;
return ret;
}
- *bs = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
- *max_lba = ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]) & 0x00000000FFFFFFFFULL; // for some reason max_lba is being sign extended even though unsigned.
-
+ *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 max lba is 0xFFFFFFFF, then need to retry with
- * 16 byteread capacity
+ * 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) {
hdr.cmd_len = 16;
- hdr.cmdp[0] = 0x9e; // Read Capacity(16)
- hdr.cmdp[1] = 0x10; // service action
+ 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);
unsigned int bs = 0;
unsigned long long max_lba = 0;
-
- if (f->filetype == FIO_TYPE_BD) {
+ if (f->filetype == FIO_TYPE_BLOCK) {
if (ioctl(f->fd, BLKSSZGET, &bs) < 0) {
td_verror(td, errno, "ioctl");
return 1;
}
} else {
td_verror(td, EINVAL, "wrong file type");
- log_err("ioengine sg only works on block devices\n");
+ log_err("ioengine sg only works on block or character devices\n");
return 1;
}
sd->bs = bs;
// Determine size of commands needed based on max_lba
- sd->max_lba = max_lba;
- if (max_lba > MAX_10B_LBA) {
- dprint(FD_IO, "sgio_type_check: using 16 byte operations: max_lba = 0x%016llx\n", max_lba);
+ if (max_lba >= MAX_10B_LBA) {
+ dprint(FD_IO, "sgio_type_check: using 16 byte read/write "
+ "commands for lba above 0x%016llx/0x%016llx\n",
+ MAX_10B_LBA, max_lba);
}
-
- if (f->filetype == FIO_TYPE_BD) {
+ if (f->filetype == FIO_TYPE_BLOCK) {
td->io_ops->getevents = NULL;
td->io_ops->event = NULL;
}
struct sg_io_hdr *hdr = &io_u->hdr;
#define MAXERRDETAIL 1024
#define MAXMSGCHUNK 128
- char *msg, msgchunk[MAXMSGCHUNK], *ret = NULL;
+ char *msg, msgchunk[MAXMSGCHUNK];
int i;
msg = calloc(1, MAXERRDETAIL);
+ strcpy(msg, "");
/*
* can't seem to find sg_err.h, so I'll just echo the define values
* so others can search on internet to find clearer clues of meaning.
*/
if (hdr->info & SG_INFO_CHECK) {
- ret = msg;
if (hdr->host_status) {
snprintf(msgchunk, MAXMSGCHUNK, "SG Host Status: 0x%02x; ", hdr->host_status);
strlcat(msg, msgchunk, MAXERRDETAIL);
case 0x0d:
strlcat(msg, "SG_ERR_DID_REQUEUE", MAXERRDETAIL);
break;
+ case 0x0e:
+ strlcat(msg, "SG_ERR_DID_TRANSPORT_DISRUPTED", MAXERRDETAIL);
+ break;
+ case 0x0f:
+ strlcat(msg, "SG_ERR_DID_TRANSPORT_FAILFAST", MAXERRDETAIL);
+ break;
+ case 0x10:
+ strlcat(msg, "SG_ERR_DID_TARGET_FAILURE", MAXERRDETAIL);
+ break;
+ case 0x11:
+ strlcat(msg, "SG_ERR_DID_NEXUS_FAILURE", MAXERRDETAIL);
+ break;
+ case 0x12:
+ strlcat(msg, "SG_ERR_DID_ALLOC_FAILURE", MAXERRDETAIL);
+ break;
+ case 0x13:
+ strlcat(msg, "SG_ERR_DID_MEDIUM_ERROR", MAXERRDETAIL);
+ break;
default:
strlcat(msg, "Unknown", MAXERRDETAIL);
break;
if (hdr->resid != 0) {
snprintf(msgchunk, MAXMSGCHUNK, "SG Driver: %d bytes out of %d not transferred. ", hdr->resid, hdr->dxfer_len);
strlcat(msg, msgchunk, MAXERRDETAIL);
- ret = msg;
}
}
- if (!ret)
- ret = strdup("SG Driver did not report a Host, Driver or Device check");
+ if (!(hdr->info & SG_INFO_CHECK) && !strlen(msg))
+ strncpy(msg, "SG Driver did not report a Host, Driver or Device check",
+ MAXERRDETAIL - 1);
- return ret;
+ return msg;
}
/*
if (fio_file_size_known(f))
return 0;
+ if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR) {
+ td_verror(td, EINVAL, "wrong file type");
+ log_err("ioengine sg only works on block or character devices\n");
+ return 1;
+ }
+
ret = fio_sgio_read_capacity(td, &bs, &max_lba);
if (ret ) {
td_verror(td, td->error, "fio_sgio_read_capacity");
.cleanup = fio_sgio_cleanup,
.open_file = fio_sgio_open,
.close_file = generic_close_file,
- .get_file_size = fio_sgio_get_file_size, // generic_get_file_size
+ .get_file_size = fio_sgio_get_file_size,
.flags = FIO_SYNCIO | FIO_RAWIO,
+ .options = options,
+ .option_struct_size = sizeof(struct sg_options)
};
#else /* FIO_HAVE_SGIO */