rtc: remove BKL for ioctl()

Remove implicit use of BKL in ioctl() from the RTC framework.

Instead, the rtc->ops_lock is used.  That's the same lock that already
protects the RTC operations when they're issued through the exported
rtc_*() calls in drivers/rtc/interface.c ...  making this a bugfix, not
just a cleanup, since both ioctl calls and set_alarm() need to update IRQ
enable flags and that implies a common lock (which RTC drivers as a rule
do not provide on their own).

A new comment at the declaration of "struct rtc_class_ops" summarizes
current locking rules.  It's not clear to me that the exceptions listed
there should exist ...  if not, those are pre-existing problems which can
be fixed in a patch that doesn't relate to BKL removal.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Jonathan Corbet <corbet@lwn.net>
Acked-by: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
David Brownell 2008-07-23 21:30:33 -07:00 committed by Linus Torvalds
parent 53f1b1433d
commit 5ad31a5751
2 changed files with 55 additions and 20 deletions

View File

@ -209,7 +209,7 @@ static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
return (data != 0) ? (POLLIN | POLLRDNORM) : 0; return (data != 0) ? (POLLIN | POLLRDNORM) : 0;
} }
static int rtc_dev_ioctl(struct inode *inode, struct file *file, static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int err = 0; int err = 0;
@ -219,6 +219,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
struct rtc_wkalrm alarm; struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg; void __user *uarg = (void __user *) arg;
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
/* check that the calling task has appropriate permissions /* check that the calling task has appropriate permissions
* for certain ioctls. doing this check here is useful * for certain ioctls. doing this check here is useful
* to avoid duplicate code in each driver. * to avoid duplicate code in each driver.
@ -227,26 +231,31 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
case RTC_EPOCH_SET: case RTC_EPOCH_SET:
case RTC_SET_TIME: case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME)) if (!capable(CAP_SYS_TIME))
return -EACCES; err = -EACCES;
break; break;
case RTC_IRQP_SET: case RTC_IRQP_SET:
if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE)) if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
return -EACCES; err = -EACCES;
break; break;
case RTC_PIE_ON: case RTC_PIE_ON:
if (rtc->irq_freq > rtc->max_user_freq && if (rtc->irq_freq > rtc->max_user_freq &&
!capable(CAP_SYS_RESOURCE)) !capable(CAP_SYS_RESOURCE))
return -EACCES; err = -EACCES;
break; break;
} }
if (err)
goto done;
/* try the driver's ioctl interface */ /* try the driver's ioctl interface */
if (ops->ioctl) { if (ops->ioctl) {
err = ops->ioctl(rtc->dev.parent, cmd, arg); err = ops->ioctl(rtc->dev.parent, cmd, arg);
if (err != -ENOIOCTLCMD) if (err != -ENOIOCTLCMD) {
mutex_unlock(&rtc->ops_lock);
return err; return err;
}
} }
/* if the driver does not provide the ioctl interface /* if the driver does not provide the ioctl interface
@ -265,15 +274,19 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
switch (cmd) { switch (cmd) {
case RTC_ALM_READ: case RTC_ALM_READ:
mutex_unlock(&rtc->ops_lock);
err = rtc_read_alarm(rtc, &alarm); err = rtc_read_alarm(rtc, &alarm);
if (err < 0) if (err < 0)
return err; return err;
if (copy_to_user(uarg, &alarm.time, sizeof(tm))) if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
return -EFAULT; err = -EFAULT;
break; return err;
case RTC_ALM_SET: case RTC_ALM_SET:
mutex_unlock(&rtc->ops_lock);
if (copy_from_user(&alarm.time, uarg, sizeof(tm))) if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
return -EFAULT; return -EFAULT;
@ -321,24 +334,26 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
} }
} }
err = rtc_set_alarm(rtc, &alarm); return rtc_set_alarm(rtc, &alarm);
break;
case RTC_RD_TIME: case RTC_RD_TIME:
mutex_unlock(&rtc->ops_lock);
err = rtc_read_time(rtc, &tm); err = rtc_read_time(rtc, &tm);
if (err < 0) if (err < 0)
return err; return err;
if (copy_to_user(uarg, &tm, sizeof(tm))) if (copy_to_user(uarg, &tm, sizeof(tm)))
return -EFAULT; err = -EFAULT;
break; return err;
case RTC_SET_TIME: case RTC_SET_TIME:
mutex_unlock(&rtc->ops_lock);
if (copy_from_user(&tm, uarg, sizeof(tm))) if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT; return -EFAULT;
err = rtc_set_time(rtc, &tm); return rtc_set_time(rtc, &tm);
break;
case RTC_PIE_ON: case RTC_PIE_ON:
err = rtc_irq_set_state(rtc, NULL, 1); err = rtc_irq_set_state(rtc, NULL, 1);
@ -376,34 +391,37 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
break; break;
#endif #endif
case RTC_WKALM_SET: case RTC_WKALM_SET:
mutex_unlock(&rtc->ops_lock);
if (copy_from_user(&alarm, uarg, sizeof(alarm))) if (copy_from_user(&alarm, uarg, sizeof(alarm)))
return -EFAULT; return -EFAULT;
err = rtc_set_alarm(rtc, &alarm); return rtc_set_alarm(rtc, &alarm);
break;
case RTC_WKALM_RD: case RTC_WKALM_RD:
mutex_unlock(&rtc->ops_lock);
err = rtc_read_alarm(rtc, &alarm); err = rtc_read_alarm(rtc, &alarm);
if (err < 0) if (err < 0)
return err; return err;
if (copy_to_user(uarg, &alarm, sizeof(alarm))) if (copy_to_user(uarg, &alarm, sizeof(alarm)))
return -EFAULT; err = -EFAULT;
break; return err;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
case RTC_UIE_OFF: case RTC_UIE_OFF:
clear_uie(rtc); clear_uie(rtc);
return 0; break;
case RTC_UIE_ON: case RTC_UIE_ON:
return set_uie(rtc); err = set_uie(rtc);
#endif #endif
default: default:
err = -ENOTTY; err = -ENOTTY;
break; break;
} }
done:
mutex_unlock(&rtc->ops_lock);
return err; return err;
} }
@ -432,7 +450,7 @@ static const struct file_operations rtc_dev_fops = {
.llseek = no_llseek, .llseek = no_llseek,
.read = rtc_dev_read, .read = rtc_dev_read,
.poll = rtc_dev_poll, .poll = rtc_dev_poll,
.ioctl = rtc_dev_ioctl, .unlocked_ioctl = rtc_dev_ioctl,
.open = rtc_dev_open, .open = rtc_dev_open,
.release = rtc_dev_release, .release = rtc_dev_release,
.fasync = rtc_dev_fasync, .fasync = rtc_dev_fasync,

View File

@ -115,6 +115,23 @@ extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm);
extern struct class *rtc_class; extern struct class *rtc_class;
/*
* For these RTC methods the device parameter is the physical device
* on whatever bus holds the hardware (I2C, Platform, SPI, etc), which
* was passed to rtc_device_register(). Its driver_data normally holds
* device state, including the rtc_device pointer for the RTC.
*
* Most of these methods are called with rtc_device.ops_lock held,
* through the rtc_*(struct rtc_device *, ...) calls.
*
* The (current) exceptions are mostly filesystem hooks:
* - the proc() hook for procfs
* - non-ioctl() chardev hooks: open(), release(), read_callback()
* - periodic irq calls: irq_set_state(), irq_set_freq()
*
* REVISIT those periodic irq calls *do* have ops_lock when they're
* issued through ioctl() ...
*/
struct rtc_class_ops { struct rtc_class_ops {
int (*open)(struct device *); int (*open)(struct device *);
void (*release)(struct device *); void (*release)(struct device *);