diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2012-07-16 14:18:58 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2012-07-27 08:25:25 +0200 |
commit | e48e84ea80cb2e7fe6e48196ce187cfba6e3eb2c (patch) | |
tree | 9ca4997e8d067b51940b9dba3ce6e9adc0781dc5 /hw/scsi-bus.c | |
parent | 350e6e419902991b073b313aa65b240d1024d57e (diff) | |
download | qemu-e48e84ea80cb2e7fe6e48196ce187cfba6e3eb2c.zip qemu-e48e84ea80cb2e7fe6e48196ce187cfba6e3eb2c.tar.gz qemu-e48e84ea80cb2e7fe6e48196ce187cfba6e3eb2c.tar.bz2 |
scsi: establish precedence levels for unit attention
When a device is resized, we will report a unit attention condition
for CAPACITY DATA HAS CHANGED. However, we should ensure that this
condition does not override a more important unit attention condition.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/scsi-bus.c')
-rw-r--r-- | hw/scsi-bus.c | 52 |
1 files changed, 51 insertions, 1 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 6ee0c10..c38c0ec 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1531,6 +1531,55 @@ void scsi_req_abort(SCSIRequest *req, int status) scsi_req_unref(req); } +static int scsi_ua_precedence(SCSISense sense) +{ + if (sense.key != UNIT_ATTENTION) { + return INT_MAX; + } + if (sense.asc == 0x29 && sense.ascq == 0x04) { + /* DEVICE INTERNAL RESET goes with POWER ON OCCURRED */ + return 1; + } else if (sense.asc == 0x3F && sense.ascq == 0x01) { + /* MICROCODE HAS BEEN CHANGED goes with SCSI BUS RESET OCCURRED */ + return 2; + } else if (sense.asc == 0x29 && (sense.ascq == 0x05 || sense.ascq == 0x06)) { + /* These two go with "all others". */ + ; + } else if (sense.asc == 0x29 && sense.ascq <= 0x07) { + /* POWER ON, RESET OR BUS DEVICE RESET OCCURRED = 0 + * POWER ON OCCURRED = 1 + * SCSI BUS RESET OCCURRED = 2 + * BUS DEVICE RESET FUNCTION OCCURRED = 3 + * I_T NEXUS LOSS OCCURRED = 7 + */ + return sense.ascq; + } else if (sense.asc == 0x2F && sense.ascq == 0x01) { + /* COMMANDS CLEARED BY POWER LOSS NOTIFICATION */ + return 8; + } + return (sense.asc << 8) | sense.ascq; +} + +void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense) +{ + int prec1, prec2; + if (sense.key != UNIT_ATTENTION) { + return; + } + trace_scsi_device_set_ua(sdev->id, sdev->lun, sense.key, + sense.asc, sense.ascq); + + /* + * Override a pre-existing unit attention condition, except for a more + * important reset condition. + */ + prec1 = scsi_ua_precedence(sdev->unit_attention); + prec2 = scsi_ua_precedence(sense); + if (prec2 < prec1) { + sdev->unit_attention = sense; + } +} + void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) { SCSIRequest *req; @@ -1539,7 +1588,8 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense) req = QTAILQ_FIRST(&sdev->requests); scsi_req_cancel(req); } - sdev->unit_attention = sense; + + scsi_device_set_ua(sdev, sense); } static char *scsibus_get_dev_path(DeviceState *dev) |