drivers/cdrom: improved ioctl for media change detection
The current implementation of the CDROM_MEDIA_CHANGED ioctl relies on global state, meaning that only one process can detect a disc change while the ioctl call will return 0 for other calling processes afterwards (see bug 213267). This introduces a new cdrom ioctl, CDROM_TIMED_MEDIA_CHANGE, that works by maintaining a timestamp of the last detected disc change instead of a boolean flag: Processes calling this ioctl command can provide a timestamp of the last disc change known to them and receive an indication whether the disc was changed since then and the updated timestamp. I considered fixing the buggy behavior in the original CDROM_MEDIA_CHANGED ioctl but that would require maintaining state for each calling process in the kernel, which seems like a worse solution than introducing this new ioctl. Signed-off-by: Lukas Prediger <lumip@lumip.de> Link: https://lore.kernel.org/all/20210912191207.74449-1-lumip@lumip.de Signed-off-by: Phillip Potter <phil@philpotter.co.uk> Link: https://lore.kernel.org/r/20210913230942.1188-1-phil@philpotter.co.uk Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
316346243b
commit
67f1e027c2
5 changed files with 89 additions and 4 deletions
|
@ -907,6 +907,17 @@ commands can be identified by the underscores in their names.
|
||||||
specifies the slot for which the information is given. The special
|
specifies the slot for which the information is given. The special
|
||||||
value *CDSL_CURRENT* requests that information about the currently
|
value *CDSL_CURRENT* requests that information about the currently
|
||||||
selected slot be returned.
|
selected slot be returned.
|
||||||
|
`CDROM_TIMED_MEDIA_CHANGE`
|
||||||
|
Checks whether the disc has been changed since a user supplied time
|
||||||
|
and returns the time of the last disc change.
|
||||||
|
|
||||||
|
*arg* is a pointer to a *cdrom_timed_media_change_info* struct.
|
||||||
|
*arg->last_media_change* may be set by calling code to signal
|
||||||
|
the timestamp of the last known media change (by the caller).
|
||||||
|
Upon successful return, this ioctl call will set
|
||||||
|
*arg->last_media_change* to the latest media change timestamp (in ms)
|
||||||
|
known by the kernel/driver and set *arg->has_changed* to 1 if
|
||||||
|
that timestamp is more recent than the timestamp set by the caller.
|
||||||
`CDROM_DRIVE_STATUS`
|
`CDROM_DRIVE_STATUS`
|
||||||
Returns the status of the drive by a call to
|
Returns the status of the drive by a call to
|
||||||
*drive_status()*. Return values are defined in cdrom_drive_status_.
|
*drive_status()*. Return values are defined in cdrom_drive_status_.
|
||||||
|
|
|
@ -54,6 +54,9 @@ are as follows:
|
||||||
CDROM_SELECT_SPEED Set the CD-ROM speed
|
CDROM_SELECT_SPEED Set the CD-ROM speed
|
||||||
CDROM_SELECT_DISC Select disc (for juke-boxes)
|
CDROM_SELECT_DISC Select disc (for juke-boxes)
|
||||||
CDROM_MEDIA_CHANGED Check is media changed
|
CDROM_MEDIA_CHANGED Check is media changed
|
||||||
|
CDROM_TIMED_MEDIA_CHANGE Check if media changed
|
||||||
|
since given time
|
||||||
|
(struct cdrom_timed_media_change_info)
|
||||||
CDROM_DRIVE_STATUS Get tray position, etc.
|
CDROM_DRIVE_STATUS Get tray position, etc.
|
||||||
CDROM_DISC_STATUS Get disc type, etc.
|
CDROM_DISC_STATUS Get disc type, etc.
|
||||||
CDROM_CHANGER_NSLOTS Get number of slots
|
CDROM_CHANGER_NSLOTS Get number of slots
|
||||||
|
|
|
@ -344,6 +344,12 @@ static void cdrom_sysctl_register(void);
|
||||||
|
|
||||||
static LIST_HEAD(cdrom_list);
|
static LIST_HEAD(cdrom_list);
|
||||||
|
|
||||||
|
static void signal_media_change(struct cdrom_device_info *cdi)
|
||||||
|
{
|
||||||
|
cdi->mc_flags = 0x3; /* set media changed bits, on both queues */
|
||||||
|
cdi->last_media_change_ms = ktime_to_ms(ktime_get());
|
||||||
|
}
|
||||||
|
|
||||||
int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi,
|
int cdrom_dummy_generic_packet(struct cdrom_device_info *cdi,
|
||||||
struct packet_command *cgc)
|
struct packet_command *cgc)
|
||||||
{
|
{
|
||||||
|
@ -616,6 +622,7 @@ int register_cdrom(struct gendisk *disk, struct cdrom_device_info *cdi)
|
||||||
ENSURE(cdo, generic_packet, CDC_GENERIC_PACKET);
|
ENSURE(cdo, generic_packet, CDC_GENERIC_PACKET);
|
||||||
cdi->mc_flags = 0;
|
cdi->mc_flags = 0;
|
||||||
cdi->options = CDO_USE_FFLAGS;
|
cdi->options = CDO_USE_FFLAGS;
|
||||||
|
cdi->last_media_change_ms = ktime_to_ms(ktime_get());
|
||||||
|
|
||||||
if (autoclose == 1 && CDROM_CAN(CDC_CLOSE_TRAY))
|
if (autoclose == 1 && CDROM_CAN(CDC_CLOSE_TRAY))
|
||||||
cdi->options |= (int) CDO_AUTO_CLOSE;
|
cdi->options |= (int) CDO_AUTO_CLOSE;
|
||||||
|
@ -1421,8 +1428,7 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
|
||||||
cdi->ops->check_events(cdi, 0, slot);
|
cdi->ops->check_events(cdi, 0, slot);
|
||||||
|
|
||||||
if (slot == CDSL_NONE) {
|
if (slot == CDSL_NONE) {
|
||||||
/* set media changed bits, on both queues */
|
signal_media_change(cdi);
|
||||||
cdi->mc_flags = 0x3;
|
|
||||||
return cdrom_load_unload(cdi, -1);
|
return cdrom_load_unload(cdi, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1455,7 +1461,7 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
|
||||||
slot = curslot;
|
slot = curslot;
|
||||||
|
|
||||||
/* set media changed bits on both queues */
|
/* set media changed bits on both queues */
|
||||||
cdi->mc_flags = 0x3;
|
signal_media_change(cdi);
|
||||||
if ((ret = cdrom_load_unload(cdi, slot)))
|
if ((ret = cdrom_load_unload(cdi, slot)))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1521,7 +1527,7 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
|
||||||
cdi->ioctl_events = 0;
|
cdi->ioctl_events = 0;
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
cdi->mc_flags = 0x3; /* set bit on both queues */
|
signal_media_change(cdi);
|
||||||
ret |= 1;
|
ret |= 1;
|
||||||
cdi->media_written = 0;
|
cdi->media_written = 0;
|
||||||
}
|
}
|
||||||
|
@ -2336,6 +2342,49 @@ static int cdrom_ioctl_media_changed(struct cdrom_device_info *cdi,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Media change detection with timing information.
|
||||||
|
*
|
||||||
|
* arg is a pointer to a cdrom_timed_media_change_info struct.
|
||||||
|
* arg->last_media_change may be set by calling code to signal
|
||||||
|
* the timestamp (in ms) of the last known media change (by the caller).
|
||||||
|
* Upon successful return, ioctl call will set arg->last_media_change
|
||||||
|
* to the latest media change timestamp known by the kernel/driver
|
||||||
|
* and set arg->has_changed to 1 if that timestamp is more recent
|
||||||
|
* than the timestamp set by the caller.
|
||||||
|
*/
|
||||||
|
static int cdrom_ioctl_timed_media_change(struct cdrom_device_info *cdi,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct cdrom_timed_media_change_info __user *info;
|
||||||
|
struct cdrom_timed_media_change_info tmp_info;
|
||||||
|
|
||||||
|
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
|
||||||
|
return -ENOSYS;
|
||||||
|
|
||||||
|
info = (struct cdrom_timed_media_change_info __user *)arg;
|
||||||
|
cd_dbg(CD_DO_IOCTL, "entering CDROM_TIMED_MEDIA_CHANGE\n");
|
||||||
|
|
||||||
|
ret = cdrom_ioctl_media_changed(cdi, CDSL_CURRENT);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (copy_from_user(&tmp_info, info, sizeof(tmp_info)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
tmp_info.media_flags = 0;
|
||||||
|
if (tmp_info.last_media_change - cdi->last_media_change_ms < 0)
|
||||||
|
tmp_info.media_flags |= MEDIA_CHANGED_FLAG;
|
||||||
|
|
||||||
|
tmp_info.last_media_change = cdi->last_media_change_ms;
|
||||||
|
|
||||||
|
if (copy_to_user(info, &tmp_info, sizeof(*info)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi,
|
static int cdrom_ioctl_set_options(struct cdrom_device_info *cdi,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
|
@ -3313,6 +3362,8 @@ int cdrom_ioctl(struct cdrom_device_info *cdi, struct block_device *bdev,
|
||||||
return cdrom_ioctl_eject_sw(cdi, arg);
|
return cdrom_ioctl_eject_sw(cdi, arg);
|
||||||
case CDROM_MEDIA_CHANGED:
|
case CDROM_MEDIA_CHANGED:
|
||||||
return cdrom_ioctl_media_changed(cdi, arg);
|
return cdrom_ioctl_media_changed(cdi, arg);
|
||||||
|
case CDROM_TIMED_MEDIA_CHANGE:
|
||||||
|
return cdrom_ioctl_timed_media_change(cdi, arg);
|
||||||
case CDROM_SET_OPTIONS:
|
case CDROM_SET_OPTIONS:
|
||||||
return cdrom_ioctl_set_options(cdi, arg);
|
return cdrom_ioctl_set_options(cdi, arg);
|
||||||
case CDROM_CLEAR_OPTIONS:
|
case CDROM_CLEAR_OPTIONS:
|
||||||
|
|
|
@ -64,6 +64,7 @@ struct cdrom_device_info {
|
||||||
int for_data;
|
int for_data;
|
||||||
int (*exit)(struct cdrom_device_info *);
|
int (*exit)(struct cdrom_device_info *);
|
||||||
int mrw_mode_page;
|
int mrw_mode_page;
|
||||||
|
__s64 last_media_change_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cdrom_device_ops {
|
struct cdrom_device_ops {
|
||||||
|
|
|
@ -147,6 +147,8 @@
|
||||||
#define CDROM_NEXT_WRITABLE 0x5394 /* get next writable block */
|
#define CDROM_NEXT_WRITABLE 0x5394 /* get next writable block */
|
||||||
#define CDROM_LAST_WRITTEN 0x5395 /* get last block written on disc */
|
#define CDROM_LAST_WRITTEN 0x5395 /* get last block written on disc */
|
||||||
|
|
||||||
|
#define CDROM_TIMED_MEDIA_CHANGE 0x5396 /* get the timestamp of the last media change */
|
||||||
|
|
||||||
/*******************************************************
|
/*******************************************************
|
||||||
* CDROM IOCTL structures
|
* CDROM IOCTL structures
|
||||||
*******************************************************/
|
*******************************************************/
|
||||||
|
@ -295,6 +297,23 @@ struct cdrom_generic_command
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This struct is used by CDROM_TIMED_MEDIA_CHANGE */
|
||||||
|
struct cdrom_timed_media_change_info {
|
||||||
|
__s64 last_media_change; /* Timestamp of the last detected media
|
||||||
|
* change in ms. May be set by caller,
|
||||||
|
* updated upon successful return of
|
||||||
|
* ioctl.
|
||||||
|
*/
|
||||||
|
__u64 media_flags; /* Flags returned by ioctl to indicate
|
||||||
|
* media status.
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
#define MEDIA_CHANGED_FLAG 0x1 /* Last detected media change was more
|
||||||
|
* recent than last_media_change set by
|
||||||
|
* caller.
|
||||||
|
*/
|
||||||
|
/* other bits of media_flags available for future use */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336,
|
* A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336,
|
||||||
* 2340, or 2352 bytes long.
|
* 2340, or 2352 bytes long.
|
||||||
|
|
Loading…
Add table
Reference in a new issue