diff options
author | Ewan D. Milne <emilne@redhat.com> | 2013-08-08 15:07:48 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-08-26 10:52:27 -0400 |
commit | 279afdfe78a020b4b1a68bffd0009b961b12982e (patch) | |
tree | c6562e5d1b0292a4b59764ad7feccb47e48088a5 | |
parent | f8813d260eebb8c5d046ba4950a134c2283606a7 (diff) |
[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>
-rw-r--r-- | drivers/scsi/scsi_error.c | 100 | ||||
-rw-r--r-- | drivers/scsi/scsi_lib.c | 26 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 10 | ||||
-rw-r--r-- | include/scsi/scsi_device.h | 13 |
4 files changed, 127 insertions, 22 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index a46c3dddcf70..83e591b60193 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c | |||
@@ -223,6 +223,74 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, | |||
223 | } | 223 | } |
224 | #endif | 224 | #endif |
225 | 225 | ||
226 | /** | ||
227 | * scsi_report_lun_change - Set flag on all *other* devices on the same target | ||
228 | * to indicate that a UNIT ATTENTION is expected. | ||
229 | * @sdev: Device reporting the UNIT ATTENTION | ||
230 | */ | ||
231 | static void scsi_report_lun_change(struct scsi_device *sdev) | ||
232 | { | ||
233 | sdev->sdev_target->expecting_lun_change = 1; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * scsi_report_sense - Examine scsi sense information and log messages for | ||
238 | * certain conditions, also issue uevents for some of them. | ||
239 | * @sdev: Device reporting the sense code | ||
240 | * @sshdr: sshdr to be examined | ||
241 | */ | ||
242 | static void scsi_report_sense(struct scsi_device *sdev, | ||
243 | struct scsi_sense_hdr *sshdr) | ||
244 | { | ||
245 | enum scsi_device_event evt_type = SDEV_EVT_MAXBITS; /* i.e. none */ | ||
246 | |||
247 | if (sshdr->sense_key == UNIT_ATTENTION) { | ||
248 | if (sshdr->asc == 0x3f && sshdr->ascq == 0x03) { | ||
249 | evt_type = SDEV_EVT_INQUIRY_CHANGE_REPORTED; | ||
250 | sdev_printk(KERN_WARNING, sdev, | ||
251 | "Inquiry data has changed"); | ||
252 | } else if (sshdr->asc == 0x3f && sshdr->ascq == 0x0e) { | ||
253 | evt_type = SDEV_EVT_LUN_CHANGE_REPORTED; | ||
254 | scsi_report_lun_change(sdev); | ||
255 | sdev_printk(KERN_WARNING, sdev, | ||
256 | "Warning! Received an indication that the " | ||
257 | "LUN assignments on this target have " | ||
258 | "changed. The Linux SCSI layer does not " | ||
259 | "automatically remap LUN assignments.\n"); | ||
260 | } else if (sshdr->asc == 0x3f) | ||
261 | sdev_printk(KERN_WARNING, sdev, | ||
262 | "Warning! Received an indication that the " | ||
263 | "operating parameters on this target have " | ||
264 | "changed. The Linux SCSI layer does not " | ||
265 | "automatically adjust these parameters.\n"); | ||
266 | |||
267 | if (sshdr->asc == 0x38 && sshdr->ascq == 0x07) { | ||
268 | evt_type = SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED; | ||
269 | sdev_printk(KERN_WARNING, sdev, | ||
270 | "Warning! Received an indication that the " | ||
271 | "LUN reached a thin provisioning soft " | ||
272 | "threshold.\n"); | ||
273 | } | ||
274 | |||
275 | if (sshdr->asc == 0x2a && sshdr->ascq == 0x01) { | ||
276 | evt_type = SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED; | ||
277 | sdev_printk(KERN_WARNING, sdev, | ||
278 | "Mode parameters changed"); | ||
279 | } else if (sshdr->asc == 0x2a && sshdr->ascq == 0x09) { | ||
280 | evt_type = SDEV_EVT_CAPACITY_CHANGE_REPORTED; | ||
281 | sdev_printk(KERN_WARNING, sdev, | ||
282 | "Capacity data has changed"); | ||
283 | } else if (sshdr->asc == 0x2a) | ||
284 | sdev_printk(KERN_WARNING, sdev, | ||
285 | "Parameters changed"); | ||
286 | } | ||
287 | |||
288 | if (evt_type != SDEV_EVT_MAXBITS) { | ||
289 | set_bit(evt_type, sdev->pending_events); | ||
290 | schedule_work(&sdev->event_work); | ||
291 | } | ||
292 | } | ||
293 | |||
226 | /** | 294 | /** |
227 | * scsi_check_sense - Examine scsi cmd sense | 295 | * scsi_check_sense - Examine scsi cmd sense |
228 | * @scmd: Cmd to have sense checked. | 296 | * @scmd: Cmd to have sense checked. |
@@ -250,6 +318,8 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) | |||
250 | */ | 318 | */ |
251 | return SUCCESS; | 319 | return SUCCESS; |
252 | 320 | ||
321 | scsi_report_sense(sdev, &sshdr); | ||
322 | |||
253 | if (scsi_sense_is_deferred(&sshdr)) | 323 | if (scsi_sense_is_deferred(&sshdr)) |
254 | return NEEDS_RETRY; | 324 | return NEEDS_RETRY; |
255 | 325 | ||
@@ -315,6 +385,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) | |||
315 | } | 385 | } |
316 | } | 386 | } |
317 | /* | 387 | /* |
388 | * we might also expect a cc/ua if another LUN on the target | ||
389 | * reported a UA with an ASC/ASCQ of 3F 0E - | ||
390 | * REPORTED LUNS DATA HAS CHANGED. | ||
391 | */ | ||
392 | if (scmd->device->sdev_target->expecting_lun_change && | ||
393 | sshdr.asc == 0x3f && sshdr.ascq == 0x0e) | ||
394 | return NEEDS_RETRY; | ||
395 | /* | ||
318 | * if the device is in the process of becoming ready, we | 396 | * if the device is in the process of becoming ready, we |
319 | * should retry. | 397 | * should retry. |
320 | */ | 398 | */ |
@@ -327,26 +405,6 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) | |||
327 | if (scmd->device->allow_restart && | 405 | if (scmd->device->allow_restart && |
328 | (sshdr.asc == 0x04) && (sshdr.ascq == 0x02)) | 406 | (sshdr.asc == 0x04) && (sshdr.ascq == 0x02)) |
329 | return FAILED; | 407 | return FAILED; |
330 | |||
331 | if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e) | ||
332 | scmd_printk(KERN_WARNING, scmd, | ||
333 | "Warning! Received an indication that the " | ||
334 | "LUN assignments on this target have " | ||
335 | "changed. The Linux SCSI layer does not " | ||
336 | "automatically remap LUN assignments.\n"); | ||
337 | else if (sshdr.asc == 0x3f) | ||
338 | scmd_printk(KERN_WARNING, scmd, | ||
339 | "Warning! Received an indication that the " | ||
340 | "operating parameters on this target have " | ||
341 | "changed. The Linux SCSI layer does not " | ||
342 | "automatically adjust these parameters.\n"); | ||
343 | |||
344 | if (sshdr.asc == 0x38 && sshdr.ascq == 0x07) | ||
345 | scmd_printk(KERN_WARNING, scmd, | ||
346 | "Warning! Received an indication that the " | ||
347 | "LUN reached a thin provisioning soft " | ||
348 | "threshold.\n"); | ||
349 | |||
350 | /* | 408 | /* |
351 | * Pass the UA upwards for a determination in the completion | 409 | * Pass the UA upwards for a determination in the completion |
352 | * functions. | 410 | * functions. |
@@ -1574,6 +1632,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) | |||
1574 | */ | 1632 | */ |
1575 | return ADD_TO_MLQUEUE; | 1633 | return ADD_TO_MLQUEUE; |
1576 | case GOOD: | 1634 | case GOOD: |
1635 | if (scmd->cmnd[0] == REPORT_LUNS) | ||
1636 | scmd->device->sdev_target->expecting_lun_change = 0; | ||
1577 | scsi_handle_queue_ramp_up(scmd->device); | 1637 | scsi_handle_queue_ramp_up(scmd->device); |
1578 | case COMMAND_TERMINATED: | 1638 | case COMMAND_TERMINATED: |
1579 | return SUCCESS; | 1639 | return SUCCESS; |
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 36b5c898db9d..d545931c85eb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c | |||
@@ -2253,7 +2253,21 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) | |||
2253 | case SDEV_EVT_MEDIA_CHANGE: | 2253 | case SDEV_EVT_MEDIA_CHANGE: |
2254 | envp[idx++] = "SDEV_MEDIA_CHANGE=1"; | 2254 | envp[idx++] = "SDEV_MEDIA_CHANGE=1"; |
2255 | break; | 2255 | break; |
2256 | 2256 | case SDEV_EVT_INQUIRY_CHANGE_REPORTED: | |
2257 | envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED"; | ||
2258 | break; | ||
2259 | case SDEV_EVT_CAPACITY_CHANGE_REPORTED: | ||
2260 | envp[idx++] = "SDEV_UA=CAPACITY_DATA_HAS_CHANGED"; | ||
2261 | break; | ||
2262 | case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED: | ||
2263 | envp[idx++] = "SDEV_UA=THIN_PROVISIONING_SOFT_THRESHOLD_REACHED"; | ||
2264 | break; | ||
2265 | case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: | ||
2266 | envp[idx++] = "SDEV_UA=MODE_PARAMETERS_CHANGED"; | ||
2267 | break; | ||
2268 | case SDEV_EVT_LUN_CHANGE_REPORTED: | ||
2269 | envp[idx++] = "SDEV_UA=REPORTED_LUNS_DATA_HAS_CHANGED"; | ||
2270 | break; | ||
2257 | default: | 2271 | default: |
2258 | /* do nothing */ | 2272 | /* do nothing */ |
2259 | break; | 2273 | break; |
@@ -2274,10 +2288,15 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) | |||
2274 | void scsi_evt_thread(struct work_struct *work) | 2288 | void scsi_evt_thread(struct work_struct *work) |
2275 | { | 2289 | { |
2276 | struct scsi_device *sdev; | 2290 | struct scsi_device *sdev; |
2291 | enum scsi_device_event evt_type; | ||
2277 | LIST_HEAD(event_list); | 2292 | LIST_HEAD(event_list); |
2278 | 2293 | ||
2279 | sdev = container_of(work, struct scsi_device, event_work); | 2294 | sdev = container_of(work, struct scsi_device, event_work); |
2280 | 2295 | ||
2296 | for (evt_type = SDEV_EVT_FIRST; evt_type <= SDEV_EVT_LAST; evt_type++) | ||
2297 | if (test_and_clear_bit(evt_type, sdev->pending_events)) | ||
2298 | sdev_evt_send_simple(sdev, evt_type, GFP_KERNEL); | ||
2299 | |||
2281 | while (1) { | 2300 | while (1) { |
2282 | struct scsi_event *evt; | 2301 | struct scsi_event *evt; |
2283 | struct list_head *this, *tmp; | 2302 | struct list_head *this, *tmp; |
@@ -2347,6 +2366,11 @@ struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, | |||
2347 | /* evt_type-specific initialization, if any */ | 2366 | /* evt_type-specific initialization, if any */ |
2348 | switch (evt_type) { | 2367 | switch (evt_type) { |
2349 | case SDEV_EVT_MEDIA_CHANGE: | 2368 | case SDEV_EVT_MEDIA_CHANGE: |
2369 | case SDEV_EVT_INQUIRY_CHANGE_REPORTED: | ||
2370 | case SDEV_EVT_CAPACITY_CHANGE_REPORTED: | ||
2371 | case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED: | ||
2372 | case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: | ||
2373 | case SDEV_EVT_LUN_CHANGE_REPORTED: | ||
2350 | default: | 2374 | default: |
2351 | /* do nothing */ | 2375 | /* do nothing */ |
2352 | break; | 2376 | break; |
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 7e50061e9ef6..40c639491b27 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c | |||
@@ -739,6 +739,11 @@ sdev_store_evt_##name(struct device *dev, struct device_attribute *attr,\ | |||
739 | #define REF_EVT(name) &dev_attr_evt_##name.attr | 739 | #define REF_EVT(name) &dev_attr_evt_##name.attr |
740 | 740 | ||
741 | DECLARE_EVT(media_change, MEDIA_CHANGE) | 741 | DECLARE_EVT(media_change, MEDIA_CHANGE) |
742 | DECLARE_EVT(inquiry_change_reported, INQUIRY_CHANGE_REPORTED) | ||
743 | DECLARE_EVT(capacity_change_reported, CAPACITY_CHANGE_REPORTED) | ||
744 | DECLARE_EVT(soft_threshold_reached, SOFT_THRESHOLD_REACHED_REPORTED) | ||
745 | DECLARE_EVT(mode_parameter_change_reported, MODE_PARAMETER_CHANGE_REPORTED) | ||
746 | DECLARE_EVT(lun_change_reported, LUN_CHANGE_REPORTED) | ||
742 | 747 | ||
743 | /* Default template for device attributes. May NOT be modified */ | 748 | /* Default template for device attributes. May NOT be modified */ |
744 | static struct attribute *scsi_sdev_attrs[] = { | 749 | static struct attribute *scsi_sdev_attrs[] = { |
@@ -759,6 +764,11 @@ static struct attribute *scsi_sdev_attrs[] = { | |||
759 | &dev_attr_ioerr_cnt.attr, | 764 | &dev_attr_ioerr_cnt.attr, |
760 | &dev_attr_modalias.attr, | 765 | &dev_attr_modalias.attr, |
761 | REF_EVT(media_change), | 766 | REF_EVT(media_change), |
767 | REF_EVT(inquiry_change_reported), | ||
768 | REF_EVT(capacity_change_reported), | ||
769 | REF_EVT(soft_threshold_reached), | ||
770 | REF_EVT(mode_parameter_change_reported), | ||
771 | REF_EVT(lun_change_reported), | ||
762 | NULL | 772 | NULL |
763 | }; | 773 | }; |
764 | 774 | ||
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index a44954c7cdc2..d65fbec2533d 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h | |||
@@ -52,8 +52,15 @@ enum scsi_device_state { | |||
52 | 52 | ||
53 | enum scsi_device_event { | 53 | enum scsi_device_event { |
54 | SDEV_EVT_MEDIA_CHANGE = 1, /* media has changed */ | 54 | SDEV_EVT_MEDIA_CHANGE = 1, /* media has changed */ |
55 | SDEV_EVT_INQUIRY_CHANGE_REPORTED, /* 3F 03 UA reported */ | ||
56 | SDEV_EVT_CAPACITY_CHANGE_REPORTED, /* 2A 09 UA reported */ | ||
57 | SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED, /* 38 07 UA reported */ | ||
58 | SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED, /* 2A 01 UA reported */ | ||
59 | SDEV_EVT_LUN_CHANGE_REPORTED, /* 3F 0E UA reported */ | ||
60 | |||
61 | SDEV_EVT_FIRST = SDEV_EVT_MEDIA_CHANGE, | ||
62 | SDEV_EVT_LAST = SDEV_EVT_LUN_CHANGE_REPORTED, | ||
55 | 63 | ||
56 | SDEV_EVT_LAST = SDEV_EVT_MEDIA_CHANGE, | ||
57 | SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1 | 64 | SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1 |
58 | }; | 65 | }; |
59 | 66 | ||
@@ -164,6 +171,7 @@ struct scsi_device { | |||
164 | atomic_t disk_events_disable_depth; /* disable depth for disk events */ | 171 | atomic_t disk_events_disable_depth; /* disable depth for disk events */ |
165 | 172 | ||
166 | DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ | 173 | DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ |
174 | DECLARE_BITMAP(pending_events, SDEV_EVT_MAXBITS); /* pending events */ | ||
167 | struct list_head event_list; /* asserted events */ | 175 | struct list_head event_list; /* asserted events */ |
168 | struct work_struct event_work; | 176 | struct work_struct event_work; |
169 | 177 | ||
@@ -261,6 +269,9 @@ struct scsi_target { | |||
261 | * means no lun present. */ | 269 | * means no lun present. */ |
262 | unsigned int no_report_luns:1; /* Don't use | 270 | unsigned int no_report_luns:1; /* Don't use |
263 | * REPORT LUNS for scanning. */ | 271 | * REPORT LUNS for scanning. */ |
272 | unsigned int expecting_lun_change:1; /* A device has reported | ||
273 | * a 3F/0E UA, other devices on | ||
274 | * the same target will also. */ | ||
264 | /* commands actually active on LLD. protected by host lock. */ | 275 | /* commands actually active on LLD. protected by host lock. */ |
265 | unsigned int target_busy; | 276 | unsigned int target_busy; |
266 | /* | 277 | /* |