Commit c8d2e937355d02db3055c2fc203e5f017297ee1f
Committed by
Jens Axboe
1 parent
93aae17af1
Exists in
master
and in
39 other branches
sd: implement sd_check_events()
Replace sd_media_change() with sd_check_events(). sd used to set the changed state whenever the device is not ready, which can cause event loop while the device is not ready. Media presence handling code is changed such that the changed state is set iff the media presence actually changes. UA still always sets the changed state and NOT_READY always (at least where it used to set ->changed) clears media presence, so no event is lost. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Showing 2 changed files with 47 additions and 41 deletions Side-by-side Diff
drivers/scsi/sd.c
... | ... | @@ -991,31 +991,51 @@ |
991 | 991 | |
992 | 992 | static void set_media_not_present(struct scsi_disk *sdkp) |
993 | 993 | { |
994 | + if (sdkp->media_present) | |
995 | + sdkp->device->changed = 1; | |
994 | 996 | sdkp->media_present = 0; |
995 | 997 | sdkp->capacity = 0; |
996 | - sdkp->device->changed = 1; | |
997 | 998 | } |
998 | 999 | |
1000 | +static int media_not_present(struct scsi_disk *sdkp, | |
1001 | + struct scsi_sense_hdr *sshdr) | |
1002 | +{ | |
1003 | + if (!scsi_sense_valid(sshdr)) | |
1004 | + return 0; | |
1005 | + | |
1006 | + /* not invoked for commands that could return deferred errors */ | |
1007 | + switch (sshdr->sense_key) { | |
1008 | + case UNIT_ATTENTION: | |
1009 | + sdkp->device->changed = 1; | |
1010 | + /* fall through */ | |
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; | |
1019 | +} | |
1020 | + | |
999 | 1021 | /** |
1000 | - * sd_media_changed - check if our medium changed | |
1001 | - * @disk: kernel device descriptor | |
1022 | + * sd_check_events - check media events | |
1023 | + * @disk: kernel device descriptor | |
1024 | + * @clearing: disk events currently being cleared | |
1002 | 1025 | * |
1003 | - * Returns 0 if not applicable or no change; 1 if change | |
1026 | + * Returns mask of DISK_EVENT_*. | |
1004 | 1027 | * |
1005 | 1028 | * Note: this function is invoked from the block subsystem. |
1006 | 1029 | **/ |
1007 | -static int sd_media_changed(struct gendisk *disk) | |
1030 | +static unsigned int sd_check_events(struct gendisk *disk, unsigned int clearing) | |
1008 | 1031 | { |
1009 | 1032 | struct scsi_disk *sdkp = scsi_disk(disk); |
1010 | 1033 | struct scsi_device *sdp = sdkp->device; |
1011 | 1034 | struct scsi_sense_hdr *sshdr = NULL; |
1012 | 1035 | int retval; |
1013 | 1036 | |
1014 | - SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n")); | |
1037 | + SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_check_events\n")); | |
1015 | 1038 | |
1016 | - if (!sdp->removable) | |
1017 | - return 0; | |
1018 | - | |
1019 | 1039 | /* |
1020 | 1040 | * If the device is offline, don't send any commands - just pretend as |
1021 | 1041 | * if the command failed. If the device ever comes back online, we |
... | ... | @@ -1024,7 +1044,6 @@ |
1024 | 1044 | */ |
1025 | 1045 | if (!scsi_device_online(sdp)) { |
1026 | 1046 | set_media_not_present(sdkp); |
1027 | - retval = 1; | |
1028 | 1047 | goto out; |
1029 | 1048 | } |
1030 | 1049 | |
1031 | 1050 | |
1032 | 1051 | |
1033 | 1052 | |
1034 | 1053 | |
1035 | 1054 | |
1036 | 1055 | |
1037 | 1056 | |
1038 | 1057 | |
... | ... | @@ -1045,26 +1064,30 @@ |
1045 | 1064 | sshdr); |
1046 | 1065 | } |
1047 | 1066 | |
1048 | - if (retval) { | |
1067 | + /* failed to execute TUR, assume media not present */ | |
1068 | + if (host_byte(retval)) { | |
1049 | 1069 | set_media_not_present(sdkp); |
1050 | - retval = 1; | |
1051 | 1070 | goto out; |
1052 | 1071 | } |
1053 | 1072 | |
1073 | + if (media_not_present(sdkp, sshdr)) | |
1074 | + goto out; | |
1075 | + | |
1054 | 1076 | /* |
1055 | 1077 | * For removable scsi disk we have to recognise the presence |
1056 | - * of a disk in the drive. This is kept in the struct scsi_disk | |
1057 | - * struct and tested at open ! Daniel Roche (dan@lectra.fr) | |
1078 | + * of a disk in the drive. | |
1058 | 1079 | */ |
1080 | + if (!sdkp->media_present) | |
1081 | + sdp->changed = 1; | |
1059 | 1082 | sdkp->media_present = 1; |
1060 | - | |
1061 | - retval = sdp->changed; | |
1062 | - sdp->changed = 0; | |
1063 | 1083 | out: |
1064 | - if (retval != sdkp->previous_state) | |
1084 | + /* for backward compatibility */ | |
1085 | + if (sdp->changed) | |
1065 | 1086 | sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL); |
1066 | - sdkp->previous_state = retval; | |
1067 | 1087 | kfree(sshdr); |
1088 | + | |
1089 | + retval = sdp->changed ? DISK_EVENT_MEDIA_CHANGE : 0; | |
1090 | + sdp->changed = 0; | |
1068 | 1091 | return retval; |
1069 | 1092 | } |
1070 | 1093 | |
... | ... | @@ -1157,7 +1180,7 @@ |
1157 | 1180 | #ifdef CONFIG_COMPAT |
1158 | 1181 | .compat_ioctl = sd_compat_ioctl, |
1159 | 1182 | #endif |
1160 | - .media_changed = sd_media_changed, | |
1183 | + .check_events = sd_check_events, | |
1161 | 1184 | .revalidate_disk = sd_revalidate_disk, |
1162 | 1185 | .unlock_native_capacity = sd_unlock_native_capacity, |
1163 | 1186 | }; |
... | ... | @@ -1293,23 +1316,6 @@ |
1293 | 1316 | return good_bytes; |
1294 | 1317 | } |
1295 | 1318 | |
1296 | -static int media_not_present(struct scsi_disk *sdkp, | |
1297 | - struct scsi_sense_hdr *sshdr) | |
1298 | -{ | |
1299 | - | |
1300 | - if (!scsi_sense_valid(sshdr)) | |
1301 | - return 0; | |
1302 | - /* not invoked for commands that could return deferred errors */ | |
1303 | - if (sshdr->sense_key != NOT_READY && | |
1304 | - sshdr->sense_key != UNIT_ATTENTION) | |
1305 | - return 0; | |
1306 | - if (sshdr->asc != 0x3A) /* medium not present */ | |
1307 | - return 0; | |
1308 | - | |
1309 | - set_media_not_present(sdkp); | |
1310 | - return 1; | |
1311 | -} | |
1312 | - | |
1313 | 1319 | /* |
1314 | 1320 | * spinup disk - called only in sd_revalidate_disk() |
1315 | 1321 | */ |
... | ... | @@ -1484,7 +1490,7 @@ |
1484 | 1490 | */ |
1485 | 1491 | if (sdp->removable && |
1486 | 1492 | sense_valid && sshdr->sense_key == NOT_READY) |
1487 | - sdp->changed = 1; | |
1493 | + set_media_not_present(sdkp); | |
1488 | 1494 | |
1489 | 1495 | /* |
1490 | 1496 | * We used to set media_present to 0 here to indicate no media |
1491 | 1497 | |
... | ... | @@ -2339,8 +2345,10 @@ |
2339 | 2345 | |
2340 | 2346 | gd->driverfs_dev = &sdp->sdev_gendev; |
2341 | 2347 | gd->flags = GENHD_FL_EXT_DEVT; |
2342 | - if (sdp->removable) | |
2348 | + if (sdp->removable) { | |
2343 | 2349 | gd->flags |= GENHD_FL_REMOVABLE; |
2350 | + gd->events |= DISK_EVENT_MEDIA_CHANGE; | |
2351 | + } | |
2344 | 2352 | |
2345 | 2353 | add_disk(gd); |
2346 | 2354 | sd_dif_config_host(sdkp); |
... | ... | @@ -2422,7 +2430,6 @@ |
2422 | 2430 | sdkp->disk = gd; |
2423 | 2431 | sdkp->index = index; |
2424 | 2432 | atomic_set(&sdkp->openers, 0); |
2425 | - sdkp->previous_state = 1; | |
2426 | 2433 | |
2427 | 2434 | if (!sdp->request_queue->rq_timeout) { |
2428 | 2435 | if (sdp->type != TYPE_MOD) |
drivers/scsi/sd.h
... | ... | @@ -55,7 +55,6 @@ |
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 */ |