+/*
+ * Build an error string with details about the driver, host or scsi
+ * error contained in the sg header Caller will use as necessary.
+ */
+static char *fio_sgio_errdetails(struct io_u *io_u)
+{
+ struct sg_io_hdr *hdr = &io_u->hdr;
+#define MAXERRDETAIL 1024
+#define MAXMSGCHUNK 128
+ 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) {
+ if (hdr->host_status) {
+ snprintf(msgchunk, MAXMSGCHUNK, "SG Host Status: 0x%02x; ", hdr->host_status);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ switch (hdr->host_status) {
+ case 0x01:
+ strlcat(msg, "SG_ERR_DID_NO_CONNECT", MAXERRDETAIL);
+ break;
+ case 0x02:
+ strlcat(msg, "SG_ERR_DID_BUS_BUSY", MAXERRDETAIL);
+ break;
+ case 0x03:
+ strlcat(msg, "SG_ERR_DID_TIME_OUT", MAXERRDETAIL);
+ break;
+ case 0x04:
+ strlcat(msg, "SG_ERR_DID_BAD_TARGET", MAXERRDETAIL);
+ break;
+ case 0x05:
+ strlcat(msg, "SG_ERR_DID_ABORT", MAXERRDETAIL);
+ break;
+ case 0x06:
+ strlcat(msg, "SG_ERR_DID_PARITY", MAXERRDETAIL);
+ break;
+ case 0x07:
+ strlcat(msg, "SG_ERR_DID_ERROR (internal error)", MAXERRDETAIL);
+ break;
+ case 0x08:
+ strlcat(msg, "SG_ERR_DID_RESET", MAXERRDETAIL);
+ break;
+ case 0x09:
+ strlcat(msg, "SG_ERR_DID_BAD_INTR (unexpected)", MAXERRDETAIL);
+ break;
+ case 0x0a:
+ strlcat(msg, "SG_ERR_DID_PASSTHROUGH", MAXERRDETAIL);
+ break;
+ case 0x0b:
+ strlcat(msg, "SG_ERR_DID_SOFT_ERROR (driver retry?)", MAXERRDETAIL);
+ break;
+ case 0x0c:
+ strlcat(msg, "SG_ERR_DID_IMM_RETRY", MAXERRDETAIL);
+ break;
+ 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;
+ }
+ strlcat(msg, ". ", MAXERRDETAIL);
+ }
+ if (hdr->driver_status) {
+ snprintf(msgchunk, MAXMSGCHUNK, "SG Driver Status: 0x%02x; ", hdr->driver_status);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ switch (hdr->driver_status & 0x0F) {
+ case 0x01:
+ strlcat(msg, "SG_ERR_DRIVER_BUSY", MAXERRDETAIL);
+ break;
+ case 0x02:
+ strlcat(msg, "SG_ERR_DRIVER_SOFT", MAXERRDETAIL);
+ break;
+ case 0x03:
+ strlcat(msg, "SG_ERR_DRIVER_MEDIA", MAXERRDETAIL);
+ break;
+ case 0x04:
+ strlcat(msg, "SG_ERR_DRIVER_ERROR", MAXERRDETAIL);
+ break;
+ case 0x05:
+ strlcat(msg, "SG_ERR_DRIVER_INVALID", MAXERRDETAIL);
+ break;
+ case 0x06:
+ strlcat(msg, "SG_ERR_DRIVER_TIMEOUT", MAXERRDETAIL);
+ break;
+ case 0x07:
+ strlcat(msg, "SG_ERR_DRIVER_HARD", MAXERRDETAIL);
+ break;
+ case 0x08:
+ strlcat(msg, "SG_ERR_DRIVER_SENSE", MAXERRDETAIL);
+ break;
+ default:
+ strlcat(msg, "Unknown", MAXERRDETAIL);
+ break;
+ }
+ strlcat(msg, "; ", MAXERRDETAIL);
+ switch (hdr->driver_status & 0xF0) {
+ case 0x10:
+ strlcat(msg, "SG_ERR_SUGGEST_RETRY", MAXERRDETAIL);
+ break;
+ case 0x20:
+ strlcat(msg, "SG_ERR_SUGGEST_ABORT", MAXERRDETAIL);
+ break;
+ case 0x30:
+ strlcat(msg, "SG_ERR_SUGGEST_REMAP", MAXERRDETAIL);
+ break;
+ case 0x40:
+ strlcat(msg, "SG_ERR_SUGGEST_DIE", MAXERRDETAIL);
+ break;
+ case 0x80:
+ strlcat(msg, "SG_ERR_SUGGEST_SENSE", MAXERRDETAIL);
+ break;
+ }
+ strlcat(msg, ". ", MAXERRDETAIL);
+ }
+ if (hdr->status) {
+ snprintf(msgchunk, MAXMSGCHUNK, "SG SCSI Status: 0x%02x; ", hdr->status);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ // SCSI 3 status codes
+ switch (hdr->status) {
+ case 0x02:
+ strlcat(msg, "CHECK_CONDITION", MAXERRDETAIL);
+ break;
+ case 0x04:
+ strlcat(msg, "CONDITION_MET", MAXERRDETAIL);
+ break;
+ case 0x08:
+ strlcat(msg, "BUSY", MAXERRDETAIL);
+ break;
+ case 0x10:
+ strlcat(msg, "INTERMEDIATE", MAXERRDETAIL);
+ break;
+ case 0x14:
+ strlcat(msg, "INTERMEDIATE_CONDITION_MET", MAXERRDETAIL);
+ break;
+ case 0x18:
+ strlcat(msg, "RESERVATION_CONFLICT", MAXERRDETAIL);
+ break;
+ case 0x22:
+ strlcat(msg, "COMMAND_TERMINATED", MAXERRDETAIL);
+ break;
+ case 0x28:
+ strlcat(msg, "TASK_SET_FULL", MAXERRDETAIL);
+ break;
+ case 0x30:
+ strlcat(msg, "ACA_ACTIVE", MAXERRDETAIL);
+ break;
+ case 0x40:
+ strlcat(msg, "TASK_ABORTED", MAXERRDETAIL);
+ break;
+ default:
+ strlcat(msg, "Unknown", MAXERRDETAIL);
+ break;
+ }
+ strlcat(msg, ". ", MAXERRDETAIL);
+ }
+ if (hdr->sb_len_wr) {
+ snprintf(msgchunk, MAXMSGCHUNK, "Sense Data (%d bytes):", hdr->sb_len_wr);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ for (i = 0; i < hdr->sb_len_wr; i++) {
+ snprintf(msgchunk, MAXMSGCHUNK, " %02x", hdr->sbp[i]);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ }
+ strlcat(msg, ". ", MAXERRDETAIL);
+ }
+ 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);
+ }
+ if (hdr->cmdp) {
+ strlcat(msg, "cdb:", MAXERRDETAIL);
+ for (i = 0; i < hdr->cmd_len; i++) {
+ snprintf(msgchunk, MAXMSGCHUNK, " %02x", hdr->cmdp[i]);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ }
+ strlcat(msg, ". ", MAXERRDETAIL);
+ if (io_u->ddir == DDIR_TRIM) {
+ unsigned char *param_list = hdr->dxferp;
+ strlcat(msg, "dxferp:", MAXERRDETAIL);
+ for (i = 0; i < hdr->dxfer_len; i++) {
+ snprintf(msgchunk, MAXMSGCHUNK, " %02x", param_list[i]);
+ strlcat(msg, msgchunk, MAXERRDETAIL);
+ }
+ strlcat(msg, ". ", MAXERRDETAIL);
+ }
+ }
+ }
+
+ if (!(hdr->info & SG_INFO_CHECK) && !strlen(msg))
+ strncpy(msg, "SG Driver did not report a Host, Driver or Device check",
+ MAXERRDETAIL - 1);
+
+ return msg;
+}
+
+/*
+ * get max file size from read capacity.
+ */
+static int fio_sgio_get_file_size(struct thread_data *td, struct fio_file *f)
+{
+ /*
+ * get_file_size is being called even before sgio_init is
+ * called, so none of the sg_io structures are
+ * initialized in the thread_data yet. So we need to do the
+ * ReadCapacity without any of those helpers. One of the effects
+ * is that ReadCapacity may get called 4 times on each open:
+ * readcap(10) followed by readcap(16) if needed - just to get
+ * the file size after the init occurs - it will be called
+ * again when "type_check" is called during structure
+ * initialization I'm not sure how to prevent this little
+ * inefficiency.
+ */
+ unsigned int bs = 0;
+ unsigned long long max_lba = 0;
+ int ret;
+
+ 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");
+ log_err("ioengine sg unable to successfully execute read capacity to get block size and maximum lba\n");
+ return 1;
+ }
+
+ f->real_file_size = (max_lba + 1) * bs;
+ fio_file_set_size_known(f);
+ return 0;
+}
+
+