cdrom: add ->check_events() support
In principle, cdrom just needs to pass through ->check_events() but CDROM_MEDIA_CHANGED ioctl makes things a bit more complex. Just as with ->media_changed() support, cdrom code needs to buffer the events and serve them to ioctl and vfs as requested. As the code has to deal with both ->check_events() and ->media_changed(), and vfs and ioctl event buffering, this patch adds check_events caching on top of the existing cdi->mc_flags buffering. It may be a good idea to deprecate CDROM_MEDIA_CHANGED ioctl and remove all this mess. Signed-off-by: Tejun Heo <tj@kernel.org> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
This commit is contained in:
@@ -1348,7 +1348,10 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
|
|||||||
if (!CDROM_CAN(CDC_SELECT_DISC))
|
if (!CDROM_CAN(CDC_SELECT_DISC))
|
||||||
return -EDRIVE_CANT_DO_THIS;
|
return -EDRIVE_CANT_DO_THIS;
|
||||||
|
|
||||||
(void) cdi->ops->media_changed(cdi, slot);
|
if (cdi->ops->check_events)
|
||||||
|
cdi->ops->check_events(cdi, 0, slot);
|
||||||
|
else
|
||||||
|
cdi->ops->media_changed(cdi, slot);
|
||||||
|
|
||||||
if (slot == CDSL_NONE) {
|
if (slot == CDSL_NONE) {
|
||||||
/* set media changed bits, on both queues */
|
/* set media changed bits, on both queues */
|
||||||
@@ -1392,6 +1395,41 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
|
|||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As cdrom implements an extra ioctl consumer for media changed
|
||||||
|
* event, it needs to buffer ->check_events() output, such that event
|
||||||
|
* is not lost for both the usual VFS and ioctl paths.
|
||||||
|
* cdi->{vfs|ioctl}_events are used to buffer pending events for each
|
||||||
|
* path.
|
||||||
|
*
|
||||||
|
* XXX: Locking is non-existent. cdi->ops->check_events() can be
|
||||||
|
* called in parallel and buffering fields are accessed without any
|
||||||
|
* exclusion. The original media_changed code had the same problem.
|
||||||
|
* It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl
|
||||||
|
* and remove this cruft altogether. It doesn't have much usefulness
|
||||||
|
* at this point.
|
||||||
|
*/
|
||||||
|
static void cdrom_update_events(struct cdrom_device_info *cdi,
|
||||||
|
unsigned int clearing)
|
||||||
|
{
|
||||||
|
unsigned int events;
|
||||||
|
|
||||||
|
events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT);
|
||||||
|
cdi->vfs_events |= events;
|
||||||
|
cdi->ioctl_events |= events;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
|
||||||
|
unsigned int clearing)
|
||||||
|
{
|
||||||
|
unsigned int events;
|
||||||
|
|
||||||
|
cdrom_update_events(cdi, clearing);
|
||||||
|
events = cdi->vfs_events;
|
||||||
|
cdi->vfs_events = 0;
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
/* We want to make media_changed accessible to the user through an
|
/* We want to make media_changed accessible to the user through an
|
||||||
* ioctl. The main problem now is that we must double-buffer the
|
* ioctl. The main problem now is that we must double-buffer the
|
||||||
* low-level implementation, to assure that the VFS and the user both
|
* low-level implementation, to assure that the VFS and the user both
|
||||||
@@ -1403,15 +1441,26 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
|
|||||||
{
|
{
|
||||||
unsigned int mask = (1 << (queue & 1));
|
unsigned int mask = (1 << (queue & 1));
|
||||||
int ret = !!(cdi->mc_flags & mask);
|
int ret = !!(cdi->mc_flags & mask);
|
||||||
|
bool changed;
|
||||||
|
|
||||||
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
|
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* changed since last call? */
|
/* changed since last call? */
|
||||||
if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
|
if (cdi->ops->check_events) {
|
||||||
|
BUG_ON(!queue); /* shouldn't be called from VFS path */
|
||||||
|
cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE);
|
||||||
|
changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE;
|
||||||
|
cdi->ioctl_events = 0;
|
||||||
|
} else
|
||||||
|
changed = cdi->ops->media_changed(cdi, CDSL_CURRENT);
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
cdi->mc_flags = 0x3; /* set bit on both queues */
|
cdi->mc_flags = 0x3; /* set bit on both queues */
|
||||||
ret |= 1;
|
ret |= 1;
|
||||||
cdi->media_written = 0;
|
cdi->media_written = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cdi->mc_flags &= ~mask; /* clear bit */
|
cdi->mc_flags &= ~mask; /* clear bit */
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -946,6 +946,8 @@ struct cdrom_device_info {
|
|||||||
/* device-related storage */
|
/* device-related storage */
|
||||||
unsigned int options : 30; /* options flags */
|
unsigned int options : 30; /* options flags */
|
||||||
unsigned mc_flags : 2; /* media change buffer flags */
|
unsigned mc_flags : 2; /* media change buffer flags */
|
||||||
|
unsigned int vfs_events; /* cached events for vfs path */
|
||||||
|
unsigned int ioctl_events; /* cached events for ioctl path */
|
||||||
int use_count; /* number of times device opened */
|
int use_count; /* number of times device opened */
|
||||||
char name[20]; /* name of the device type */
|
char name[20]; /* name of the device type */
|
||||||
/* per-device flags */
|
/* per-device flags */
|
||||||
@@ -965,6 +967,8 @@ struct cdrom_device_ops {
|
|||||||
int (*open) (struct cdrom_device_info *, int);
|
int (*open) (struct cdrom_device_info *, int);
|
||||||
void (*release) (struct cdrom_device_info *);
|
void (*release) (struct cdrom_device_info *);
|
||||||
int (*drive_status) (struct cdrom_device_info *, int);
|
int (*drive_status) (struct cdrom_device_info *, int);
|
||||||
|
unsigned int (*check_events) (struct cdrom_device_info *cdi,
|
||||||
|
unsigned int clearing, int slot);
|
||||||
int (*media_changed) (struct cdrom_device_info *, int);
|
int (*media_changed) (struct cdrom_device_info *, int);
|
||||||
int (*tray_move) (struct cdrom_device_info *, int);
|
int (*tray_move) (struct cdrom_device_info *, int);
|
||||||
int (*lock_door) (struct cdrom_device_info *, int);
|
int (*lock_door) (struct cdrom_device_info *, int);
|
||||||
@@ -993,6 +997,8 @@ extern int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
|
|||||||
extern void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode);
|
extern void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode);
|
||||||
extern int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
|
extern int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
|
||||||
fmode_t mode, unsigned int cmd, unsigned long arg);
|
fmode_t mode, unsigned int cmd, unsigned long arg);
|
||||||
|
extern unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
|
||||||
|
unsigned int clearing);
|
||||||
extern int cdrom_media_changed(struct cdrom_device_info *);
|
extern int cdrom_media_changed(struct cdrom_device_info *);
|
||||||
|
|
||||||
extern int register_cdrom(struct cdrom_device_info *cdi);
|
extern int register_cdrom(struct cdrom_device_info *cdi);
|
||||||
|
Reference in New Issue
Block a user