diff options
author | Jesper Nilsson <jesper.nilsson@axis.com> | 2010-08-02 07:17:05 -0400 |
---|---|---|
committer | Jesper Nilsson <jesper.nilsson@axis.com> | 2010-08-04 06:59:14 -0400 |
commit | 60362158e2419b20cc04d43a6ffa60c1845775dc (patch) | |
tree | 5c255d6b869bd98592b862a31dc79110df91557a /arch/cris/arch-v10 | |
parent | 16bc0fe5ce84df89c8e802be210af88721d4cc4f (diff) |
CRIS: gpio: don't call copy_to_user()/copy_from_user() while holding 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>
Diffstat (limited to 'arch/cris/arch-v10')
-rw-r--r-- | arch/cris/arch-v10/drivers/gpio.c | 80 |
1 files changed, 48 insertions, 32 deletions
diff --git a/arch/cris/arch-v10/drivers/gpio.c b/arch/cris/arch-v10/drivers/gpio.c index 080927ffe63b..a07b6d25b0c7 100644 --- a/arch/cris/arch-v10/drivers/gpio.c +++ b/arch/cris/arch-v10/drivers/gpio.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
19 | #include <linux/smp_lock.h> | ||
20 | #include <linux/string.h> | 19 | #include <linux/string.h> |
21 | #include <linux/poll.h> | 20 | #include <linux/poll.h> |
22 | #include <linux/init.h> | 21 | #include <linux/init.h> |
@@ -46,7 +45,7 @@ static char gpio_name[] = "etrax gpio"; | |||
46 | static wait_queue_head_t *gpio_wq; | 45 | static wait_queue_head_t *gpio_wq; |
47 | #endif | 46 | #endif |
48 | 47 | ||
49 | static int gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | 48 | static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); |
50 | static ssize_t gpio_write(struct file *file, const char __user *buf, | 49 | static ssize_t gpio_write(struct file *file, const char __user *buf, |
51 | size_t count, loff_t *off); | 50 | size_t count, loff_t *off); |
52 | static int gpio_open(struct inode *inode, struct file *filp); | 51 | static int gpio_open(struct inode *inode, struct file *filp); |
@@ -323,7 +322,6 @@ gpio_open(struct inode *inode, struct file *filp) | |||
323 | if (!priv) | 322 | if (!priv) |
324 | return -ENOMEM; | 323 | return -ENOMEM; |
325 | 324 | ||
326 | lock_kernel(); | ||
327 | priv->minor = p; | 325 | priv->minor = p; |
328 | 326 | ||
329 | /* initialize the io/alarm struct */ | 327 | /* initialize the io/alarm struct */ |
@@ -358,7 +356,6 @@ gpio_open(struct inode *inode, struct file *filp) | |||
358 | alarmlist = priv; | 356 | alarmlist = priv; |
359 | spin_unlock_irqrestore(&gpio_lock, flags); | 357 | spin_unlock_irqrestore(&gpio_lock, flags); |
360 | 358 | ||
361 | unlock_kernel(); | ||
362 | return 0; | 359 | return 0; |
363 | } | 360 | } |
364 | 361 | ||
@@ -503,8 +500,7 @@ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) | |||
503 | static int | 500 | static int |
504 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg); | 501 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg); |
505 | 502 | ||
506 | static int | 503 | static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
507 | gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | ||
508 | { | 504 | { |
509 | unsigned long flags; | 505 | unsigned long flags; |
510 | unsigned long val; | 506 | unsigned long val; |
@@ -514,54 +510,65 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | |||
514 | if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) | 510 | if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) |
515 | return -EINVAL; | 511 | return -EINVAL; |
516 | 512 | ||
517 | spin_lock_irqsave(&gpio_lock, flags); | ||
518 | |||
519 | switch (_IOC_NR(cmd)) { | 513 | switch (_IOC_NR(cmd)) { |
520 | case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ | 514 | case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ |
521 | // read the port | 515 | // read the port |
516 | spin_lock_irqsave(&gpio_lock, flags); | ||
522 | if (USE_PORTS(priv)) { | 517 | if (USE_PORTS(priv)) { |
523 | ret = *priv->port; | 518 | ret = *priv->port; |
524 | } else if (priv->minor == GPIO_MINOR_G) { | 519 | } else if (priv->minor == GPIO_MINOR_G) { |
525 | ret = (*R_PORT_G_DATA) & 0x7FFFFFFF; | 520 | ret = (*R_PORT_G_DATA) & 0x7FFFFFFF; |
526 | } | 521 | } |
522 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
523 | |||
527 | break; | 524 | break; |
528 | case IO_SETBITS: | 525 | case IO_SETBITS: |
529 | // set changeable bits with a 1 in arg | 526 | // set changeable bits with a 1 in arg |
527 | spin_lock_irqsave(&gpio_lock, flags); | ||
528 | |||
530 | if (USE_PORTS(priv)) { | 529 | if (USE_PORTS(priv)) { |
531 | *priv->port = *priv->shadow |= | 530 | *priv->port = *priv->shadow |= |
532 | ((unsigned char)arg & priv->changeable_bits); | 531 | ((unsigned char)arg & priv->changeable_bits); |
533 | } else if (priv->minor == GPIO_MINOR_G) { | 532 | } else if (priv->minor == GPIO_MINOR_G) { |
534 | *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); | 533 | *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); |
535 | } | 534 | } |
535 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
536 | |||
536 | break; | 537 | break; |
537 | case IO_CLRBITS: | 538 | case IO_CLRBITS: |
538 | // clear changeable bits with a 1 in arg | 539 | // clear changeable bits with a 1 in arg |
540 | spin_lock_irqsave(&gpio_lock, flags); | ||
539 | if (USE_PORTS(priv)) { | 541 | if (USE_PORTS(priv)) { |
540 | *priv->port = *priv->shadow &= | 542 | *priv->port = *priv->shadow &= |
541 | ~((unsigned char)arg & priv->changeable_bits); | 543 | ~((unsigned char)arg & priv->changeable_bits); |
542 | } else if (priv->minor == GPIO_MINOR_G) { | 544 | } else if (priv->minor == GPIO_MINOR_G) { |
543 | *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); | 545 | *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); |
544 | } | 546 | } |
547 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
545 | break; | 548 | break; |
546 | case IO_HIGHALARM: | 549 | case IO_HIGHALARM: |
547 | // set alarm when bits with 1 in arg go high | 550 | // set alarm when bits with 1 in arg go high |
551 | spin_lock_irqsave(&gpio_lock, flags); | ||
548 | priv->highalarm |= arg; | 552 | priv->highalarm |= arg; |
549 | gpio_some_alarms = 1; | 553 | gpio_some_alarms = 1; |
554 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
550 | break; | 555 | break; |
551 | case IO_LOWALARM: | 556 | case IO_LOWALARM: |
552 | // set alarm when bits with 1 in arg go low | 557 | // set alarm when bits with 1 in arg go low |
558 | spin_lock_irqsave(&gpio_lock, flags); | ||
553 | priv->lowalarm |= arg; | 559 | priv->lowalarm |= arg; |
554 | gpio_some_alarms = 1; | 560 | gpio_some_alarms = 1; |
561 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
555 | break; | 562 | break; |
556 | case IO_CLRALARM: | 563 | case IO_CLRALARM: |
557 | // clear alarm for bits with 1 in arg | 564 | /* clear alarm for bits with 1 in arg */ |
565 | spin_lock_irqsave(&gpio_lock, flags); | ||
558 | priv->highalarm &= ~arg; | 566 | priv->highalarm &= ~arg; |
559 | priv->lowalarm &= ~arg; | 567 | priv->lowalarm &= ~arg; |
560 | { | 568 | { |
561 | /* Must update gpio_some_alarms */ | 569 | /* Must update gpio_some_alarms */ |
562 | struct gpio_private *p = alarmlist; | 570 | struct gpio_private *p = alarmlist; |
563 | int some_alarms; | 571 | int some_alarms; |
564 | spin_lock_irq(&gpio_lock); | ||
565 | p = alarmlist; | 572 | p = alarmlist; |
566 | some_alarms = 0; | 573 | some_alarms = 0; |
567 | while (p) { | 574 | while (p) { |
@@ -572,11 +579,12 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | |||
572 | p = p->next; | 579 | p = p->next; |
573 | } | 580 | } |
574 | gpio_some_alarms = some_alarms; | 581 | gpio_some_alarms = some_alarms; |
575 | spin_unlock_irq(&gpio_lock); | ||
576 | } | 582 | } |
583 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
577 | break; | 584 | break; |
578 | case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ | 585 | case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ |
579 | /* Read direction 0=input 1=output */ | 586 | /* Read direction 0=input 1=output */ |
587 | spin_lock_irqsave(&gpio_lock, flags); | ||
580 | if (USE_PORTS(priv)) { | 588 | if (USE_PORTS(priv)) { |
581 | ret = *priv->dir_shadow; | 589 | ret = *priv->dir_shadow; |
582 | } else if (priv->minor == GPIO_MINOR_G) { | 590 | } else if (priv->minor == GPIO_MINOR_G) { |
@@ -585,30 +593,40 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | |||
585 | */ | 593 | */ |
586 | ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; | 594 | ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; |
587 | } | 595 | } |
596 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
588 | break; | 597 | break; |
589 | case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ | 598 | case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ |
590 | /* Set direction 0=unchanged 1=input, | 599 | /* Set direction 0=unchanged 1=input, |
591 | * return mask with 1=input | 600 | * return mask with 1=input |
592 | */ | 601 | */ |
602 | spin_lock_irqsave(&gpio_lock, flags); | ||
593 | ret = setget_input(priv, arg) & 0x7FFFFFFF; | 603 | ret = setget_input(priv, arg) & 0x7FFFFFFF; |
604 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
594 | break; | 605 | break; |
595 | case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ | 606 | case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ |
596 | /* Set direction 0=unchanged 1=output, | 607 | /* Set direction 0=unchanged 1=output, |
597 | * return mask with 1=output | 608 | * return mask with 1=output |
598 | */ | 609 | */ |
610 | spin_lock_irqsave(&gpio_lock, flags); | ||
599 | ret = setget_output(priv, arg) & 0x7FFFFFFF; | 611 | ret = setget_output(priv, arg) & 0x7FFFFFFF; |
612 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
600 | break; | 613 | break; |
601 | case IO_SHUTDOWN: | 614 | case IO_SHUTDOWN: |
615 | spin_lock_irqsave(&gpio_lock, flags); | ||
602 | SOFT_SHUTDOWN(); | 616 | SOFT_SHUTDOWN(); |
617 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
603 | break; | 618 | break; |
604 | case IO_GET_PWR_BT: | 619 | case IO_GET_PWR_BT: |
620 | spin_lock_irqsave(&gpio_lock, flags); | ||
605 | #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) | 621 | #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) |
606 | ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); | 622 | ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); |
607 | #else | 623 | #else |
608 | ret = 0; | 624 | ret = 0; |
609 | #endif | 625 | #endif |
626 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
610 | break; | 627 | break; |
611 | case IO_CFG_WRITE_MODE: | 628 | case IO_CFG_WRITE_MODE: |
629 | spin_lock_irqsave(&gpio_lock, flags); | ||
612 | priv->clk_mask = arg & 0xFF; | 630 | priv->clk_mask = arg & 0xFF; |
613 | priv->data_mask = (arg >> 8) & 0xFF; | 631 | priv->data_mask = (arg >> 8) & 0xFF; |
614 | priv->write_msb = (arg >> 16) & 0x01; | 632 | priv->write_msb = (arg >> 16) & 0x01; |
@@ -624,28 +642,33 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | |||
624 | priv->data_mask = 0; | 642 | priv->data_mask = 0; |
625 | ret = -EPERM; | 643 | ret = -EPERM; |
626 | } | 644 | } |
645 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
627 | break; | 646 | break; |
628 | case IO_READ_INBITS: | 647 | case IO_READ_INBITS: |
629 | /* *arg is result of reading the input pins */ | 648 | /* *arg is result of reading the input pins */ |
649 | spin_lock_irqsave(&gpio_lock, flags); | ||
630 | if (USE_PORTS(priv)) { | 650 | if (USE_PORTS(priv)) { |
631 | val = *priv->port; | 651 | val = *priv->port; |
632 | } else if (priv->minor == GPIO_MINOR_G) { | 652 | } else if (priv->minor == GPIO_MINOR_G) { |
633 | val = *R_PORT_G_DATA; | 653 | val = *R_PORT_G_DATA; |
634 | } | 654 | } |
655 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
635 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 656 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
636 | ret = -EFAULT; | 657 | ret = -EFAULT; |
637 | break; | 658 | break; |
638 | case IO_READ_OUTBITS: | 659 | case IO_READ_OUTBITS: |
639 | /* *arg is result of reading the output shadow */ | 660 | /* *arg is result of reading the output shadow */ |
661 | spin_lock_irqsave(&gpio_lock, flags); | ||
640 | if (USE_PORTS(priv)) { | 662 | if (USE_PORTS(priv)) { |
641 | val = *priv->shadow; | 663 | val = *priv->shadow; |
642 | } else if (priv->minor == GPIO_MINOR_G) { | 664 | } else if (priv->minor == GPIO_MINOR_G) { |
643 | val = port_g_data_shadow; | 665 | val = port_g_data_shadow; |
644 | } | 666 | } |
667 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
645 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 668 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
646 | ret = -EFAULT; | 669 | ret = -EFAULT; |
647 | break; | 670 | break; |
648 | case IO_SETGET_INPUT: | 671 | case IO_SETGET_INPUT: |
649 | /* bits set in *arg is set to input, | 672 | /* bits set in *arg is set to input, |
650 | * *arg updated with current input pins. | 673 | * *arg updated with current input pins. |
651 | */ | 674 | */ |
@@ -654,7 +677,9 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | |||
654 | ret = -EFAULT; | 677 | ret = -EFAULT; |
655 | break; | 678 | break; |
656 | } | 679 | } |
680 | spin_lock_irqsave(&gpio_lock, flags); | ||
657 | val = setget_input(priv, val); | 681 | val = setget_input(priv, val); |
682 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
658 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 683 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
659 | ret = -EFAULT; | 684 | ret = -EFAULT; |
660 | break; | 685 | break; |
@@ -666,34 +691,25 @@ gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg) | |||
666 | ret = -EFAULT; | 691 | ret = -EFAULT; |
667 | break; | 692 | break; |
668 | } | 693 | } |
694 | spin_lock_irqsave(&gpio_lock, flags); | ||
669 | val = setget_output(priv, val); | 695 | val = setget_output(priv, val); |
696 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
670 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 697 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) |
671 | ret = -EFAULT; | 698 | ret = -EFAULT; |
672 | break; | 699 | break; |
673 | default: | 700 | default: |
701 | spin_lock_irqsave(&gpio_lock, flags); | ||
674 | if (priv->minor == GPIO_MINOR_LEDS) | 702 | if (priv->minor == GPIO_MINOR_LEDS) |
675 | ret = gpio_leds_ioctl(cmd, arg); | 703 | ret = gpio_leds_ioctl(cmd, arg); |
676 | else | 704 | else |
677 | ret = -EINVAL; | 705 | ret = -EINVAL; |
706 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
678 | } /* switch */ | 707 | } /* switch */ |
679 | 708 | ||
680 | spin_unlock_irqrestore(&gpio_lock, flags); | ||
681 | return ret; | 709 | return ret; |
682 | } | 710 | } |
683 | 711 | ||
684 | static int | 712 | static int |
685 | gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
686 | { | ||
687 | long ret; | ||
688 | |||
689 | lock_kernel(); | ||
690 | ret = gpio_ioctl_unlocked(file, cmd, arg); | ||
691 | unlock_kernel(); | ||
692 | |||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | static int | ||
697 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg) | 713 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg) |
698 | { | 714 | { |
699 | unsigned char green; | 715 | unsigned char green; |