Commit cb406274 authored by Kai Mäkisara's avatar Kai Mäkisara Committed by James Bottomley

[PATCH] SCSI tape descriptor based sense data support

The patch at the end of this message converts the SCSI tape driver to support
also descriptor based sense data. Test for deferred sense data have been added
in a couple of places and the EOM tests have been unified. Some tests have been
simplified but the patch is not meant to change the current behavior. The
patch is against 2.6.11-rc4 and has been tested to some extent.

The patch also includes the msleep_interruptible change from from kernel
janitors.

Thanks to Doug Gilbert for doing a first version of this sense data
conversion.
Signed-off-by: default avatarKai Makisara <kai.makisara@kolumbus.fi>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 178a856d
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
Michael Schaefer, J"org Weule, and Eric Youngdale. Michael Schaefer, J"org Weule, and Eric Youngdale.
Copyright 1992 - 2004 Kai Makisara Copyright 1992 - 2005 Kai Makisara
email Kai.Makisara@kolumbus.fi email Kai.Makisara@kolumbus.fi
Some small formal changes - aeb, 950809 Some small formal changes - aeb, 950809
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
*/ */
static char *verstr = "20041025"; static char *verstr = "20050213";
#include <linux/module.h> #include <linux/module.h>
...@@ -144,7 +144,7 @@ static char *st_formats[] = { ...@@ -144,7 +144,7 @@ static char *st_formats[] = {
#error "Buffer size should not exceed (2 << 24 - 1) bytes!" #error "Buffer size should not exceed (2 << 24 - 1) bytes!"
#endif #endif
DEB( static int debugging = DEBUG; ) static int debugging = DEBUG;
#define MAX_RETRIES 0 #define MAX_RETRIES 0
#define MAX_WRITE_RETRIES 0 #define MAX_WRITE_RETRIES 0
...@@ -262,25 +262,57 @@ static inline char *tape_name(struct scsi_tape *tape) ...@@ -262,25 +262,57 @@ static inline char *tape_name(struct scsi_tape *tape)
return tape->disk->disk_name; return tape->disk->disk_name;
} }
static void st_analyze_sense(struct scsi_request *SRpnt, struct st_cmdstatus *s)
{
const u8 *ucp;
const u8 *sense = SRpnt->sr_sense_buffer;
s->have_sense = scsi_request_normalize_sense(SRpnt, &s->sense_hdr);
if (s->have_sense) {
s->remainder_valid =
scsi_get_sense_info_fld(sense, SCSI_SENSE_BUFFERSIZE, &s->uremainder64);
switch (sense[0] & 0x7f) {
case 0x71:
s->deferred = 1;
case 0x70:
s->fixed_format = 1;
s->flags = sense[2] & 0xe0;
break;
case 0x73:
s->deferred = 1;
case 0x72:
s->fixed_format = 0;
ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4);
s->flags = ucp ? (ucp[3] & 0xe0) : 0;
break;
default:
s->flags = 0;
}
}
}
/* Convert the result to success code */ /* Convert the result to success code */
static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt)
{ {
int result = SRpnt->sr_result; int result = SRpnt->sr_result;
unsigned char *sense = SRpnt->sr_sense_buffer, scode; u8 scode;
DEB(const char *stp;) DEB(const char *stp;)
char *name = tape_name(STp); char *name = tape_name(STp);
struct st_cmdstatus *cmdstatp;
if (!result) { if (!result)
sense[0] = 0; /* We don't have sense data if this byte is zero */
return 0; return 0;
}
if ((driver_byte(result) & DRIVER_MASK) == DRIVER_SENSE) cmdstatp = &STp->buffer->cmdstat;
scode = sense[2] & 0x0f; st_analyze_sense(STp->buffer->last_SRpnt, cmdstatp);
else {
sense[0] = 0; if (cmdstatp->have_sense)
scode = STp->buffer->cmdstat.sense_hdr.sense_key;
else
scode = 0; scode = 0;
}
DEB( DEB(
if (debugging) { if (debugging) {
...@@ -289,29 +321,30 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) ...@@ -289,29 +321,30 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt)
SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2], SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2],
SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5], SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5],
SRpnt->sr_bufflen); SRpnt->sr_bufflen);
if (driver_byte(result) & DRIVER_SENSE) if (cmdstatp->have_sense)
scsi_print_req_sense("st", SRpnt); scsi_print_req_sense("st", SRpnt);
} else ) /* end DEB */ } ) /* end DEB */
if (!(driver_byte(result) & DRIVER_SENSE) || if (!debugging) { /* Abnormal conditions for tape */
((sense[0] & 0x70) == 0x70 && if (!cmdstatp->have_sense)
scode != NO_SENSE &&
scode != RECOVERED_ERROR &&
/* scode != UNIT_ATTENTION && */
scode != BLANK_CHECK &&
scode != VOLUME_OVERFLOW &&
SRpnt->sr_cmnd[0] != MODE_SENSE &&
SRpnt->sr_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
if (driver_byte(result) & DRIVER_SENSE) {
printk(KERN_WARNING "%s: Error with sense data: ", name);
scsi_print_req_sense("st", SRpnt);
} else
printk(KERN_WARNING printk(KERN_WARNING
"%s: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n", "%s: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
name, result, suggestion(result), name, result, suggestion(result),
driver_byte(result) & DRIVER_MASK, host_byte(result)); driver_byte(result) & DRIVER_MASK, host_byte(result));
else if (cmdstatp->have_sense &&
scode != NO_SENSE &&
scode != RECOVERED_ERROR &&
/* scode != UNIT_ATTENTION && */
scode != BLANK_CHECK &&
scode != VOLUME_OVERFLOW &&
SRpnt->sr_cmnd[0] != MODE_SENSE &&
SRpnt->sr_cmnd[0] != TEST_UNIT_READY) {
printk(KERN_WARNING "%s: Error with sense data: ", name);
scsi_print_req_sense("st", SRpnt);
}
} }
if (STp->cln_mode >= EXTENDED_SENSE_START) { if (cmdstatp->fixed_format &&
STp->cln_mode >= EXTENDED_SENSE_START) { /* Only fixed format sense */
if (STp->cln_sense_value) if (STp->cln_sense_value)
STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] & STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] &
STp->cln_sense_mask) == STp->cln_sense_value); STp->cln_sense_mask) == STp->cln_sense_value);
...@@ -319,12 +352,13 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) ...@@ -319,12 +352,13 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt)
STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] & STp->cleaning_req |= ((SRpnt->sr_sense_buffer[STp->cln_mode] &
STp->cln_sense_mask) != 0); STp->cln_sense_mask) != 0);
} }
if (sense[12] == 0 && sense[13] == 0x17) /* ASC and ASCQ => cleaning requested */ if (cmdstatp->have_sense &&
STp->cleaning_req = 1; cmdstatp->sense_hdr.asc == 0 && cmdstatp->sense_hdr.ascq == 0x17)
STp->cleaning_req = 1; /* ASC and ASCQ => cleaning requested */
STp->pos_unknown |= STp->device->was_reset; STp->pos_unknown |= STp->device->was_reset;
if ((sense[0] & 0x70) == 0x70 && if (cmdstatp->have_sense &&
scode == RECOVERED_ERROR scode == RECOVERED_ERROR
#if ST_RECOVERED_WRITE_FATAL #if ST_RECOVERED_WRITE_FATAL
&& SRpnt->sr_cmnd[0] != WRITE_6 && SRpnt->sr_cmnd[0] != WRITE_6
...@@ -346,7 +380,7 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) ...@@ -346,7 +380,7 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt)
STp->recover_count); STp->recover_count);
} ) /* end DEB */ } ) /* end DEB */
if ((sense[2] & 0xe0) == 0) if (cmdstatp->flags == 0)
return 0; return 0;
} }
return (-EIO); return (-EIO);
...@@ -356,28 +390,10 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) ...@@ -356,28 +390,10 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt)
/* Wakeup from interrupt */ /* Wakeup from interrupt */
static void st_sleep_done(struct scsi_cmnd * SCpnt) static void st_sleep_done(struct scsi_cmnd * SCpnt)
{ {
int remainder;
struct scsi_tape *STp = container_of(SCpnt->request->rq_disk->private_data, struct scsi_tape *STp = container_of(SCpnt->request->rq_disk->private_data,
struct scsi_tape, driver); struct scsi_tape, driver);
if ((STp->buffer)->writing && (STp->buffer)->cmdstat.midlevel_result = SCpnt->result;
(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x40)) {
/* EOM at write-behind, has all been written? */
if ((SCpnt->sense_buffer[0] & 0x80) != 0)
remainder = (SCpnt->sense_buffer[3] << 24) |
(SCpnt->sense_buffer[4] << 16) |
(SCpnt->sense_buffer[5] << 8) |
SCpnt->sense_buffer[6];
else
remainder = 0;
if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
remainder > 0)
(STp->buffer)->midlevel_result = SCpnt->result; /* Error */
else
(STp->buffer)->midlevel_result = INT_MAX; /* OK */
} else
(STp->buffer)->midlevel_result = SCpnt->result;
SCpnt->request->rq_status = RQ_SCSI_DONE; SCpnt->request->rq_status = RQ_SCSI_DONE;
(STp->buffer)->last_SRpnt = SCpnt->sc_request; (STp->buffer)->last_SRpnt = SCpnt->sc_request;
DEB( STp->write_pending = 0; ) DEB( STp->write_pending = 0; )
...@@ -421,6 +437,7 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c ...@@ -421,6 +437,7 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c
SRpnt->sr_request->waiting = &(STp->wait); SRpnt->sr_request->waiting = &(STp->wait);
SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; SRpnt->sr_request->rq_status = RQ_SCSI_BUSY;
SRpnt->sr_request->rq_disk = STp->disk; SRpnt->sr_request->rq_disk = STp->disk;
STp->buffer->cmdstat.have_sense = 0;
scsi_do_req(SRpnt, (void *) cmd, bp, bytes, scsi_do_req(SRpnt, (void *) cmd, bp, bytes,
st_sleep_done, timeout, retries); st_sleep_done, timeout, retries);
...@@ -434,13 +451,20 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c ...@@ -434,13 +451,20 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c
} }
/* Handle the write-behind checking (downs the semaphore) */ /* Handle the write-behind checking (waits for completion). Returns -ENOSPC if
static void write_behind_check(struct scsi_tape * STp) write has been correct but EOM early warning reached, -EIO if write ended in
error or zero if write successful. Asynchronous writes are used only in
variable block mode. */
static int write_behind_check(struct scsi_tape * STp)
{ {
int retval = 0;
struct st_buffer *STbuffer; struct st_buffer *STbuffer;
struct st_partstat *STps; struct st_partstat *STps;
struct st_cmdstatus *cmdstatp;
STbuffer = STp->buffer; STbuffer = STp->buffer;
if (!STbuffer->writing)
return 0;
DEB( DEB(
if (STp->write_pending) if (STp->write_pending)
...@@ -463,9 +487,29 @@ static void write_behind_check(struct scsi_tape * STp) ...@@ -463,9 +487,29 @@ static void write_behind_check(struct scsi_tape * STp)
else else
STps->drv_block += STbuffer->writing / STp->block_size; STps->drv_block += STbuffer->writing / STp->block_size;
} }
cmdstatp = &STbuffer->cmdstat;
if (STbuffer->syscall_result) {
retval = -EIO;
if (cmdstatp->have_sense && !cmdstatp->deferred &&
(cmdstatp->flags & SENSE_EOM) &&
(cmdstatp->sense_hdr.sense_key == NO_SENSE ||
cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR)) {
/* EOM at write-behind, has all data been written? */
if (!cmdstatp->remainder_valid ||
cmdstatp->uremainder64 == 0)
retval = -ENOSPC;
}
if (retval == -EIO)
STps->drv_block = -1;
}
STbuffer->writing = 0; STbuffer->writing = 0;
return; DEB(if (debugging && retval)
printk(ST_DEB_MSG "%s: Async write error %x, return value %d.\n",
tape_name(STp), STbuffer->cmdstat.midlevel_result, retval);) /* end DEB */
return retval;
} }
...@@ -496,7 +540,7 @@ static int cross_eof(struct scsi_tape * STp, int forward) ...@@ -496,7 +540,7 @@ static int cross_eof(struct scsi_tape * STp, int forward)
scsi_release_request(SRpnt); scsi_release_request(SRpnt);
SRpnt = NULL; SRpnt = NULL;
if ((STp->buffer)->midlevel_result != 0) if ((STp->buffer)->cmdstat.midlevel_result != 0)
printk(KERN_ERR "%s: Stepping over filemark %s failed.\n", printk(KERN_ERR "%s: Stepping over filemark %s failed.\n",
tape_name(STp), forward ? "forward" : "backward"); tape_name(STp), forward ? "forward" : "backward");
...@@ -513,19 +557,9 @@ static int flush_write_buffer(struct scsi_tape * STp) ...@@ -513,19 +557,9 @@ static int flush_write_buffer(struct scsi_tape * STp)
struct scsi_request *SRpnt; struct scsi_request *SRpnt;
struct st_partstat *STps; struct st_partstat *STps;
if ((STp->buffer)->writing) { result = write_behind_check(STp);
write_behind_check(STp); if (result)
if ((STp->buffer)->syscall_result) { return result;
DEBC(printk(ST_DEB_MSG
"%s: Async write error (flush) %x.\n",
tape_name(STp), (STp->buffer)->midlevel_result))
if ((STp->buffer)->midlevel_result == INT_MAX)
return (-ENOSPC);
return (-EIO);
}
}
if (STp->block_size == 0)
return 0;
result = 0; result = 0;
if (STp->dirty == 1) { if (STp->dirty == 1) {
...@@ -553,18 +587,25 @@ static int flush_write_buffer(struct scsi_tape * STp) ...@@ -553,18 +587,25 @@ static int flush_write_buffer(struct scsi_tape * STp)
STps = &(STp->ps[STp->partition]); STps = &(STp->ps[STp->partition]);
if ((STp->buffer)->syscall_result != 0) { if ((STp->buffer)->syscall_result != 0) {
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
(SRpnt->sr_sense_buffer[2] & 0x40) &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == NO_SENSE) { if (cmdstatp->have_sense && !cmdstatp->deferred &&
(cmdstatp->flags & SENSE_EOM) &&
(cmdstatp->sense_hdr.sense_key == NO_SENSE ||
cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
(!cmdstatp->remainder_valid ||
cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */
STp->dirty = 0; STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0; (STp->buffer)->buffer_bytes = 0;
if (STps->drv_block >= 0)
STps->drv_block += blks;
result = (-ENOSPC); result = (-ENOSPC);
} else { } else {
printk(KERN_ERR "%s: Error on flush.\n", printk(KERN_ERR "%s: Error on flush.\n",
tape_name(STp)); tape_name(STp));
STps->drv_block = (-1);
result = (-EIO); result = (-EIO);
} }
STps->drv_block = (-1);
} else { } else {
if (STps->drv_block >= 0) if (STps->drv_block >= 0)
STps->drv_block += blks; STps->drv_block += blks;
...@@ -728,6 +769,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait) ...@@ -728,6 +769,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
int retval = CHKRES_READY, new_session = 0; int retval = CHKRES_READY, new_session = 0;
unsigned char cmd[MAX_COMMAND_SIZE]; unsigned char cmd[MAX_COMMAND_SIZE];
struct scsi_request *SRpnt = NULL; struct scsi_request *SRpnt = NULL;
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
max_wait = do_wait ? ST_BLOCK_SECONDS : 0; max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
...@@ -742,9 +784,9 @@ static int test_ready(struct scsi_tape *STp, int do_wait) ...@@ -742,9 +784,9 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
break; break;
} }
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) { if (cmdstatp->have_sense) {
scode = (SRpnt->sr_sense_buffer[2] & 0x0f); scode = cmdstatp->sense_hdr.sense_key;
if (scode == UNIT_ATTENTION) { /* New media? */ if (scode == UNIT_ATTENTION) { /* New media? */
new_session = 1; new_session = 1;
...@@ -760,9 +802,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait) ...@@ -760,9 +802,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
if (scode == NOT_READY) { if (scode == NOT_READY) {
if (waits < max_wait) { if (waits < max_wait) {
set_current_state(TASK_INTERRUPTIBLE); if (msleep_interruptible(1000)) {
schedule_timeout(HZ);
if (signal_pending(current)) {
retval = (-EINTR); retval = (-EINTR);
break; break;
} }
...@@ -771,7 +811,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait) ...@@ -771,7 +811,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
} }
else { else {
if ((STp->device)->scsi_level >= SCSI_2 && if ((STp->device)->scsi_level >= SCSI_2 &&
SRpnt->sr_sense_buffer[12] == 0x3a) /* Check ASC */ cmdstatp->sense_hdr.asc == 0x3a) /* Check ASC */
retval = CHKRES_NO_TAPE; retval = CHKRES_NO_TAPE;
else else
retval = CHKRES_NOT_READY; retval = CHKRES_NOT_READY;
...@@ -877,7 +917,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) ...@@ -877,7 +917,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
goto err_out; goto err_out;
} }
if (!SRpnt->sr_result && !SRpnt->sr_sense_buffer[0]) { if (!SRpnt->sr_result && !STp->buffer->cmdstat.have_sense) {
STp->max_block = ((STp->buffer)->b_data[1] << 16) | STp->max_block = ((STp->buffer)->b_data[1] << 16) |
((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3]; ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
STp->min_block = ((STp->buffer)->b_data[4] << 8) | STp->min_block = ((STp->buffer)->b_data[4] << 8) |
...@@ -1108,6 +1148,7 @@ static int st_flush(struct file *filp) ...@@ -1108,6 +1148,7 @@ static int st_flush(struct file *filp)
name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable)); name, STp->nbr_requests, STp->nbr_dio, STp->nbr_pages, STp->nbr_combinable));
if (STps->rw == ST_WRITING && !STp->pos_unknown) { if (STps->rw == ST_WRITING && !STp->pos_unknown) {
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
DEBC(printk(ST_DEB_MSG "%s: File length %lld bytes.\n", DEBC(printk(ST_DEB_MSG "%s: File length %lld bytes.\n",
name, (long long)filp->f_pos); name, (long long)filp->f_pos);
...@@ -1126,20 +1167,13 @@ static int st_flush(struct file *filp) ...@@ -1126,20 +1167,13 @@ static int st_flush(struct file *filp)
goto out; goto out;
} }
if ((STp->buffer)->syscall_result != 0 && if (STp->buffer->syscall_result == 0 ||
((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 || (cmdstatp->have_sense && !cmdstatp->deferred &&
(SRpnt->sr_sense_buffer[2] & 0x4f) != 0x40 || (cmdstatp->flags & SENSE_EOM) &&
((SRpnt->sr_sense_buffer[0] & 0x80) != 0 && (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
(SRpnt->sr_sense_buffer[3] | SRpnt->sr_sense_buffer[4] | cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
SRpnt->sr_sense_buffer[5] | (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0))) {
SRpnt->sr_sense_buffer[6]) != 0))) { /* Write successful at EOM */
/* Filter out successful write at EOM */
scsi_release_request(SRpnt);
SRpnt = NULL;
printk(KERN_ERR "%s: Error on write filemark.\n", name);
if (result == 0)
result = (-EIO);
} else {
scsi_release_request(SRpnt); scsi_release_request(SRpnt);
SRpnt = NULL; SRpnt = NULL;
if (STps->drv_file >= 0) if (STps->drv_file >= 0)
...@@ -1149,6 +1183,13 @@ static int st_flush(struct file *filp) ...@@ -1149,6 +1183,13 @@ static int st_flush(struct file *filp)
cross_eof(STp, 0); cross_eof(STp, 0);
STps->eof = ST_FM; STps->eof = ST_FM;
} }
else { /* Write error */
scsi_release_request(SRpnt);
SRpnt = NULL;
printk(KERN_ERR "%s: Error on write filemark.\n", name);
if (result == 0)
result = (-EIO);
}
DEBC(printk(ST_DEB_MSG "%s: Buffer flushed, %d EOF(s) written\n", DEBC(printk(ST_DEB_MSG "%s: Buffer flushed, %d EOF(s) written\n",
name, cmd[4])); name, cmd[4]));
...@@ -1410,16 +1451,12 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) ...@@ -1410,16 +1451,12 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
} }
STbp = STp->buffer; STbp = STp->buffer;
if (STbp->writing) { i = write_behind_check(STp);
write_behind_check(STp); if (i) {
if (STbp->syscall_result) { if (i == -ENOSPC)
DEBC(printk(ST_DEB_MSG "%s: Async write error (write) %x.\n", STps->eof = ST_EOM_OK;
name, STbp->midlevel_result)); else
if (STbp->midlevel_result == INT_MAX) STps->eof = ST_EOM_ERROR;
STps->eof = ST_EOM_OK;
else
STps->eof = ST_EOM_ERROR;
}
} }
if (STps->eof == ST_EOM_OK) { if (STps->eof == ST_EOM_OK) {
...@@ -1523,15 +1560,13 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) ...@@ -1523,15 +1560,13 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
} }
if (STbp->syscall_result != 0) { if (STbp->syscall_result != 0) {
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
DEBC(printk(ST_DEB_MSG "%s: Error on write:\n", name)); DEBC(printk(ST_DEB_MSG "%s: Error on write:\n", name));
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && if (cmdstatp->have_sense && (cmdstatp->flags & SENSE_EOM)) {
(SRpnt->sr_sense_buffer[2] & 0x40)) { scode = cmdstatp->sense_hdr.sense_key;
scode = SRpnt->sr_sense_buffer[2] & 0x0f; if (cmdstatp->remainder_valid)
if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0) undone = (int)cmdstatp->uremainder64;
undone = (SRpnt->sr_sense_buffer[3] << 24) |
(SRpnt->sr_sense_buffer[4] << 16) |
(SRpnt->sr_sense_buffer[5] << 8) |
SRpnt->sr_sense_buffer[6];
else if (STp->block_size == 0 && else if (STp->block_size == 0 &&
scode == VOLUME_OVERFLOW) scode == VOLUME_OVERFLOW)
undone = transfer; undone = transfer;
...@@ -1556,11 +1591,11 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) ...@@ -1556,11 +1591,11 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
retval = (-ENOSPC); /* EOM within current request */ retval = (-ENOSPC); /* EOM within current request */
DEBC(printk(ST_DEB_MSG DEBC(printk(ST_DEB_MSG
"%s: EOM with %d bytes unwritten.\n", "%s: EOM with %d bytes unwritten.\n",
name, count)); name, (int)count));
} else { } else {
/* EOT within data buffered earlier (possible only /* EOT within data buffered earlier (possible only
in fixed block mode without direct i/o) */ in fixed block mode without direct i/o) */
if (!retry_eot && (SRpnt->sr_sense_buffer[0] & 1) == 0 && if (!retry_eot && !cmdstatp->deferred &&
(scode == NO_SENSE || scode == RECOVERED_ERROR)) { (scode == NO_SENSE || scode == RECOVERED_ERROR)) {
move_buffer_data(STp->buffer, transfer - undone); move_buffer_data(STp->buffer, transfer - undone);
retry_eot = 1; retry_eot = 1;
...@@ -1690,6 +1725,8 @@ static long read_tape(struct scsi_tape *STp, long count, ...@@ -1690,6 +1725,8 @@ static long read_tape(struct scsi_tape *STp, long count,
/* Something to check */ /* Something to check */
if (STbp->syscall_result) { if (STbp->syscall_result) {
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
retval = 1; retval = 1;
DEBC(printk(ST_DEB_MSG "%s: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", DEBC(printk(ST_DEB_MSG "%s: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
name, name,
...@@ -1697,25 +1734,22 @@ static long read_tape(struct scsi_tape *STp, long count, ...@@ -1697,25 +1734,22 @@ static long read_tape(struct scsi_tape *STp, long count,
SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3], SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3],
SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5], SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5],
SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7])); SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7]));
if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ if (cmdstatp->have_sense) {
if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
SRpnt->sr_sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ cmdstatp->flags &= 0xcf; /* No need for EOM in this case */
if ((SRpnt->sr_sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ if (cmdstatp->flags != 0) { /* EOF, EOM, or ILI */
/* Compute the residual count */ /* Compute the residual count */
if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0) if (cmdstatp->remainder_valid)
transfer = (SRpnt->sr_sense_buffer[3] << 24) | transfer = (int)cmdstatp->uremainder64;
(SRpnt->sr_sense_buffer[4] << 16) |
(SRpnt->sr_sense_buffer[5] << 8) |
SRpnt->sr_sense_buffer[6];
else else
transfer = 0; transfer = 0;
if (STp->block_size == 0 && if (STp->block_size == 0 &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == MEDIUM_ERROR) cmdstatp->sense_hdr.sense_key == MEDIUM_ERROR)
transfer = bytes; transfer = bytes;
if (SRpnt->sr_sense_buffer[2] & 0x20) { /* ILI */ if (cmdstatp->flags & SENSE_ILI) { /* ILI */
if (STp->block_size == 0) { if (STp->block_size == 0) {
if (transfer <= 0) { if (transfer <= 0) {
if (transfer < 0) if (transfer < 0)
...@@ -1749,7 +1783,7 @@ static long read_tape(struct scsi_tape *STp, long count, ...@@ -1749,7 +1783,7 @@ static long read_tape(struct scsi_tape *STp, long count,
if (st_int_ioctl(STp, MTBSR, 1)) if (st_int_ioctl(STp, MTBSR, 1))
return (-EIO); return (-EIO);
} }
} else if (SRpnt->sr_sense_buffer[2] & 0x80) { /* FM overrides EOM */ } else if (cmdstatp->flags & SENSE_FMK) { /* FM overrides EOM */
if (STps->eof != ST_FM_HIT) if (STps->eof != ST_FM_HIT)
STps->eof = ST_FM_HIT; STps->eof = ST_FM_HIT;
else else
...@@ -1762,7 +1796,7 @@ static long read_tape(struct scsi_tape *STp, long count, ...@@ -1762,7 +1796,7 @@ static long read_tape(struct scsi_tape *STp, long count,
DEBC(printk(ST_DEB_MSG DEBC(printk(ST_DEB_MSG
"%s: EOF detected (%d bytes read).\n", "%s: EOF detected (%d bytes read).\n",
name, STbp->buffer_bytes)); name, STbp->buffer_bytes));
} else if (SRpnt->sr_sense_buffer[2] & 0x40) { } else if (cmdstatp->flags & SENSE_EOM) {
if (STps->eof == ST_FM) if (STps->eof == ST_FM)
STps->eof = ST_EOD_1; STps->eof = ST_EOD_1;
else else
...@@ -1783,7 +1817,7 @@ static long read_tape(struct scsi_tape *STp, long count, ...@@ -1783,7 +1817,7 @@ static long read_tape(struct scsi_tape *STp, long count,
"%s: Tape error while reading.\n", name)); "%s: Tape error while reading.\n", name));
STps->drv_block = (-1); STps->drv_block = (-1);
if (STps->eof == ST_FM && if (STps->eof == ST_FM &&
(SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) { cmdstatp->sense_hdr.sense_key == BLANK_CHECK) {
DEBC(printk(ST_DEB_MSG DEBC(printk(ST_DEB_MSG
"%s: Zero returned for first BLANK CHECK after EOF.\n", "%s: Zero returned for first BLANK CHECK after EOF.\n",
name)); name));
...@@ -1907,7 +1941,7 @@ st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) ...@@ -1907,7 +1941,7 @@ st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
printk(ST_DEB_MSG printk(ST_DEB_MSG
"%s: EOF up (%d). Left %d, needed %d.\n", name, "%s: EOF up (%d). Left %d, needed %d.\n", name,
STps->eof, STbp->buffer_bytes, STps->eof, STbp->buffer_bytes,
count - total); (int)(count - total));
) /* end DEB */ ) /* end DEB */
transfer = STbp->buffer_bytes < count - total ? transfer = STbp->buffer_bytes < count - total ?
STbp->buffer_bytes : count - total; STbp->buffer_bytes : count - total;
...@@ -2695,23 +2729,26 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ...@@ -2695,23 +2729,26 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
} else { /* SCSI command was not completely successful. Don't return } else { /* SCSI command was not completely successful. Don't return
from this block without releasing the SCSI command block! */ from this block without releasing the SCSI command block! */
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
if (SRpnt->sr_sense_buffer[2] & 0x40) { if (cmdstatp->flags & SENSE_EOM) {
if (cmd_in != MTBSF && cmd_in != MTBSFM && if (cmd_in != MTBSF && cmd_in != MTBSFM &&
cmd_in != MTBSR && cmd_in != MTBSS) cmd_in != MTBSR && cmd_in != MTBSS)
STps->eof = ST_EOM_OK; STps->eof = ST_EOM_OK;
STps->drv_block = 0; STps->drv_block = 0;
} }
undone = ((SRpnt->sr_sense_buffer[3] << 24) + if (cmdstatp->remainder_valid)
(SRpnt->sr_sense_buffer[4] << 16) + undone = (int)cmdstatp->uremainder64;
(SRpnt->sr_sense_buffer[5] << 8) + else
SRpnt->sr_sense_buffer[6]); undone = 0;
if (cmd_in == MTWEOF && if (cmd_in == MTWEOF &&
(SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && cmdstatp->have_sense &&
(SRpnt->sr_sense_buffer[2] & 0x4f) == 0x40 && (cmdstatp->flags & SENSE_EOM) &&
((SRpnt->sr_sense_buffer[0] & 0x80) == 0 || undone == 0)) { (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
undone == 0) {
ioctl_result = 0; /* EOF written succesfully at EOM */ ioctl_result = 0; /* EOF written succesfully at EOM */
if (fileno >= 0) if (fileno >= 0)
fileno++; fileno++;
...@@ -2732,7 +2769,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ...@@ -2732,7 +2769,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
STps->drv_block = 0; STps->drv_block = 0;
STps->eof = ST_NOEOF; STps->eof = ST_NOEOF;
} else if (cmd_in == MTFSR) { } else if (cmd_in == MTFSR) {
if (SRpnt->sr_sense_buffer[2] & 0x80) { /* Hit filemark */ if (cmdstatp->flags & SENSE_FMK) { /* Hit filemark */
if (STps->drv_file >= 0) if (STps->drv_file >= 0)
STps->drv_file++; STps->drv_file++;
STps->drv_block = 0; STps->drv_block = 0;
...@@ -2745,7 +2782,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ...@@ -2745,7 +2782,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
STps->eof = ST_NOEOF; STps->eof = ST_NOEOF;
} }
} else if (cmd_in == MTBSR) { } else if (cmd_in == MTBSR) {
if (SRpnt->sr_sense_buffer[2] & 0x80) { /* Hit filemark */ if (cmdstatp->flags & SENSE_FMK) { /* Hit filemark */
STps->drv_file--; STps->drv_file--;
STps->drv_block = (-1); STps->drv_block = (-1);
} else { } else {
...@@ -2763,7 +2800,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ...@@ -2763,7 +2800,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd_in == MTSETDENSITY || cmd_in == MTSETDENSITY ||
cmd_in == MTSETDRVBUFFER || cmd_in == MTSETDRVBUFFER ||
cmd_in == SET_DENS_AND_BLK) { cmd_in == SET_DENS_AND_BLK) {
if ((SRpnt->sr_sense_buffer[2] & 0x0f) == ILLEGAL_REQUEST && if (cmdstatp->sense_hdr.sense_key == ILLEGAL_REQUEST &&
!(STp->use_pf & PF_TESTED)) { !(STp->use_pf & PF_TESTED)) {
/* Try the other possible state of Page Format if not /* Try the other possible state of Page Format if not
already tried */ already tried */
...@@ -2775,7 +2812,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ...@@ -2775,7 +2812,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
} else if (chg_eof) } else if (chg_eof)
STps->eof = ST_NOEOF; STps->eof = ST_NOEOF;
if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
STps->eof = ST_EOD; STps->eof = ST_EOD;
scsi_release_request(SRpnt); scsi_release_request(SRpnt);
......
...@@ -5,6 +5,18 @@ ...@@ -5,6 +5,18 @@
#include <linux/completion.h> #include <linux/completion.h>
/* Descriptor for analyzed sense data */
struct st_cmdstatus {
int midlevel_result;
struct scsi_sense_hdr sense_hdr;
int have_sense;
u64 uremainder64;
u8 flags;
u8 remainder_valid;
u8 fixed_format;
u8 deferred;
};
/* The tape buffer descriptor. */ /* The tape buffer descriptor. */
struct st_buffer { struct st_buffer {
unsigned char in_use; unsigned char in_use;
...@@ -15,9 +27,9 @@ struct st_buffer { ...@@ -15,9 +27,9 @@ struct st_buffer {
int buffer_bytes; int buffer_bytes;
int read_pointer; int read_pointer;
int writing; int writing;
int midlevel_result;
int syscall_result; int syscall_result;
struct scsi_request *last_SRpnt; struct scsi_request *last_SRpnt;
struct st_cmdstatus cmdstat;
unsigned char *b_data; unsigned char *b_data;
unsigned short use_sg; /* zero or max number of s/g segments for this adapter */ unsigned short use_sg; /* zero or max number of s/g segments for this adapter */
unsigned short sg_segs; /* number of segments in s/g list */ unsigned short sg_segs; /* number of segments in s/g list */
...@@ -192,4 +204,9 @@ struct scsi_tape { ...@@ -192,4 +204,9 @@ struct scsi_tape {
#define EXTENDED_SENSE_START 18 #define EXTENDED_SENSE_START 18
/* Masks for some conditions in the sense data */
#define SENSE_FMK 0x80
#define SENSE_EOM 0x40
#define SENSE_ILI 0x20
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment