aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-12-08 14:57:38 -0500
committerJens Axboe <jaxboe@fusionio.com>2010-12-16 11:53:38 -0500
commit2d9217296bfa6fdc0d3707264076e5296faffdbd (patch)
treeea929977770d95a47a4e7cbf15f13fb0e3d8dcad
parent77ea887e433ad8389d416826936c110fa7910f80 (diff)
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>
-rw-r--r--drivers/cdrom/cdrom.c55
-rw-r--r--include/linux/cdrom.h6
2 files changed, 58 insertions, 3 deletions
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index af13c62dc473..1ea8f8d363c6 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1348,7 +1348,10 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
1348 if (!CDROM_CAN(CDC_SELECT_DISC)) 1348 if (!CDROM_CAN(CDC_SELECT_DISC))
1349 return -EDRIVE_CANT_DO_THIS; 1349 return -EDRIVE_CANT_DO_THIS;
1350 1350
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);
1352 1355
1353 if (slot == CDSL_NONE) { 1356 if (slot == CDSL_NONE) {
1354 /* set media changed bits, on both queues */ 1357 /* set media changed bits, on both queues */
@@ -1392,6 +1395,41 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
1392 return slot; 1395 return slot;
1393} 1396}
1394 1397
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 */
1412static 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
1422unsigned 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
1395/* We want to make media_changed accessible to the user through an 1433/* We want to make media_changed accessible to the user through an
1396 * ioctl. The main problem now is that we must double-buffer the 1434 * ioctl. The main problem now is that we must double-buffer the
1397 * low-level implementation, to assure that the VFS and the user both 1435 * 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)
1403{ 1441{
1404 unsigned int mask = (1 << (queue & 1)); 1442 unsigned int mask = (1 << (queue & 1));
1405 int ret = !!(cdi->mc_flags & mask); 1443 int ret = !!(cdi->mc_flags & mask);
1444 bool changed;
1406 1445
1407 if (!CDROM_CAN(CDC_MEDIA_CHANGED)) 1446 if (!CDROM_CAN(CDC_MEDIA_CHANGED))
1408 return ret; 1447 return ret;
1448
1409 /* changed since last call? */ 1449 /* changed since last call? */
1410 if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) { 1450 if (cdi->ops->check_events) {
1451 BUG_ON(!queue); /* shouldn't be called from VFS path */
1452 cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE);
1453 changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE;
1454 cdi->ioctl_events = 0;
1455 } else
1456 changed = cdi->ops->media_changed(cdi, CDSL_CURRENT);
1457
1458 if (changed) {
1411 cdi->mc_flags = 0x3; /* set bit on both queues */ 1459 cdi->mc_flags = 0x3; /* set bit on both queues */
1412 ret |= 1; 1460 ret |= 1;
1413 cdi->media_written = 0; 1461 cdi->media_written = 0;
1414 } 1462 }
1463
1415 cdi->mc_flags &= ~mask; /* clear bit */ 1464 cdi->mc_flags &= ~mask; /* clear bit */
1416 return ret; 1465 return ret;
1417} 1466}
diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h
index 78e904796622..35eae4b67503 100644
--- a/include/linux/cdrom.h
+++ b/include/linux/cdrom.h
@@ -946,6 +946,8 @@ struct cdrom_device_info {
946/* device-related storage */ 946/* device-related storage */
947 unsigned int options : 30; /* options flags */ 947 unsigned int options : 30; /* options flags */
948 unsigned mc_flags : 2; /* media change buffer flags */ 948 unsigned mc_flags : 2; /* media change buffer flags */
949 unsigned int vfs_events; /* cached events for vfs path */
950 unsigned int ioctl_events; /* cached events for ioctl path */
949 int use_count; /* number of times device opened */ 951 int use_count; /* number of times device opened */
950 char name[20]; /* name of the device type */ 952 char name[20]; /* name of the device type */
951/* per-device flags */ 953/* per-device flags */
@@ -965,6 +967,8 @@ struct cdrom_device_ops {
965 int (*open) (struct cdrom_device_info *, int); 967 int (*open) (struct cdrom_device_info *, int);
966 void (*release) (struct cdrom_device_info *); 968 void (*release) (struct cdrom_device_info *);
967 int (*drive_status) (struct cdrom_device_info *, int); 969 int (*drive_status) (struct cdrom_device_info *, int);
970 unsigned int (*check_events) (struct cdrom_device_info *cdi,
971 unsigned int clearing, int slot);
968 int (*media_changed) (struct cdrom_device_info *, int); 972 int (*media_changed) (struct cdrom_device_info *, int);
969 int (*tray_move) (struct cdrom_device_info *, int); 973 int (*tray_move) (struct cdrom_device_info *, int);
970 int (*lock_door) (struct cdrom_device_info *, int); 974 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,
993extern void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode); 997extern void cdrom_release(struct cdrom_device_info *cdi, fmode_t mode);
994extern int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev, 998extern int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
995 fmode_t mode, unsigned int cmd, unsigned long arg); 999 fmode_t mode, unsigned int cmd, unsigned long arg);
1000extern unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
1001 unsigned int clearing);
996extern int cdrom_media_changed(struct cdrom_device_info *); 1002extern int cdrom_media_changed(struct cdrom_device_info *);
997 1003
998extern int register_cdrom(struct cdrom_device_info *cdi); 1004extern int register_cdrom(struct cdrom_device_info *cdi);