[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>
This commit is contained in:
parent
57a6fa9acd
commit
79b9677d88
@ -221,14 +221,33 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi,
|
||||
return 0;
|
||||
|
||||
events = sr_get_events(cd->device);
|
||||
cd->get_event_changed |= events & DISK_EVENT_MEDIA_CHANGE;
|
||||
|
||||
/*
|
||||
* If earlier GET_EVENT_STATUS_NOTIFICATION and TUR did not agree
|
||||
* for several times in a row. We rely on TUR only for this likely
|
||||
* broken device, to prevent generating incorrect media changed
|
||||
* events for every open().
|
||||
*/
|
||||
if (cd->ignore_get_event) {
|
||||
events &= ~DISK_EVENT_MEDIA_CHANGE;
|
||||
goto do_tur;
|
||||
}
|
||||
|
||||
/*
|
||||
* GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE
|
||||
* is being cleared. Note that there are devices which hang
|
||||
* if asked to execute TUR repeatedly.
|
||||
*/
|
||||
if (!(clearing & DISK_EVENT_MEDIA_CHANGE))
|
||||
goto skip_tur;
|
||||
if (cd->device->changed) {
|
||||
events |= DISK_EVENT_MEDIA_CHANGE;
|
||||
cd->device->changed = 0;
|
||||
cd->tur_changed = true;
|
||||
}
|
||||
|
||||
if (!(clearing & DISK_EVENT_MEDIA_CHANGE))
|
||||
return events;
|
||||
do_tur:
|
||||
/* let's see whether the media is there with TUR */
|
||||
last_present = cd->media_present;
|
||||
ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
|
||||
@ -242,13 +261,32 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi,
|
||||
(scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a);
|
||||
|
||||
if (last_present != cd->media_present)
|
||||
events |= DISK_EVENT_MEDIA_CHANGE;
|
||||
skip_tur:
|
||||
cd->device->changed = 1;
|
||||
|
||||
if (cd->device->changed) {
|
||||
events |= DISK_EVENT_MEDIA_CHANGE;
|
||||
cd->device->changed = 0;
|
||||
cd->tur_changed = true;
|
||||
}
|
||||
|
||||
if (cd->ignore_get_event)
|
||||
return events;
|
||||
|
||||
/* check whether GET_EVENT is reporting spurious MEDIA_CHANGE */
|
||||
if (!cd->tur_changed) {
|
||||
if (cd->get_event_changed) {
|
||||
if (cd->tur_mismatch++ > 8) {
|
||||
sdev_printk(KERN_WARNING, cd->device,
|
||||
"GET_EVENT and TUR disagree continuously, suppress GET_EVENT events\n");
|
||||
cd->ignore_get_event = true;
|
||||
}
|
||||
} else {
|
||||
cd->tur_mismatch = 0;
|
||||
}
|
||||
}
|
||||
cd->tur_changed = false;
|
||||
cd->get_event_changed = false;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,13 @@ typedef struct scsi_cd {
|
||||
unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */
|
||||
unsigned readcd_cdda:1; /* reading audio data using READ_CD */
|
||||
unsigned media_present:1; /* media is present */
|
||||
|
||||
/* GET_EVENT spurious event handling, blk layer guarantees exclusion */
|
||||
int tur_mismatch; /* nr of get_event TUR mismatches */
|
||||
bool tur_changed:1; /* changed according to TUR */
|
||||
bool get_event_changed:1; /* changed according to GET_EVENT */
|
||||
bool ignore_get_event:1; /* GET_EVENT is unreliable, use TUR */
|
||||
|
||||
struct cdrom_device_info cdi;
|
||||
/* We hold gendisk and scsi_device references on probe and use
|
||||
* the refs on this kref to decide when to release them */
|
||||
|
Loading…
Reference in New Issue
Block a user