From 404f2392070e5b7f0e63c80a925313efe62edce3 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Thu, 14 Nov 2013 10:17:48 +0000 Subject: [PATCH] Improved reaction on Linux SG_IO transport problems --- cdrskin/cdrskin_timestamp.h | 2 +- libburn/libdax_msgs.h | 6 +- libburn/sg-linux.c | 203 ++++++++++++++++++++++++++++++++---- libburn/spc.c | 16 +++ libburn/spc.h | 3 + 5 files changed, 207 insertions(+), 23 deletions(-) diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index c2fcfd9..4e6373e 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2013.11.11.160915" +#define Cdrskin_timestamP "2013.11.14.101636" diff --git a/libburn/libdax_msgs.h b/libburn/libdax_msgs.h index e7837e6..cab1cee 100644 --- a/libburn/libdax_msgs.h +++ b/libburn/libdax_msgs.h @@ -520,7 +520,7 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff 0x0002014a (SORRY,HIGH) = Cannot read desired amount of data 0x0002014b (SORRY,HIGH) = Drive is already registered resp. scanned 0x0002014c (FATAL,HIGH) = Emulated drive caught in SCSI function - 0x0002014d (SORRY,HIGH) = Asynchromous SCSI error + 0x0002014d (SORRY,HIGH) = Asynchronous SCSI error 0x0002014f (SORRY,HIGH) = Timeout with asynchronous SCSI command 0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time 0x00020151 (FAILURE,HIGH) = Read attempt on write-only drive @@ -606,6 +606,10 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff 0x000201a2 (FAILURE,HIGH) = Error while writing to disk file 0x000201a3 (UPDATE,HIGH) = Progress message of burn_drive_extract_audio() 0x000201a4 (FAILURE,HIGH) = Failure to read audio sectors + 0x000201a5 (FAILURE,HIGH) = Asynchronous SCSI error + 0x000201a6 (FATAL,HIGH) = Lost connection to drive + 0x000201a7 (FAILURE,HIGH) = SCSI command yielded host problem + 0x000201a8 (FAILURE,HIGH) = SCSI command yielded driver problem libdax_audioxtr: diff --git a/libburn/sg-linux.c b/libburn/sg-linux.c index 3edd377..59e1516 100644 --- a/libburn/sg-linux.c +++ b/libburn/sg-linux.c @@ -1884,6 +1884,167 @@ int sg_release(struct burn_drive *d) return 0; } +/* @return -1= transport failed, give up drive + 0= transport failed, do not retry + 1= transport succeeded + 2- transport failed, please retry +*/ +static int evaluate_transport_success(struct burn_drive *d, struct command *c, + FILE *fp, + unsigned short host_status, + unsigned short driver_status) +{ + int ret, do_retry= 0, give_up_drive= 0; + char *msg = NULL, *host_problem, *driver_problem, *driver_sugg; + + BURN_ALLOC_MEM(msg, char, 161); + + if ((host_status == Libburn_sg_host_oK && + driver_status == Libburn_sg_driver_oK) || c->error) + {ret = 1; goto ex;} /* No transport problems */ + + /* See http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html */ + + switch(host_status) { + case 0x00: + host_problem = + "SG_ERR_DID_OK (No error)"; + break; case 0x01: + host_problem = + "SG_ERR_DID_NO_CONNECT (Could not connect before timeout period)"; + give_up_drive= 1; + break; case 0x02: + host_problem = + "SG_ERR_DID_BUS_BUSY (Bus stayed busy through time out period)"; + break; case 0x03: + host_problem = + "SG_ERR_DID_TIME_OUT (Timed out for miscellaneous reasons)"; + break; case 0x04: + host_problem = + "SG_ERR_DID_BAD_TARGET (Bad target, device not responding ?)"; + give_up_drive= 1; + break; case 0x05: + host_problem = + "SG_ERR_DID_ABORT (Told to abort)"; + break; case 0x06: + host_problem = + "SG_ERR_DID_PARITY (Parity error)"; + break; case 0x07: + host_problem = + "SG_ERR_DID_ERROR (Internal error detected in the host adapter)"; + give_up_drive= 1; + break; case 0x08: + host_problem = + "SG_ERR_DID_RESET (The SCSI bus or the device have been reset)"; + give_up_drive= 1; + break; case 0x09: + host_problem = + "SG_ERR_DID_BAD_INTR (Got an unexpected interrupt)"; + break; case 0x0a: + host_problem = + "SG_ERR_DID_PASSTHROUGH (Force command past mid-layer)"; + break; case 0x0b: + host_problem = + "SG_ERR_DID_SOFT_ERROR (The low level driver wants a retry)"; + do_retry = 1; + default: + host_problem = + "? (unknown host_status code)"; + } + if (host_status != Libburn_sg_host_oK) { + sprintf(msg, "SCSI command %2.2Xh yielded host problem: ", + (unsigned int) c->opcode[0]); + sprintf(msg+strlen(msg), "0x%x %s", + (unsigned int) host_status, host_problem); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x000201a7, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + sprintf(msg, "--- SG_IO: host_status= 0x%x %s", + (unsigned int) host_status, host_problem); + scsi_log_message(d, fp, msg, 0); + } + + switch (driver_status & 0x0f) { + case 0: + driver_problem = "SG_ERR_DRIVER_OK"; + break; case 1: + driver_problem = "SG_ERR_DRIVER_BUSY"; + break; case 2: + driver_problem = "SG_ERR_DRIVER_SOFT"; + break; case 3: + driver_problem = "SG_ERR_DRIVER_MEDIA"; + break; case 4: + driver_problem = "SG_ERR_DRIVER_ERROR"; + break; case 5: + driver_problem = "SG_ERR_DRIVER_INVALID"; + break; case 6: + driver_problem = "SG_ERR_DRIVER_TIMEOUT"; + break; case 7: + driver_problem = "SG_ERR_DRIVER_HARD"; + break; case 8: + driver_problem = "SG_ERR_DRIVER_SENSE"; + default: + driver_problem = "(unknown driver_status code)"; + } + switch (driver_status & 0xf0) { + case 0: + driver_sugg = "(no suggestion)"; + break; case 0x10: + driver_sugg = "SG_ERR_SUGGEST_RETRY"; + do_retry = 1; + break; case 0x20: + driver_sugg = "SG_ERR_SUGGEST_ABORT"; + give_up_drive= 1; + break; case 0x30: + driver_sugg = "SG_ERR_SUGGEST_REMAP"; + give_up_drive= 1; + break; case 0x40: + driver_sugg = "SG_ERR_SUGGEST_DIE"; + give_up_drive= 1; + break; case 0x80: + driver_sugg = "SG_ERR_SUGGEST_SENSE"; + default: + driver_sugg = "(unknown driver_status suggestion)"; + } + if (driver_status != Libburn_sg_driver_oK) { + sprintf(msg, "SCSI command %2.2Xh yielded driver problem: ", + (unsigned int) c->opcode[0]); + sprintf(msg+strlen(msg), "driver_status= 0x%x %s / %s", + (unsigned int) driver_status, + driver_problem, driver_sugg); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x000201a8, + LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + sprintf(msg, "--- SG_IO: driver_status= 0x%x %s / %s", + (unsigned int) driver_status, + driver_problem, driver_sugg); + scsi_log_message(d, fp, msg, 0); + } + + if (! do_retry) + c->error = 1; + ret = give_up_drive ? -1 : do_retry ? 2 : 0; +ex:; + BURN_FREE_MEM(msg); + return ret; +} + +static void react_on_drive_loss(struct burn_drive *d, struct command *c, + FILE *fp) +{ + sg_close_drive(d); + d->released = 1; + d->busy = BURN_DRIVE_IDLE; + d->cancel = 1; + c->error = 1; + libdax_msgs_submit(libdax_messenger, + d->global_index, 0x000201a6, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + "Lost connection to drive", 0, 0); + scsi_log_message(d, fp, "--- SG_IO: Gave up connection to drive", 0); +} /** Sends a SCSI command to the drive, receives reply and evaluates wether the command succeeded or shall be retried or finally failed. @@ -1923,8 +2084,6 @@ int sg_issue_command(struct burn_drive *d, struct command *c) "\n-----------------------------------------\n"); } } - if (burn_sg_log_scsi & 3) - scsi_log_cmd(c,fp,0); /* ts A61010 : with no fd there is no chance to send an ioctl */ if (d->fd < 0) { @@ -1932,8 +2091,11 @@ int sg_issue_command(struct burn_drive *d, struct command *c) {ret = 0; goto ex;} } + c->error = 0; memset(&s, 0, sizeof(sg_io_hdr_t)); + if (burn_sg_log_scsi & 3) + scsi_log_cmd(c,fp,0); s.interface_id = 'S'; @@ -2012,32 +2174,31 @@ int sg_issue_command(struct burn_drive *d, struct command *c) LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); - sg_close_drive(d); - d->released = 1; - d->busy = BURN_DRIVE_IDLE; - c->error = 1; + sprintf(msg, "--- SG_IO: return= -1 , "); + sprintf(msg + strlen(msg), "errno= %d , ", errno); + sprintf(msg + strlen(msg), + "host_status= 0x%x , driver_status= 0x%x", + (unsigned int) s.host_status, + (unsigned int) s.driver_status); + scsi_log_message(d, fp, msg, 0); + react_on_drive_loss(d, c, fp); {ret = -1; goto ex;} } done = scsi_eval_cmd_outcome(d, c, fp, s.sbp, s.sb_len_wr, start_time, s.timeout, i, 0); if (d->cancel) - done = 1; + break; + ret = evaluate_transport_success(d, c, fp, + s.host_status, s.driver_status); + if (ret == -1) + react_on_drive_loss(d, c, fp); + if (ret <= 0) + {ret = -1; goto ex;} + if (ret != 2 || d->cancel) + break; + /* loop for retry */; } - if (s.host_status != Libburn_sg_host_oK || - (s.driver_status != Libburn_sg_driver_oK && !c->error)) { - sprintf(msg, - "SCSI command %2.2Xh indicates host or driver error:", - (unsigned int) c->opcode[0]); - sprintf(msg+strlen(msg), - " host_status= %xh , driver_status= %xh", - (unsigned int) s.host_status, - (unsigned int) s.driver_status); - libdax_msgs_submit(libdax_messenger, d->global_index, - 0x0002013b, - LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, - msg, 0, 0); - } ret = 1; ex:; BURN_FREE_MEM(msg); diff --git a/libburn/spc.c b/libburn/spc.c index 3418056..5e6a606 100644 --- a/libburn/spc.c +++ b/libburn/spc.c @@ -1747,6 +1747,22 @@ int scsi_log_err(struct burn_drive *d, struct command *c, return ret; } +/* ts B31112 */ +int scsi_log_message(struct burn_drive *d, void *fp_in, char * msg, int flag) +{ + int ret; + FILE *fp = fp_in; + + if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) { + fprintf(fp, "%s\n", msg); + if (burn_sg_log_scsi & 4) + fflush(fp); + } + if (fp == stderr || !(burn_sg_log_scsi & 2)) + return 1; + ret = scsi_log_message(d, stderr, msg, flag); + return ret; +} /* ts B00808 */ /* diff --git a/libburn/spc.h b/libburn/spc.h index e1f21ec..81491ed 100644 --- a/libburn/spc.h +++ b/libburn/spc.h @@ -98,6 +98,9 @@ int scsi_log_err(struct burn_drive *d, struct command *c, void *fp, unsigned char sense[18], int sense_len, int flag); +/* ts B31112 */ +int scsi_log_message(struct burn_drive *d, void *fp, char * msg, int flag); + /* ts B00728 */ int spc_decode_sense(unsigned char *sense, int senselen, int *key, int *asc, int *ascq);