Skip to content

Commit 81c5e2a

Browse files
author
Jens Axboe
committed
Merge branch 'for-2.6.38/event-handling' into for-2.6.38/core
2 parents 797a455 + fcc5704 commit 81c5e2a

File tree

14 files changed

+734
-230
lines changed

14 files changed

+734
-230
lines changed

block/genhd.c

+516-28
Large diffs are not rendered by default.

drivers/cdrom/cdrom.c

+53-3
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,10 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
13481348
if (!CDROM_CAN(CDC_SELECT_DISC))
13491349
return -EDRIVE_CANT_DO_THIS;
13501350

1351-
(void) cdi->ops->media_changed(cdi, slot);
1351+
if (cdi->ops->check_events)
1352+
cdi->ops->check_events(cdi, 0, slot);
1353+
else
1354+
cdi->ops->media_changed(cdi, slot);
13521355

13531356
if (slot == CDSL_NONE) {
13541357
/* set media changed bits, on both queues */
@@ -1392,6 +1395,42 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
13921395
return slot;
13931396
}
13941397

1398+
/*
1399+
* As cdrom implements an extra ioctl consumer for media changed
1400+
* event, it needs to buffer ->check_events() output, such that event
1401+
* is not lost for both the usual VFS and ioctl paths.
1402+
* cdi->{vfs|ioctl}_events are used to buffer pending events for each
1403+
* path.
1404+
*
1405+
* XXX: Locking is non-existent. cdi->ops->check_events() can be
1406+
* called in parallel and buffering fields are accessed without any
1407+
* exclusion. The original media_changed code had the same problem.
1408+
* It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl
1409+
* and remove this cruft altogether. It doesn't have much usefulness
1410+
* at this point.
1411+
*/
1412+
static void cdrom_update_events(struct cdrom_device_info *cdi,
1413+
unsigned int clearing)
1414+
{
1415+
unsigned int events;
1416+
1417+
events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT);
1418+
cdi->vfs_events |= events;
1419+
cdi->ioctl_events |= events;
1420+
}
1421+
1422+
unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
1423+
unsigned int clearing)
1424+
{
1425+
unsigned int events;
1426+
1427+
cdrom_update_events(cdi, clearing);
1428+
events = cdi->vfs_events;
1429+
cdi->vfs_events = 0;
1430+
return events;
1431+
}
1432+
EXPORT_SYMBOL(cdrom_check_events);
1433+
13951434
/* We want to make media_changed accessible to the user through an
13961435
* ioctl. The main problem now is that we must double-buffer the
13971436
* low-level implementation, to assure that the VFS and the user both
@@ -1403,15 +1442,26 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
14031442
{
14041443
unsigned int mask = (1 << (queue & 1));
14051444
int ret = !!(cdi->mc_flags & mask);
1445+
bool changed;
14061446

14071447
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
1408-
return ret;
1448+
return ret;
1449+
14091450
/* changed since last call? */
1410-
if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
1451+
if (cdi->ops->check_events) {
1452+
BUG_ON(!queue); /* shouldn't be called from VFS path */
1453+
cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE);
1454+
changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE;
1455+
cdi->ioctl_events = 0;
1456+
} else
1457+
changed = cdi->ops->media_changed(cdi, CDSL_CURRENT);
1458+
1459+
if (changed) {
14111460
cdi->mc_flags = 0x3; /* set bit on both queues */
14121461
ret |= 1;
14131462
cdi->media_written = 0;
14141463
}
1464+
14151465
cdi->mc_flags &= ~mask; /* clear bit */
14161466
return ret;
14171467
}

drivers/scsi/scsi_lib.c

+1-12
Original file line numberDiff line numberDiff line change
@@ -1984,8 +1984,7 @@ EXPORT_SYMBOL(scsi_mode_sense);
19841984
* in.
19851985
*
19861986
* Returns zero if unsuccessful or an error if TUR failed. For
1987-
* removable media, a return of NOT_READY or UNIT_ATTENTION is
1988-
* translated to success, with the ->changed flag updated.
1987+
* removable media, UNIT_ATTENTION sets ->changed flag.
19891988
**/
19901989
int
19911990
scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
@@ -2012,16 +2011,6 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
20122011
} while (scsi_sense_valid(sshdr) &&
20132012
sshdr->sense_key == UNIT_ATTENTION && --retries);
20142013

2015-
if (!sshdr)
2016-
/* could not allocate sense buffer, so can't process it */
2017-
return result;
2018-
2019-
if (sdev->removable && scsi_sense_valid(sshdr) &&
2020-
(sshdr->sense_key == UNIT_ATTENTION ||
2021-
sshdr->sense_key == NOT_READY)) {
2022-
sdev->changed = 1;
2023-
result = 0;
2024-
}
20252014
if (!sshdr_external)
20262015
kfree(sshdr);
20272016
return result;

drivers/scsi/sd.c

+1-9
Original file line numberDiff line numberDiff line change
@@ -1045,15 +1045,7 @@ static int sd_media_changed(struct gendisk *disk)
10451045
sshdr);
10461046
}
10471047

1048-
/*
1049-
* Unable to test, unit probably not ready. This usually
1050-
* means there is no disc in the drive. Mark as changed,
1051-
* and we will figure it out later once the drive is
1052-
* available again.
1053-
*/
1054-
if (retval || (scsi_sense_valid(sshdr) &&
1055-
/* 0x3a is medium not present */
1056-
sshdr->asc == 0x3a)) {
1048+
if (retval) {
10571049
set_media_not_present(sdkp);
10581050
retval = 1;
10591051
goto out;

drivers/scsi/sr.c

+99-75
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,15 @@ static void sr_release(struct cdrom_device_info *);
104104
static void get_sectorsize(struct scsi_cd *);
105105
static void get_capabilities(struct scsi_cd *);
106106

107-
static int sr_media_change(struct cdrom_device_info *, int);
107+
static unsigned int sr_check_events(struct cdrom_device_info *cdi,
108+
unsigned int clearing, int slot);
108109
static int sr_packet(struct cdrom_device_info *, struct packet_command *);
109110

110111
static struct cdrom_device_ops sr_dops = {
111112
.open = sr_open,
112113
.release = sr_release,
113114
.drive_status = sr_drive_status,
114-
.media_changed = sr_media_change,
115+
.check_events = sr_check_events,
115116
.tray_move = sr_tray_move,
116117
.lock_door = sr_lock_door,
117118
.select_speed = sr_select_speed,
@@ -165,90 +166,96 @@ static void scsi_cd_put(struct scsi_cd *cd)
165166
mutex_unlock(&sr_ref_mutex);
166167
}
167168

168-
/* identical to scsi_test_unit_ready except that it doesn't
169-
* eat the NOT_READY returns for removable media */
170-
int sr_test_unit_ready(struct scsi_device *sdev, struct scsi_sense_hdr *sshdr)
169+
static unsigned int sr_get_events(struct scsi_device *sdev)
171170
{
172-
int retries = MAX_RETRIES;
173-
int the_result;
174-
u8 cmd[] = {TEST_UNIT_READY, 0, 0, 0, 0, 0 };
171+
u8 buf[8];
172+
u8 cmd[] = { GET_EVENT_STATUS_NOTIFICATION,
173+
1, /* polled */
174+
0, 0, /* reserved */
175+
1 << 4, /* notification class: media */
176+
0, 0, /* reserved */
177+
0, sizeof(buf), /* allocation length */
178+
0, /* control */
179+
};
180+
struct event_header *eh = (void *)buf;
181+
struct media_event_desc *med = (void *)(buf + 4);
182+
struct scsi_sense_hdr sshdr;
183+
int result;
175184

176-
/* issue TEST_UNIT_READY until the initial startup UNIT_ATTENTION
177-
* conditions are gone, or a timeout happens
178-
*/
179-
do {
180-
the_result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL,
181-
0, sshdr, SR_TIMEOUT,
182-
retries--, NULL);
183-
if (scsi_sense_valid(sshdr) &&
184-
sshdr->sense_key == UNIT_ATTENTION)
185-
sdev->changed = 1;
186-
187-
} while (retries > 0 &&
188-
(!scsi_status_is_good(the_result) ||
189-
(scsi_sense_valid(sshdr) &&
190-
sshdr->sense_key == UNIT_ATTENTION)));
191-
return the_result;
185+
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, sizeof(buf),
186+
&sshdr, SR_TIMEOUT, MAX_RETRIES, NULL);
187+
if (scsi_sense_valid(&sshdr) && sshdr.sense_key == UNIT_ATTENTION)
188+
return DISK_EVENT_MEDIA_CHANGE;
189+
190+
if (result || be16_to_cpu(eh->data_len) < sizeof(*med))
191+
return 0;
192+
193+
if (eh->nea || eh->notification_class != 0x4)
194+
return 0;
195+
196+
if (med->media_event_code == 1)
197+
return DISK_EVENT_EJECT_REQUEST;
198+
else if (med->media_event_code == 2)
199+
return DISK_EVENT_MEDIA_CHANGE;
200+
return 0;
192201
}
193202

194203
/*
195-
* This function checks to see if the media has been changed in the
196-
* CDROM drive. It is possible that we have already sensed a change,
197-
* or the drive may have sensed one and not yet reported it. We must
198-
* be ready for either case. This function always reports the current
199-
* value of the changed bit. If flag is 0, then the changed bit is reset.
200-
* This function could be done as an ioctl, but we would need to have
201-
* an inode for that to work, and we do not always have one.
204+
* This function checks to see if the media has been changed or eject
205+
* button has been pressed. It is possible that we have already
206+
* sensed a change, or the drive may have sensed one and not yet
207+
* reported it. The past events are accumulated in sdev->changed and
208+
* returned together with the current state.
202209
*/
203-
204-
static int sr_media_change(struct cdrom_device_info *cdi, int slot)
210+
static unsigned int sr_check_events(struct cdrom_device_info *cdi,
211+
unsigned int clearing, int slot)
205212
{
206213
struct scsi_cd *cd = cdi->handle;
207-
int retval;
208-
struct scsi_sense_hdr *sshdr;
214+
bool last_present;
215+
struct scsi_sense_hdr sshdr;
216+
unsigned int events;
217+
int ret;
209218

210-
if (CDSL_CURRENT != slot) {
211-
/* no changer support */
212-
return -EINVAL;
213-
}
219+
/* no changer support */
220+
if (CDSL_CURRENT != slot)
221+
return 0;
214222

215-
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
216-
retval = sr_test_unit_ready(cd->device, sshdr);
217-
if (retval || (scsi_sense_valid(sshdr) &&
218-
/* 0x3a is medium not present */
219-
sshdr->asc == 0x3a)) {
220-
/* Media not present or unable to test, unit probably not
221-
* ready. This usually means there is no disc in the drive.
222-
* Mark as changed, and we will figure it out later once
223-
* the drive is available again.
224-
*/
225-
cd->device->changed = 1;
226-
/* This will force a flush, if called from check_disk_change */
227-
retval = 1;
228-
goto out;
229-
};
223+
events = sr_get_events(cd->device);
224+
/*
225+
* GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE
226+
* is being cleared. Note that there are devices which hang
227+
* if asked to execute TUR repeatedly.
228+
*/
229+
if (!(clearing & DISK_EVENT_MEDIA_CHANGE))
230+
goto skip_tur;
231+
232+
/* let's see whether the media is there with TUR */
233+
last_present = cd->media_present;
234+
ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
235+
236+
/*
237+
* Media is considered to be present if TUR succeeds or fails with
238+
* sense data indicating something other than media-not-present
239+
* (ASC 0x3a).
240+
*/
241+
cd->media_present = scsi_status_is_good(ret) ||
242+
(scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a);
230243

231-
retval = cd->device->changed;
232-
cd->device->changed = 0;
233-
/* If the disk changed, the capacity will now be different,
234-
* so we force a re-read of this information */
235-
if (retval) {
236-
/* check multisession offset etc */
237-
sr_cd_check(cdi);
238-
get_sectorsize(cd);
244+
if (last_present != cd->media_present)
245+
events |= DISK_EVENT_MEDIA_CHANGE;
246+
skip_tur:
247+
if (cd->device->changed) {
248+
events |= DISK_EVENT_MEDIA_CHANGE;
249+
cd->device->changed = 0;
239250
}
240251

241-
out:
242-
/* Notify userspace, that media has changed. */
243-
if (retval != cd->previous_state)
252+
/* for backward compatibility */
253+
if (events & DISK_EVENT_MEDIA_CHANGE)
244254
sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
245255
GFP_KERNEL);
246-
cd->previous_state = retval;
247-
kfree(sshdr);
248-
249-
return retval;
256+
return events;
250257
}
251-
258+
252259
/*
253260
* sr_done is the interrupt routine for the device driver.
254261
*
@@ -533,10 +540,25 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
533540
return ret;
534541
}
535542

536-
static int sr_block_media_changed(struct gendisk *disk)
543+
static unsigned int sr_block_check_events(struct gendisk *disk,
544+
unsigned int clearing)
537545
{
538546
struct scsi_cd *cd = scsi_cd(disk);
539-
return cdrom_media_changed(&cd->cdi);
547+
return cdrom_check_events(&cd->cdi, clearing);
548+
}
549+
550+
static int sr_block_revalidate_disk(struct gendisk *disk)
551+
{
552+
struct scsi_cd *cd = scsi_cd(disk);
553+
struct scsi_sense_hdr sshdr;
554+
555+
/* if the unit is not ready, nothing more to do */
556+
if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
557+
return 0;
558+
559+
sr_cd_check(&cd->cdi);
560+
get_sectorsize(cd);
561+
return 0;
540562
}
541563

542564
static const struct block_device_operations sr_bdops =
@@ -545,7 +567,8 @@ static const struct block_device_operations sr_bdops =
545567
.open = sr_block_open,
546568
.release = sr_block_release,
547569
.ioctl = sr_block_ioctl,
548-
.media_changed = sr_block_media_changed,
570+
.check_events = sr_block_check_events,
571+
.revalidate_disk = sr_block_revalidate_disk,
549572
/*
550573
* No compat_ioctl for now because sr_block_ioctl never
551574
* seems to pass arbitary ioctls down to host drivers.
@@ -618,6 +641,7 @@ static int sr_probe(struct device *dev)
618641
sprintf(disk->disk_name, "sr%d", minor);
619642
disk->fops = &sr_bdops;
620643
disk->flags = GENHD_FL_CD;
644+
disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST;
621645

622646
blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);
623647

@@ -627,7 +651,7 @@ static int sr_probe(struct device *dev)
627651
cd->disk = disk;
628652
cd->capacity = 0x1fffff;
629653
cd->device->changed = 1; /* force recheck CD type */
630-
cd->previous_state = 1;
654+
cd->media_present = 1;
631655
cd->use = 1;
632656
cd->readcd_known = 0;
633657
cd->readcd_cdda = 0;
@@ -780,7 +804,7 @@ static void get_capabilities(struct scsi_cd *cd)
780804
}
781805

782806
/* eat unit attentions */
783-
sr_test_unit_ready(cd->device, &sshdr);
807+
scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
784808

785809
/* ask for mode page 0x2a */
786810
rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,

drivers/scsi/sr.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ typedef struct scsi_cd {
4040
unsigned xa_flag:1; /* CD has XA sectors ? */
4141
unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */
4242
unsigned readcd_cdda:1; /* reading audio data using READ_CD */
43-
unsigned previous_state:1; /* media has changed */
43+
unsigned media_present:1; /* media is present */
4444
struct cdrom_device_info cdi;
4545
/* We hold gendisk and scsi_device references on probe and use
4646
* the refs on this kref to decide when to release them */
@@ -61,7 +61,6 @@ int sr_select_speed(struct cdrom_device_info *cdi, int speed);
6161
int sr_audio_ioctl(struct cdrom_device_info *, unsigned int, void *);
6262

6363
int sr_is_xa(Scsi_CD *);
64-
int sr_test_unit_ready(struct scsi_device *sdev, struct scsi_sense_hdr *sshdr);
6564

6665
/* sr_vendor.c */
6766
void sr_vendor_init(Scsi_CD *);

0 commit comments

Comments
 (0)