diff options
author | Tejun Heo <tj@kernel.org> | 2010-12-08 14:57:38 -0500 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-12-16 11:53:38 -0500 |
commit | 2d9217296bfa6fdc0d3707264076e5296faffdbd (patch) | |
tree | ea929977770d95a47a4e7cbf15f13fb0e3d8dcad /drivers/cdrom/cdrom.c | |
parent | 77ea887e433ad8389d416826936c110fa7910f80 (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>
Diffstat (limited to 'drivers/cdrom/cdrom.c')
-rw-r--r-- | drivers/cdrom/cdrom.c | 55 |
1 files changed, 52 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 | */ | ||
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 | |||
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 | } |