[SCSI] Generate uevents on certain unit attention codes
Generate a uevent when the following Unit Attention ASC/ASCQ codes are received: 2A/01 MODE PARAMETERS CHANGED 2A/09 CAPACITY DATA HAS CHANGED 38/07 THIN PROVISIONING SOFT THRESHOLD REACHED 3F/03 INQUIRY DATA HAS CHANGED 3F/0E REPORTED LUNS DATA HAS CHANGED Log kernel messages when the following Unit Attention ASC/ASCQ codes are received that are not as specific as those above: 2A/xx PARAMETERS CHANGED 3F/xx TARGET OPERATING CONDITIONS HAVE CHANGED Added logic to set expecting_lun_change for other LUNs on the target after REPORTED LUNS DATA HAS CHANGED is received, so that duplicate uevents are not generated, and clear expecting_lun_change when a REPORT LUNS command completes, in accordance with the SPC-3 specification regarding reporting of the 3F 0E ASC/ASCQ UA. [jejb: remove SPC3 test in scsi_report_lun_change and some docbook fixes and unused variable fix, both reported by Fengguang Wu] Signed-off-by: Ewan D. Milne <emilne@redhat.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
parent
f8813d260e
commit
279afdfe78
|
@ -223,6 +223,74 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scsi_report_lun_change - Set flag on all *other* devices on the same target
|
||||||
|
* to indicate that a UNIT ATTENTION is expected.
|
||||||
|
* @sdev: Device reporting the UNIT ATTENTION
|
||||||
|
*/
|
||||||
|
static void scsi_report_lun_change(struct scsi_device *sdev)
|
||||||
|
{
|
||||||
|
sdev->sdev_target->expecting_lun_change = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scsi_report_sense - Examine scsi sense information and log messages for
|
||||||
|
* certain conditions, also issue uevents for some of them.
|
||||||
|
* @sdev: Device reporting the sense code
|
||||||
|
* @sshdr: sshdr to be examined
|
||||||
|
*/
|
||||||
|
static void scsi_report_sense(struct scsi_device *sdev,
|
||||||
|
struct scsi_sense_hdr *sshdr)
|
||||||
|
{
|
||||||
|
enum scsi_device_event evt_type = SDEV_EVT_MAXBITS; /* i.e. none */
|
||||||
|
|
||||||
|
if (sshdr->sense_key == UNIT_ATTENTION) {
|
||||||
|
if (sshdr->asc == 0x3f && sshdr->ascq == 0x03) {
|
||||||
|
evt_type = SDEV_EVT_INQUIRY_CHANGE_REPORTED;
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Inquiry data has changed");
|
||||||
|
} else if (sshdr->asc == 0x3f && sshdr->ascq == 0x0e) {
|
||||||
|
evt_type = SDEV_EVT_LUN_CHANGE_REPORTED;
|
||||||
|
scsi_report_lun_change(sdev);
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Warning! Received an indication that the "
|
||||||
|
"LUN assignments on this target have "
|
||||||
|
"changed. The Linux SCSI layer does not "
|
||||||
|
"automatically remap LUN assignments.\n");
|
||||||
|
} else if (sshdr->asc == 0x3f)
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Warning! Received an indication that the "
|
||||||
|
"operating parameters on this target have "
|
||||||
|
"changed. The Linux SCSI layer does not "
|
||||||
|
"automatically adjust these parameters.\n");
|
||||||
|
|
||||||
|
if (sshdr->asc == 0x38 && sshdr->ascq == 0x07) {
|
||||||
|
evt_type = SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED;
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Warning! Received an indication that the "
|
||||||
|
"LUN reached a thin provisioning soft "
|
||||||
|
"threshold.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sshdr->asc == 0x2a && sshdr->ascq == 0x01) {
|
||||||
|
evt_type = SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED;
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Mode parameters changed");
|
||||||
|
} else if (sshdr->asc == 0x2a && sshdr->ascq == 0x09) {
|
||||||
|
evt_type = SDEV_EVT_CAPACITY_CHANGE_REPORTED;
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Capacity data has changed");
|
||||||
|
} else if (sshdr->asc == 0x2a)
|
||||||
|
sdev_printk(KERN_WARNING, sdev,
|
||||||
|
"Parameters changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt_type != SDEV_EVT_MAXBITS) {
|
||||||
|
set_bit(evt_type, sdev->pending_events);
|
||||||
|
schedule_work(&sdev->event_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* scsi_check_sense - Examine scsi cmd sense
|
* scsi_check_sense - Examine scsi cmd sense
|
||||||
* @scmd: Cmd to have sense checked.
|
* @scmd: Cmd to have sense checked.
|
||||||
|
@ -250,6 +318,8 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
|
||||||
*/
|
*/
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
|
||||||
|
scsi_report_sense(sdev, &sshdr);
|
||||||
|
|
||||||
if (scsi_sense_is_deferred(&sshdr))
|
if (scsi_sense_is_deferred(&sshdr))
|
||||||
return NEEDS_RETRY;
|
return NEEDS_RETRY;
|
||||||
|
|
||||||
|
@ -314,6 +384,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
|
||||||
return NEEDS_RETRY;
|
return NEEDS_RETRY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* we might also expect a cc/ua if another LUN on the target
|
||||||
|
* reported a UA with an ASC/ASCQ of 3F 0E -
|
||||||
|
* REPORTED LUNS DATA HAS CHANGED.
|
||||||
|
*/
|
||||||
|
if (scmd->device->sdev_target->expecting_lun_change &&
|
||||||
|
sshdr.asc == 0x3f && sshdr.ascq == 0x0e)
|
||||||
|
return NEEDS_RETRY;
|
||||||
/*
|
/*
|
||||||
* if the device is in the process of becoming ready, we
|
* if the device is in the process of becoming ready, we
|
||||||
* should retry.
|
* should retry.
|
||||||
|
@ -327,26 +405,6 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
|
||||||
if (scmd->device->allow_restart &&
|
if (scmd->device->allow_restart &&
|
||||||
(sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
|
(sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
|
||||||
return FAILED;
|
return FAILED;
|
||||||
|
|
||||||
if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e)
|
|
||||||
scmd_printk(KERN_WARNING, scmd,
|
|
||||||
"Warning! Received an indication that the "
|
|
||||||
"LUN assignments on this target have "
|
|
||||||
"changed. The Linux SCSI layer does not "
|
|
||||||
"automatically remap LUN assignments.\n");
|
|
||||||
else if (sshdr.asc == 0x3f)
|
|
||||||
scmd_printk(KERN_WARNING, scmd,
|
|
||||||
"Warning! Received an indication that the "
|
|
||||||
"operating parameters on this target have "
|
|
||||||
"changed. The Linux SCSI layer does not "
|
|
||||||
"automatically adjust these parameters.\n");
|
|
||||||
|
|
||||||
if (sshdr.asc == 0x38 && sshdr.ascq == 0x07)
|
|
||||||
scmd_printk(KERN_WARNING, scmd,
|
|
||||||
"Warning! Received an indication that the "
|
|
||||||
"LUN reached a thin provisioning soft "
|
|
||||||
"threshold.\n");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pass the UA upwards for a determination in the completion
|
* Pass the UA upwards for a determination in the completion
|
||||||
* functions.
|
* functions.
|
||||||
|
@ -1574,6 +1632,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
|
||||||
*/
|
*/
|
||||||
return ADD_TO_MLQUEUE;
|
return ADD_TO_MLQUEUE;
|
||||||
case GOOD:
|
case GOOD:
|
||||||
|
if (scmd->cmnd[0] == REPORT_LUNS)
|
||||||
|
scmd->device->sdev_target->expecting_lun_change = 0;
|
||||||
scsi_handle_queue_ramp_up(scmd->device);
|
scsi_handle_queue_ramp_up(scmd->device);
|
||||||
case COMMAND_TERMINATED:
|
case COMMAND_TERMINATED:
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
|
|
@ -2253,7 +2253,21 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
|
||||||
case SDEV_EVT_MEDIA_CHANGE:
|
case SDEV_EVT_MEDIA_CHANGE:
|
||||||
envp[idx++] = "SDEV_MEDIA_CHANGE=1";
|
envp[idx++] = "SDEV_MEDIA_CHANGE=1";
|
||||||
break;
|
break;
|
||||||
|
case SDEV_EVT_INQUIRY_CHANGE_REPORTED:
|
||||||
|
envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED";
|
||||||
|
break;
|
||||||
|
case SDEV_EVT_CAPACITY_CHANGE_REPORTED:
|
||||||
|
envp[idx++] = "SDEV_UA=CAPACITY_DATA_HAS_CHANGED";
|
||||||
|
break;
|
||||||
|
case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED:
|
||||||
|
envp[idx++] = "SDEV_UA=THIN_PROVISIONING_SOFT_THRESHOLD_REACHED";
|
||||||
|
break;
|
||||||
|
case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED:
|
||||||
|
envp[idx++] = "SDEV_UA=MODE_PARAMETERS_CHANGED";
|
||||||
|
break;
|
||||||
|
case SDEV_EVT_LUN_CHANGE_REPORTED:
|
||||||
|
envp[idx++] = "SDEV_UA=REPORTED_LUNS_DATA_HAS_CHANGED";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* do nothing */
|
/* do nothing */
|
||||||
break;
|
break;
|
||||||
|
@ -2274,10 +2288,15 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt)
|
||||||
void scsi_evt_thread(struct work_struct *work)
|
void scsi_evt_thread(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct scsi_device *sdev;
|
struct scsi_device *sdev;
|
||||||
|
enum scsi_device_event evt_type;
|
||||||
LIST_HEAD(event_list);
|
LIST_HEAD(event_list);
|
||||||
|
|
||||||
sdev = container_of(work, struct scsi_device, event_work);
|
sdev = container_of(work, struct scsi_device, event_work);
|
||||||
|
|
||||||
|
for (evt_type = SDEV_EVT_FIRST; evt_type <= SDEV_EVT_LAST; evt_type++)
|
||||||
|
if (test_and_clear_bit(evt_type, sdev->pending_events))
|
||||||
|
sdev_evt_send_simple(sdev, evt_type, GFP_KERNEL);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
struct scsi_event *evt;
|
struct scsi_event *evt;
|
||||||
struct list_head *this, *tmp;
|
struct list_head *this, *tmp;
|
||||||
|
@ -2347,6 +2366,11 @@ struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
|
||||||
/* evt_type-specific initialization, if any */
|
/* evt_type-specific initialization, if any */
|
||||||
switch (evt_type) {
|
switch (evt_type) {
|
||||||
case SDEV_EVT_MEDIA_CHANGE:
|
case SDEV_EVT_MEDIA_CHANGE:
|
||||||
|
case SDEV_EVT_INQUIRY_CHANGE_REPORTED:
|
||||||
|
case SDEV_EVT_CAPACITY_CHANGE_REPORTED:
|
||||||
|
case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED:
|
||||||
|
case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED:
|
||||||
|
case SDEV_EVT_LUN_CHANGE_REPORTED:
|
||||||
default:
|
default:
|
||||||
/* do nothing */
|
/* do nothing */
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -739,6 +739,11 @@ sdev_store_evt_##name(struct device *dev, struct device_attribute *attr,\
|
||||||
#define REF_EVT(name) &dev_attr_evt_##name.attr
|
#define REF_EVT(name) &dev_attr_evt_##name.attr
|
||||||
|
|
||||||
DECLARE_EVT(media_change, MEDIA_CHANGE)
|
DECLARE_EVT(media_change, MEDIA_CHANGE)
|
||||||
|
DECLARE_EVT(inquiry_change_reported, INQUIRY_CHANGE_REPORTED)
|
||||||
|
DECLARE_EVT(capacity_change_reported, CAPACITY_CHANGE_REPORTED)
|
||||||
|
DECLARE_EVT(soft_threshold_reached, SOFT_THRESHOLD_REACHED_REPORTED)
|
||||||
|
DECLARE_EVT(mode_parameter_change_reported, MODE_PARAMETER_CHANGE_REPORTED)
|
||||||
|
DECLARE_EVT(lun_change_reported, LUN_CHANGE_REPORTED)
|
||||||
|
|
||||||
/* Default template for device attributes. May NOT be modified */
|
/* Default template for device attributes. May NOT be modified */
|
||||||
static struct attribute *scsi_sdev_attrs[] = {
|
static struct attribute *scsi_sdev_attrs[] = {
|
||||||
|
@ -759,6 +764,11 @@ static struct attribute *scsi_sdev_attrs[] = {
|
||||||
&dev_attr_ioerr_cnt.attr,
|
&dev_attr_ioerr_cnt.attr,
|
||||||
&dev_attr_modalias.attr,
|
&dev_attr_modalias.attr,
|
||||||
REF_EVT(media_change),
|
REF_EVT(media_change),
|
||||||
|
REF_EVT(inquiry_change_reported),
|
||||||
|
REF_EVT(capacity_change_reported),
|
||||||
|
REF_EVT(soft_threshold_reached),
|
||||||
|
REF_EVT(mode_parameter_change_reported),
|
||||||
|
REF_EVT(lun_change_reported),
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,15 @@ enum scsi_device_state {
|
||||||
|
|
||||||
enum scsi_device_event {
|
enum scsi_device_event {
|
||||||
SDEV_EVT_MEDIA_CHANGE = 1, /* media has changed */
|
SDEV_EVT_MEDIA_CHANGE = 1, /* media has changed */
|
||||||
|
SDEV_EVT_INQUIRY_CHANGE_REPORTED, /* 3F 03 UA reported */
|
||||||
|
SDEV_EVT_CAPACITY_CHANGE_REPORTED, /* 2A 09 UA reported */
|
||||||
|
SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED, /* 38 07 UA reported */
|
||||||
|
SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED, /* 2A 01 UA reported */
|
||||||
|
SDEV_EVT_LUN_CHANGE_REPORTED, /* 3F 0E UA reported */
|
||||||
|
|
||||||
|
SDEV_EVT_FIRST = SDEV_EVT_MEDIA_CHANGE,
|
||||||
|
SDEV_EVT_LAST = SDEV_EVT_LUN_CHANGE_REPORTED,
|
||||||
|
|
||||||
SDEV_EVT_LAST = SDEV_EVT_MEDIA_CHANGE,
|
|
||||||
SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1
|
SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,6 +171,7 @@ struct scsi_device {
|
||||||
atomic_t disk_events_disable_depth; /* disable depth for disk events */
|
atomic_t disk_events_disable_depth; /* disable depth for disk events */
|
||||||
|
|
||||||
DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
|
DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
|
||||||
|
DECLARE_BITMAP(pending_events, SDEV_EVT_MAXBITS); /* pending events */
|
||||||
struct list_head event_list; /* asserted events */
|
struct list_head event_list; /* asserted events */
|
||||||
struct work_struct event_work;
|
struct work_struct event_work;
|
||||||
|
|
||||||
|
@ -261,6 +269,9 @@ struct scsi_target {
|
||||||
* means no lun present. */
|
* means no lun present. */
|
||||||
unsigned int no_report_luns:1; /* Don't use
|
unsigned int no_report_luns:1; /* Don't use
|
||||||
* REPORT LUNS for scanning. */
|
* REPORT LUNS for scanning. */
|
||||||
|
unsigned int expecting_lun_change:1; /* A device has reported
|
||||||
|
* a 3F/0E UA, other devices on
|
||||||
|
* the same target will also. */
|
||||||
/* commands actually active on LLD. protected by host lock. */
|
/* commands actually active on LLD. protected by host lock. */
|
||||||
unsigned int target_busy;
|
unsigned int target_busy;
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue