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 /drivers | |
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>
Diffstat (limited to 'drivers')
-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 */ |