[SCSI] sd: implement sd_check_events()

Replace sd_media_change() with sd_check_events().

* Move media removed logic into set_media_not_present() and
media_not_present() and set sdev->changed iff an existing media is
removed or the device indicates UNIT_ATTENTION.

* Make sd_check_events() sets sdev->changed if previously missing
media becomes present.

* Event is reported only if sdev->changed is set.

This makes media presence event reported if scsi_disk->media_present
actually changed or the device indicated UNIT_ATTENTION. For backward
compatibility, SDEV_EVT_MEDIA_CHANGE is generated each time
sd_check_events() detects media change event.

[jejb: fix boot failure]
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Jens Axboe <jaxboe@fusionio.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>

authored by Tejun Heo and committed by James Bottomley 2bae0093 52cfd503

+53 -52
+53 -51
drivers/scsi/sd.c
··· 990 990 991 991 static void set_media_not_present(struct scsi_disk *sdkp) 992 992 { 993 - sdkp->media_present = 0; 994 - sdkp->capacity = 0; 995 - sdkp->device->changed = 1; 993 + if (sdkp->media_present) 994 + sdkp->device->changed = 1; 995 + 996 + if (sdkp->device->removable) { 997 + sdkp->media_present = 0; 998 + sdkp->capacity = 0; 999 + } 1000 + } 1001 + 1002 + static int media_not_present(struct scsi_disk *sdkp, 1003 + struct scsi_sense_hdr *sshdr) 1004 + { 1005 + if (!scsi_sense_valid(sshdr)) 1006 + return 0; 1007 + 1008 + /* not invoked for commands that could return deferred errors */ 1009 + switch (sshdr->sense_key) { 1010 + case UNIT_ATTENTION: 1011 + case NOT_READY: 1012 + /* medium not present */ 1013 + if (sshdr->asc == 0x3A) { 1014 + set_media_not_present(sdkp); 1015 + return 1; 1016 + } 1017 + } 1018 + return 0; 996 1019 } 997 1020 998 1021 /** 999 - * sd_media_changed - check if our medium changed 1000 - * @disk: kernel device descriptor 1022 + * sd_check_events - check media events 1023 + * @disk: kernel device descriptor 1024 + * @clearing: disk events currently being cleared 1001 1025 * 1002 - * Returns 0 if not applicable or no change; 1 if change 1026 + * Returns mask of DISK_EVENT_*. 1003 1027 * 1004 1028 * Note: this function is invoked from the block subsystem. 1005 1029 **/ 1006 - static int sd_media_changed(struct gendisk *disk) 1030 + static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) 1007 1031 { 1008 1032 struct scsi_disk *sdkp = scsi_disk(disk); 1009 1033 struct scsi_device *sdp = sdkp->device; 1010 1034 struct scsi_sense_hdr *sshdr = NULL; 1011 1035 int retval; 1012 1036 1013 - SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n")); 1014 - 1015 - if (!sdp->removable) 1016 - return 0; 1037 + SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n")); 1017 1038 1018 1039 /* 1019 1040 * If the device is offline, don't send any commands - just pretend as ··· 1064 1043 sshdr); 1065 1044 } 1066 1045 1067 - if (retval) { 1046 + /* failed to execute TUR, assume media not present */ 1047 + if (host_byte(retval)) { 1068 1048 set_media_not_present(sdkp); 1069 1049 goto out; 1070 1050 } 1071 1051 1052 + if (media_not_present(sdkp, sshdr)) 1053 + goto out; 1054 + 1072 1055 /* 1073 1056 * For removable scsi disk we have to recognise the presence 1074 - * of a disk in the drive. This is kept in the struct scsi_disk 1075 - * struct and tested at open ! Daniel Roche (dan@lectra.fr) 1057 + * of a disk in the drive. 1076 1058 */ 1059 + if (!sdkp->media_present) 1060 + sdp->changed = 1; 1077 1061 sdkp->media_present = 1; 1078 - 1079 1062 out: 1080 1063 /* 1081 - * Report a media change under the following conditions: 1064 + * sdp->changed is set under the following conditions: 1082 1065 * 1083 - * Medium is present now and wasn't present before. 1084 - * Medium wasn't present before and is present now. 1085 - * Medium was present at all times, but it changed while 1086 - * we weren't looking (sdp->changed is set). 1066 + * Medium present state has changed in either direction. 1067 + * Device has indicated UNIT_ATTENTION. 1087 1068 * 1088 - * If there was no medium before and there is no medium now then 1089 - * don't report a change, even if a medium was inserted and removed 1090 - * while we weren't looking. 1069 + * Report SDEV_EVT_MEDIA_CHANGE too for backward compatibility. 1091 1070 */ 1092 - retval = (sdkp->media_present != sdkp->previous_state || 1093 - (sdkp->media_present && sdp->changed)); 1094 - if (retval) 1071 + if (sdp->changed) 1095 1072 sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); 1096 - sdkp->previous_state = sdkp->media_present; 1097 - 1098 - /* sdp->changed indicates medium was changed or is not present */ 1099 - sdp->changed = !sdkp->media_present; 1100 1073 kfree(sshdr); 1074 + 1075 + retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0; 1076 + sdp->changed = 0; 1101 1077 return retval; 1102 1078 } 1103 1079 ··· 1187 1169 #ifdef CONFIG_COMPAT 1188 1170 .compat_ioctl = sd_compat_ioctl, 1189 1171 #endif 1190 - .media_changed = sd_media_changed, 1172 + .check_events = sd_check_events, 1191 1173 .revalidate_disk = sd_revalidate_disk, 1192 1174 .unlock_native_capacity = sd_unlock_native_capacity, 1193 1175 }; ··· 1328 1310 } 1329 1311 1330 1312 return good_bytes; 1331 - } 1332 - 1333 - static int media_not_present(struct scsi_disk *sdkp, 1334 - struct scsi_sense_hdr *sshdr) 1335 - { 1336 - 1337 - if (!scsi_sense_valid(sshdr)) 1338 - return 0; 1339 - /* not invoked for commands that could return deferred errors */ 1340 - if (sshdr->sense_key != NOT_READY && 1341 - sshdr->sense_key != UNIT_ATTENTION) 1342 - return 0; 1343 - if (sshdr->asc != 0x3A) /* medium not present */ 1344 - return 0; 1345 - 1346 - set_media_not_present(sdkp); 1347 - return 1; 1348 1313 } 1349 1314 1350 1315 /* ··· 1504 1503 */ 1505 1504 if (sdp->removable && 1506 1505 sense_valid && sshdr->sense_key == NOT_READY) 1507 - sdp->changed = 1; 1506 + set_media_not_present(sdkp); 1508 1507 1509 1508 /* 1510 1509 * We used to set media_present to 0 here to indicate no media ··· 2390 2389 2391 2390 gd->driverfs_dev = &sdp->sdev_gendev; 2392 2391 gd->flags = GENHD_FL_EXT_DEVT; 2393 - if (sdp->removable) 2392 + if (sdp->removable) { 2394 2393 gd->flags |= GENHD_FL_REMOVABLE; 2394 + gd->events |= DISK_EVENT_MEDIA_CHANGE; 2395 + } 2395 2396 2396 2397 add_disk(gd); 2397 2398 sd_dif_config_host(sdkp); ··· 2475 2472 sdkp->disk = gd; 2476 2473 sdkp->index = index; 2477 2474 atomic_set(&sdkp->openers, 0); 2478 - sdkp->previous_state = 1; 2479 2475 2480 2476 if (!sdp->request_queue->rq_timeout) { 2481 2477 if (sdp->type != TYPE_MOD)
-1
drivers/scsi/sd.h
··· 55 55 u8 media_present; 56 56 u8 write_prot; 57 57 u8 protection_type;/* Data Integrity Field */ 58 - unsigned previous_state : 1; 59 58 unsigned ATO : 1; /* state of disk ATO bit */ 60 59 unsigned WCE : 1; /* state of disk WCE bit */ 61 60 unsigned RCD : 1; /* state of disk RCD bit, unused */