can: c_can: don't call pm_runtime_get_sync() from interrupt context
authorMarc Kleine-Budde <mkl@pengutronix.de>
Sun, 24 Nov 2013 22:31:24 +0000 (23:31 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Dec 2013 06:36:26 +0000 (22:36 -0800)
commit e35d46adc49b469fd92bdb64fea8af93640e6651 upstream.

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>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/can/c_can/c_can.c

index e3fc07cf2f6269e5ccf0ca5dbf42897c191cecd4..e59c42b446a9b7f32a335e8b17f7b2950352f448 100644 (file)
@@ -712,22 +712,31 @@ static int c_can_set_mode(struct net_device *dev, enum can_mode mode)
        return 0;
 }
 
-static int c_can_get_berr_counter(const struct net_device *dev,
-                                       struct can_berr_counter *bec)
+static int __c_can_get_berr_counter(const struct net_device *dev,
+                                   struct can_berr_counter *bec)
 {
        unsigned int reg_err_counter;
        struct c_can_priv *priv = netdev_priv(dev);
 
-       c_can_pm_runtime_get_sync(priv);
-
        reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG);
        bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >>
                                ERR_CNT_REC_SHIFT;
        bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK;
 
+       return 0;
+}
+
+static int c_can_get_berr_counter(const struct net_device *dev,
+                                 struct can_berr_counter *bec)
+{
+       struct c_can_priv *priv = netdev_priv(dev);
+       int err;
+
+       c_can_pm_runtime_get_sync(priv);
+       err = __c_can_get_berr_counter(dev, bec);
        c_can_pm_runtime_put_sync(priv);
 
-       return 0;
+       return err;
 }
 
 /*
@@ -872,7 +881,7 @@ static int c_can_handle_state_change(struct net_device *dev,
        if (unlikely(!skb))
                return 0;
 
-       c_can_get_berr_counter(dev, &bec);
+       __c_can_get_berr_counter(dev, &bec);
        reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG);
        rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >>
                                ERR_CNT_RP_SHIFT;