diff options
author | Marc Kleine-Budde <mkl@pengutronix.de> | 2013-11-24 17:31:24 -0500 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2013-11-25 15:48:51 -0500 |
commit | e35d46adc49b469fd92bdb64fea8af93640e6651 (patch) | |
tree | 450dc5d0e82ae23c24cdd2374ba578f904b81b2f /drivers/net/can | |
parent | 2fea6cd303c0d0cd9067da31d873b6a6d5bd75e7 (diff) |
can: c_can: don't call pm_runtime_get_sync() from interrupt context
The c_can driver contians a callpath (c_can_poll -> c_can_state_change ->
c_can_get_berr_counter) which may call pm_runtime_get_sync() from the IRQ
handler, which is not allowed and results in "BUG: scheduling while atomic".
This problem is fixed by introducing __c_can_get_berr_counter, which will not
call pm_runtime_get_sync().
Reported-by: Andrew Glen <AGlen@bepmarine.com>
Tested-by: Andrew Glen <AGlen@bepmarine.com>
Signed-off-by: Andrew Glen <AGlen@bepmarine.com>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'drivers/net/can')
-rw-r--r-- | drivers/net/can/c_can/c_can.c | 21 |
1 files changed, 15 insertions, 6 deletions
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index e3fc07cf2f62..e59c42b446a9 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c | |||
@@ -712,22 +712,31 @@ static int c_can_set_mode(struct net_device *dev, enum can_mode mode) | |||
712 | return 0; | 712 | return 0; |
713 | } | 713 | } |
714 | 714 | ||
715 | static int c_can_get_berr_counter(const struct net_device *dev, | 715 | static int __c_can_get_berr_counter(const struct net_device *dev, |
716 | struct can_berr_counter *bec) | 716 | struct can_berr_counter *bec) |
717 | { | 717 | { |
718 | unsigned int reg_err_counter; | 718 | unsigned int reg_err_counter; |
719 | struct c_can_priv *priv = netdev_priv(dev); | 719 | struct c_can_priv *priv = netdev_priv(dev); |
720 | 720 | ||
721 | c_can_pm_runtime_get_sync(priv); | ||
722 | |||
723 | reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); | 721 | reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); |
724 | bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> | 722 | bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> |
725 | ERR_CNT_REC_SHIFT; | 723 | ERR_CNT_REC_SHIFT; |
726 | bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; | 724 | bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; |
727 | 725 | ||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static int c_can_get_berr_counter(const struct net_device *dev, | ||
730 | struct can_berr_counter *bec) | ||
731 | { | ||
732 | struct c_can_priv *priv = netdev_priv(dev); | ||
733 | int err; | ||
734 | |||
735 | c_can_pm_runtime_get_sync(priv); | ||
736 | err = __c_can_get_berr_counter(dev, bec); | ||
728 | c_can_pm_runtime_put_sync(priv); | 737 | c_can_pm_runtime_put_sync(priv); |
729 | 738 | ||
730 | return 0; | 739 | return err; |
731 | } | 740 | } |
732 | 741 | ||
733 | /* | 742 | /* |
@@ -872,7 +881,7 @@ static int c_can_handle_state_change(struct net_device *dev, | |||
872 | if (unlikely(!skb)) | 881 | if (unlikely(!skb)) |
873 | return 0; | 882 | return 0; |
874 | 883 | ||
875 | c_can_get_berr_counter(dev, &bec); | 884 | __c_can_get_berr_counter(dev, &bec); |
876 | reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); | 885 | reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); |
877 | rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >> | 886 | rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >> |
878 | ERR_CNT_RP_SHIFT; | 887 | ERR_CNT_RP_SHIFT; |