diff options
| author | Kay Sievers <kay.sievers@vrfy.org> | 2011-06-30 09:03:48 -0400 |
|---|---|---|
| committer | James Bottomley <JBottomley@Parallels.com> | 2011-07-21 17:15:58 -0400 |
| commit | 79b9677d885d1a792bc103f2febb06f91f92de43 (patch) | |
| tree | 5971cd5ec435ca750b8ba77929df53e1fd78dbad | |
| parent | 57a6fa9acd6b4a479a6ede4d6d2258f04afd3a6f (diff) | |
[SCSI] sr: check_events() ignore GET_EVENT when TUR says otherwise
Some broken devices indicates that media has changed on every
GET_EVENT_STATUS_NOTIFICATION. This translates into MEDIA_CHANGE
uevent on every open() which lets udev run into a loop.
Verify GET_EVENT result against TUR and if it generates spurious
events for several times in a row, ignore the GET_EVENT events, and
trust only the TUR status.
This is the log of a USB stick with a (broken) fake CDROM drive:
scsi 5:0:0:0: Direct-Access SanDisk U3 Cruzer Micro 8.02 PQ: 0 ANSI: 0 CCS
sd 5:0:0:0: Attached scsi generic sg3 type 0
scsi 5:0:0:1: CD-ROM SanDisk U3 Cruzer Micro 8.02 PQ: 0 ANSI: 0
sd 5:0:0:0: [sdb] Attached SCSI removable disk
sr2: scsi3-mmc drive: 48x/48x tray
sr 5:0:0:1: Attached scsi CD-ROM sr2
sr 5:0:0:1: Attached scsi generic sg4 type 5
sr2: GET_EVENT and TUR disagree continuously, suppress GET_EVENT events
sd 5:0:0:0: [sdb] 31777279 512-byte logical blocks: (16.2 GB/15.1 GiB)
sd 5:0:0:0: [sdb] No Caching mode page present
sd 5:0:0:0: [sdb] Assuming drive cache: write through
sd 5:0:0:0: [sdb] No Caching mode page present
sd 5:0:0:0: [sdb] Assuming drive cache: write through
sdb: sdb1
-tj: Updated to consider only spurious GET_EVENT events among
different types of disagreement and allow using TUR for kernel
event polling after GET_EVENT is ignored.
Reported-By: Markus Rathgeb maggu2810@googlemail.com
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: stable@kernel.org # >= v2.6.38, fixes udev busy looping w/ certain devices
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
| -rw-r--r-- | drivers/scsi/sr.c | 46 | ||||
| -rw-r--r-- | drivers/scsi/sr.h | 7 |
2 files changed, 49 insertions, 4 deletions
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 4778e2707168..5fc97d2ba2fd 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c | |||
| @@ -221,14 +221,33 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, | |||
| 221 | return 0; | 221 | return 0; |
| 222 | 222 | ||
| 223 | events = sr_get_events(cd->device); | 223 | events = sr_get_events(cd->device); |
| 224 | cd->get_event_changed |= events & DISK_EVENT_MEDIA_CHANGE; | ||
| 225 | |||
| 226 | /* | ||
| 227 | * If earlier GET_EVENT_STATUS_NOTIFICATION and TUR did not agree | ||
| 228 | * for several times in a row. We rely on TUR only for this likely | ||
| 229 | * broken device, to prevent generating incorrect media changed | ||
| 230 | * events for every open(). | ||
| 231 | */ | ||
| 232 | if (cd->ignore_get_event) { | ||
| 233 | events &= ~DISK_EVENT_MEDIA_CHANGE; | ||
| 234 | goto do_tur; | ||
| 235 | } | ||
| 236 | |||
| 224 | /* | 237 | /* |
| 225 | * GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE | 238 | * GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE |
| 226 | * is being cleared. Note that there are devices which hang | 239 | * is being cleared. Note that there are devices which hang |
| 227 | * if asked to execute TUR repeatedly. | 240 | * if asked to execute TUR repeatedly. |
| 228 | */ | 241 | */ |
| 229 | if (!(clearing & DISK_EVENT_MEDIA_CHANGE)) | 242 | if (cd->device->changed) { |
| 230 | goto skip_tur; | 243 | events |= DISK_EVENT_MEDIA_CHANGE; |
| 244 | cd->device->changed = 0; | ||
| 245 | cd->tur_changed = true; | ||
| 246 | } | ||
| 231 | 247 | ||
| 248 | if (!(clearing & DISK_EVENT_MEDIA_CHANGE)) | ||
| 249 | return events; | ||
| 250 | do_tur: | ||
| 232 | /* let's see whether the media is there with TUR */ | 251 | /* let's see whether the media is there with TUR */ |
| 233 | last_present = cd->media_present; | 252 | last_present = cd->media_present; |
| 234 | ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr); | 253 | ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr); |
| @@ -242,12 +261,31 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, | |||
| 242 | (scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a); | 261 | (scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a); |
| 243 | 262 | ||
| 244 | if (last_present != cd->media_present) | 263 | if (last_present != cd->media_present) |
| 245 | events |= DISK_EVENT_MEDIA_CHANGE; | 264 | cd->device->changed = 1; |
| 246 | skip_tur: | 265 | |
| 247 | if (cd->device->changed) { | 266 | if (cd->device->changed) { |
| 248 | events |= DISK_EVENT_MEDIA_CHANGE; | 267 | events |= DISK_EVENT_MEDIA_CHANGE; |
| 249 | cd->device->changed = 0; | 268 | cd->device->changed = 0; |
| 269 | cd->tur_changed = true; | ||
| 270 | } | ||
| 271 | |||
| 272 | if (cd->ignore_get_event) | ||
| 273 | return events; | ||
| 274 | |||
| 275 | /* check whether GET_EVENT is reporting spurious MEDIA_CHANGE */ | ||
| 276 | if (!cd->tur_changed) { | ||
| 277 | if (cd->get_event_changed) { | ||
| 278 | if (cd->tur_mismatch++ > 8) { | ||
| 279 | sdev_printk(KERN_WARNING, cd->device, | ||
| 280 | "GET_EVENT and TUR disagree continuously, suppress GET_EVENT events\n"); | ||
| 281 | cd->ignore_get_event = true; | ||
| 282 | } | ||
| 283 | } else { | ||
| 284 | cd->tur_mismatch = 0; | ||
| 285 | } | ||
| 250 | } | 286 | } |
| 287 | cd->tur_changed = false; | ||
| 288 | cd->get_event_changed = false; | ||
| 251 | 289 | ||
| 252 | return events; | 290 | return events; |
| 253 | } | 291 | } |
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index e036f1dc83c8..37c8f6b17510 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h | |||
| @@ -41,6 +41,13 @@ typedef struct scsi_cd { | |||
| 41 | unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ | 41 | unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ |
| 42 | unsigned readcd_cdda:1; /* reading audio data using READ_CD */ | 42 | unsigned readcd_cdda:1; /* reading audio data using READ_CD */ |
| 43 | unsigned media_present:1; /* media is present */ | 43 | unsigned media_present:1; /* media is present */ |
| 44 | |||
| 45 | /* GET_EVENT spurious event handling, blk layer guarantees exclusion */ | ||
| 46 | int tur_mismatch; /* nr of get_event TUR mismatches */ | ||
| 47 | bool tur_changed:1; /* changed according to TUR */ | ||
| 48 | bool get_event_changed:1; /* changed according to GET_EVENT */ | ||
| 49 | bool ignore_get_event:1; /* GET_EVENT is unreliable, use TUR */ | ||
| 50 | |||
| 44 | struct cdrom_device_info cdi; | 51 | struct cdrom_device_info cdi; |
| 45 | /* We hold gendisk and scsi_device references on probe and use | 52 | /* We hold gendisk and scsi_device references on probe and use |
| 46 | * the refs on this kref to decide when to release them */ | 53 | * the refs on this kref to decide when to release them */ |
