Skip to content

Commit

Permalink
CRIS: gpio: don't call copy_to_user()/copy_from_user() while holding …
Browse files Browse the repository at this point in the history
…spinlocks

copy_to_user()/copy_from_user() must not be used with spinlocks held.
Move locks inside each case so we have better control of when the locks
are held.

Also, since we use spinlocks, we don't need to hold the BKL, so remove it.

Reported-by: Kulikov Vasiliy <segooon@gmail.com>
Signed-off-by: Jesper Nilsson <jesper.nilsson@axis.com>
  • Loading branch information
Jesper Nilsson committed Aug 4, 2010
1 parent 16bc0fe commit 6036215
Showing 1 changed file with 48 additions and 32 deletions.
80 changes: 48 additions & 32 deletions arch/cris/arch-v10/drivers/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/poll.h>
#include <linux/init.h>
Expand Down Expand Up @@ -46,7 +45,7 @@ static char gpio_name[] = "etrax gpio";
static wait_queue_head_t *gpio_wq;
#endif

static int gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static ssize_t gpio_write(struct file *file, const char __user *buf,
size_t count, loff_t *off);
static int gpio_open(struct inode *inode, struct file *filp);
Expand Down Expand Up @@ -323,7 +322,6 @@ gpio_open(struct inode *inode, struct file *filp)
if (!priv)
return -ENOMEM;

lock_kernel();
priv->minor = p;

/* initialize the io/alarm struct */
Expand Down Expand Up @@ -358,7 +356,6 @@ gpio_open(struct inode *inode, struct file *filp)
alarmlist = priv;
spin_unlock_irqrestore(&gpio_lock, flags);

unlock_kernel();
return 0;
}

Expand Down Expand Up @@ -503,8 +500,7 @@ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
static int
gpio_leds_ioctl(unsigned int cmd, unsigned long arg);

static int
gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
unsigned long val;
Expand All @@ -514,54 +510,65 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
return -EINVAL;

spin_lock_irqsave(&gpio_lock, flags);

switch (_IOC_NR(cmd)) {
case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
// read the port
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
ret = *priv->port;
} else if (priv->minor == GPIO_MINOR_G) {
ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
}
spin_unlock_irqrestore(&gpio_lock, flags);

break;
case IO_SETBITS:
// set changeable bits with a 1 in arg
spin_lock_irqsave(&gpio_lock, flags);

if (USE_PORTS(priv)) {
*priv->port = *priv->shadow |=
*priv->port = *priv->shadow |=
((unsigned char)arg & priv->changeable_bits);
} else if (priv->minor == GPIO_MINOR_G) {
*R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
}
spin_unlock_irqrestore(&gpio_lock, flags);

break;
case IO_CLRBITS:
// clear changeable bits with a 1 in arg
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
*priv->port = *priv->shadow &=
*priv->port = *priv->shadow &=
~((unsigned char)arg & priv->changeable_bits);
} else if (priv->minor == GPIO_MINOR_G) {
*R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_HIGHALARM:
// set alarm when bits with 1 in arg go high
spin_lock_irqsave(&gpio_lock, flags);
priv->highalarm |= arg;
gpio_some_alarms = 1;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_LOWALARM:
// set alarm when bits with 1 in arg go low
spin_lock_irqsave(&gpio_lock, flags);
priv->lowalarm |= arg;
gpio_some_alarms = 1;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_CLRALARM:
// clear alarm for bits with 1 in arg
/* clear alarm for bits with 1 in arg */
spin_lock_irqsave(&gpio_lock, flags);
priv->highalarm &= ~arg;
priv->lowalarm &= ~arg;
{
/* Must update gpio_some_alarms */
struct gpio_private *p = alarmlist;
int some_alarms;
spin_lock_irq(&gpio_lock);
p = alarmlist;
some_alarms = 0;
while (p) {
Expand All @@ -572,11 +579,12 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
p = p->next;
}
gpio_some_alarms = some_alarms;
spin_unlock_irq(&gpio_lock);
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
/* Read direction 0=input 1=output */
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
ret = *priv->dir_shadow;
} else if (priv->minor == GPIO_MINOR_G) {
Expand All @@ -585,30 +593,40 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
*/
ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
/* Set direction 0=unchanged 1=input,
* return mask with 1=input
/* Set direction 0=unchanged 1=input,
* return mask with 1=input
*/
spin_lock_irqsave(&gpio_lock, flags);
ret = setget_input(priv, arg) & 0x7FFFFFFF;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
/* Set direction 0=unchanged 1=output,
* return mask with 1=output
/* Set direction 0=unchanged 1=output,
* return mask with 1=output
*/
spin_lock_irqsave(&gpio_lock, flags);
ret = setget_output(priv, arg) & 0x7FFFFFFF;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SHUTDOWN:
spin_lock_irqsave(&gpio_lock, flags);
SOFT_SHUTDOWN();
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_GET_PWR_BT:
spin_lock_irqsave(&gpio_lock, flags);
#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
#else
ret = 0;
#endif
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_CFG_WRITE_MODE:
spin_lock_irqsave(&gpio_lock, flags);
priv->clk_mask = arg & 0xFF;
priv->data_mask = (arg >> 8) & 0xFF;
priv->write_msb = (arg >> 16) & 0x01;
Expand All @@ -624,28 +642,33 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
priv->data_mask = 0;
ret = -EPERM;
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_READ_INBITS:
case IO_READ_INBITS:
/* *arg is result of reading the input pins */
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
val = *priv->port;
} else if (priv->minor == GPIO_MINOR_G) {
val = *R_PORT_G_DATA;
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
ret = -EFAULT;
break;
case IO_READ_OUTBITS:
/* *arg is result of reading the output shadow */
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
val = *priv->shadow;
} else if (priv->minor == GPIO_MINOR_G) {
val = port_g_data_shadow;
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
ret = -EFAULT;
break;
case IO_SETGET_INPUT:
case IO_SETGET_INPUT:
/* bits set in *arg is set to input,
* *arg updated with current input pins.
*/
Expand All @@ -654,7 +677,9 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
ret = -EFAULT;
break;
}
spin_lock_irqsave(&gpio_lock, flags);
val = setget_input(priv, val);
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
ret = -EFAULT;
break;
Expand All @@ -666,33 +691,24 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
ret = -EFAULT;
break;
}
spin_lock_irqsave(&gpio_lock, flags);
val = setget_output(priv, val);
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
ret = -EFAULT;
break;
default:
spin_lock_irqsave(&gpio_lock, flags);
if (priv->minor == GPIO_MINOR_LEDS)
ret = gpio_leds_ioctl(cmd, arg);
else
ret = -EINVAL;
spin_unlock_irqrestore(&gpio_lock, flags);
} /* switch */

spin_unlock_irqrestore(&gpio_lock, flags);
return ret;
}

static int
gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret;

lock_kernel();
ret = gpio_ioctl_unlocked(file, cmd, arg);
unlock_kernel();

return ret;
}

static int
gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
{
Expand Down

0 comments on commit 6036215

Please sign in to comment.