aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2014-03-18 13:19:10 -0400
committerMarc Kleine-Budde <mkl@pengutronix.de>2014-04-01 05:54:58 -0400
commit64f08f2f3544eb8b6b14fd35e6087d7d3ede77cd (patch)
treeea75d2125f91762caedc2d287ad90b496046ebd2
parent640916db2bf7de7c5ac13a1f470fa959459ccf6d (diff)
can: c_can: Fix buffer ordering
The buffer handling of c_can has been broken forever. That leads to message reordering: ksoftirqd/0-3 [000] ..s. 79.123776: c_can_poll: rx_poll: val: 00007fff ksoftirqd/0-3 [000] ..s. 79.124101: c_can_poll: rx_poll: val: 00008001 What happens is: CPU HW queue new packet into obj 16 (0-15 are busy) read obj 1-15 return because pending is 0 set pending obj 16 -> pending reg 8000 queue new packet into obj 1 set pending obj 1 -> pending reg 8001 So the current algorithmus reads the newest message first, which violates the ordering rules of CAN. Add proper handling of that situation by analyzing the contents of the pending register for gaps. This does NOT fix the message object corruption which can lead to interrupt storms. Thats addressed in the next patches. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> [mkl: adjusted subject] Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r--drivers/net/can/c_can/c_can.c52
1 files changed, 50 insertions, 2 deletions
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 38f9adaf15ac..cef9967eff93 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -817,6 +817,38 @@ static void c_can_do_tx(struct net_device *dev)
817} 817}
818 818
819/* 819/*
820 * If we have a gap in the pending bits, that means we either
821 * raced with the hardware or failed to readout all upper
822 * objects in the last run due to quota limit.
823 */
824static u32 c_can_adjust_pending(u32 pend)
825{
826 u32 weight, lasts;
827
828 if (pend == RECEIVE_OBJECT_BITS)
829 return pend;
830
831 /*
832 * If the last set bit is larger than the number of pending
833 * bits we have a gap.
834 */
835 weight = hweight32(pend);
836 lasts = fls(pend);
837
838 /* If the bits are linear, nothing to do */
839 if (lasts == weight)
840 return pend;
841
842 /*
843 * Find the first set bit after the gap. We walk backwards
844 * from the last set bit.
845 */
846 for (lasts--; pend & (1 << (lasts - 1)); lasts--);
847
848 return pend & ~((1 << lasts) - 1);
849}
850
851/*
820 * theory of operation: 852 * theory of operation:
821 * 853 *
822 * c_can core saves a received CAN message into the first free message 854 * c_can core saves a received CAN message into the first free message
@@ -843,7 +875,7 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
843 u32 num_rx_pkts = 0; 875 u32 num_rx_pkts = 0;
844 unsigned int msg_obj, msg_ctrl_save; 876 unsigned int msg_obj, msg_ctrl_save;
845 struct c_can_priv *priv = netdev_priv(dev); 877 struct c_can_priv *priv = netdev_priv(dev);
846 u16 val; 878 u32 val, pend = 0;
847 879
848 /* 880 /*
849 * It is faster to read only one 16bit register. This is only possible 881 * It is faster to read only one 16bit register. This is only possible
@@ -852,7 +884,23 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota)
852 BUILD_BUG_ON_MSG(C_CAN_MSG_OBJ_RX_LAST > 16, 884 BUILD_BUG_ON_MSG(C_CAN_MSG_OBJ_RX_LAST > 16,
853 "Implementation does not support more message objects than 16"); 885 "Implementation does not support more message objects than 16");
854 886
855 while (quota > 0 && (val = priv->read_reg(priv, C_CAN_INTPND1_REG))) { 887 while (quota > 0) {
888
889 if (!pend) {
890 pend = priv->read_reg(priv, C_CAN_INTPND1_REG);
891 if (!pend)
892 return num_rx_pkts;
893 /*
894 * If the pending field has a gap, handle the
895 * bits above the gap first.
896 */
897 val = c_can_adjust_pending(pend);
898 } else {
899 val = pend;
900 }
901 /* Remove the bits from pend */
902 pend &= ~val;
903
856 while ((msg_obj = ffs(val)) && quota > 0) { 904 while ((msg_obj = ffs(val)) && quota > 0) {
857 val &= ~BIT(msg_obj - 1); 905 val &= ~BIT(msg_obj - 1);
858 906