Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 4/4] net: ethernet: ti: cpsw: allow vlan tagged packets to be timestamped
From: Ivan Khoronzhuk @ 2018-11-12 14:00 UTC (permalink / raw)
  To: grygorii.strashko, davem
  Cc: linux-omap, netdev, linux-kernel, Ivan Khoronzhuk
In-Reply-To: <20181112140023.12407-1-ivan.khoronzhuk@linaro.org>

Allow vlan tagged packets to be timestamped, as no any restrictions
for this.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpsw.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 48ea21e7e0cb..e4aa030f1726 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -283,7 +283,7 @@ struct cpsw_ss_regs {
 
 #define CTRL_V2_TS_BITS \
 	(TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
-	 TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN)
+	 TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN | VLAN_LTYPE1_EN)
 
 #define CTRL_V2_ALL_TS_MASK (CTRL_V2_TS_BITS | TS_TX_EN | TS_RX_EN)
 #define CTRL_V2_TX_TS_BITS  (CTRL_V2_TS_BITS | TS_TX_EN)
@@ -293,7 +293,7 @@ struct cpsw_ss_regs {
 #define CTRL_V3_TS_BITS \
 	(TS_107 | TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
 	 TS_TTL_NONZERO | TS_ANNEX_F_EN | TS_ANNEX_D_EN |\
-	 TS_LTYPE1_EN)
+	 TS_LTYPE1_EN | VLAN_LTYPE1_EN)
 
 #define CTRL_V3_ALL_TS_MASK (CTRL_V3_TS_BITS | TS_TX_EN | TS_RX_EN)
 #define CTRL_V3_TX_TS_BITS  (CTRL_V3_TS_BITS | TS_TX_EN)
@@ -2235,6 +2235,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
 	slave_write(slave, mtype, CPSW2_TS_SEQ_MTYPE);
 	slave_write(slave, ctrl, CPSW2_CONTROL);
 	writel_relaxed(ETH_P_1588, &cpsw->regs->ts_ltype);
+	writel_relaxed(ETH_P_8021Q, &cpsw->regs->vlan_ltype);
 }
 
 static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
-- 
2.17.1

^ permalink raw reply related

* [PATCH net-next 3/4] net: ethernet: ti: cpts: move enable/disable flags outside of cpts module
From: Ivan Khoronzhuk @ 2018-11-12 14:00 UTC (permalink / raw)
  To: grygorii.strashko, davem
  Cc: linux-omap, netdev, linux-kernel, Ivan Khoronzhuk
In-Reply-To: <20181112140023.12407-1-ivan.khoronzhuk@linaro.org>

Each slave has it's own receive timestamp filter. But cpts rx/tx
timestamp enable flags are used to allow ts retrieve only for one
user. This limitation causes data path redundancy and setting overlap
if cpsw module is in dual-mac mode for instance.

If rx ts is enabled only for one port - the second interface must expect
every incoming packet to be PTP packet w/o absolutely any reason, and if
it's PTP - do unneeded stuff, as rx filter for second port is not set
and cpts fifo is not supposed to contain appropriate ts event.
That's not correct.

So, to fix control overlap and avoid redundant CPU cycles, the patch
splits rx/tx ts enable flags between network devices. After the patch,
PTP timestamping still should be used for only one port (or PTP id
counter has to be different for both ports as cpts IP is common).

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpsw.c        | 46 ++++++++++++++-------------
 drivers/net/ethernet/ti/cpts.c        |  2 --
 drivers/net/ethernet/ti/cpts.h        | 38 ----------------------
 drivers/net/ethernet/ti/netcp_ethss.c | 32 ++++++++++---------
 4 files changed, 41 insertions(+), 77 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 9434fd5a5477..48ea21e7e0cb 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -466,6 +466,8 @@ struct cpsw_priv {
 	bool				mqprio_hw;
 	int				fifo_bw[CPSW_TC_NUM];
 	int				shp_cfg_speed;
+	int				tx_ts_enabled;
+	int				rx_ts_enabled;
 	u32 emac_port;
 	struct cpsw_common *cpsw;
 };
@@ -905,6 +907,7 @@ static void cpsw_rx_handler(void *token, int len, int status)
 	struct net_device	*ndev = skb->dev;
 	int			ret = 0, port;
 	struct cpsw_common	*cpsw = ndev_to_cpsw(ndev);
+	struct cpsw_priv	*priv;
 
 	if (cpsw->data.dual_emac) {
 		port = CPDMA_RX_SOURCE_PORT(status);
@@ -939,7 +942,9 @@ static void cpsw_rx_handler(void *token, int len, int status)
 		skb_put(skb, len);
 		if (status & CPDMA_RX_VLAN_ENCAP)
 			cpsw_rx_vlan_encap(skb);
-		cpts_rx_timestamp(cpsw->cpts, skb);
+		priv = netdev_priv(ndev);
+		if (priv->rx_ts_enabled)
+			cpts_rx_timestamp(cpsw->cpts, skb);
 		skb->protocol = eth_type_trans(skb, ndev);
 		netif_receive_skb(skb);
 		ndev->stats.rx_bytes += len;
@@ -2126,7 +2131,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
 	}
 
 	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
-	    cpts_is_tx_enabled(cpts) && cpts_can_timestamp(cpts, skb))
+	    priv->tx_ts_enabled && cpts_can_timestamp(cpts, skb))
 		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 
 	q_idx = skb_get_queue_mapping(skb);
@@ -2170,13 +2175,13 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb,
 
 #if IS_ENABLED(CONFIG_TI_CPTS)
 
-static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
+static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
 {
+	struct cpsw_common *cpsw = priv->cpsw;
 	struct cpsw_slave *slave = &cpsw->slaves[cpsw->data.active_slave];
 	u32 ts_en, seq_id;
 
-	if (!cpts_is_tx_enabled(cpsw->cpts) &&
-	    !cpts_is_rx_enabled(cpsw->cpts)) {
+	if (!priv->tx_ts_enabled && !priv->rx_ts_enabled) {
 		slave_write(slave, 0, CPSW1_TS_CTL);
 		return;
 	}
@@ -2184,10 +2189,10 @@ static void cpsw_hwtstamp_v1(struct cpsw_common *cpsw)
 	seq_id = (30 << CPSW_V1_SEQ_ID_OFS_SHIFT) | ETH_P_1588;
 	ts_en = EVENT_MSG_BITS << CPSW_V1_MSG_TYPE_OFS;
 
-	if (cpts_is_tx_enabled(cpsw->cpts))
+	if (priv->tx_ts_enabled)
 		ts_en |= CPSW_V1_TS_TX_EN;
 
-	if (cpts_is_rx_enabled(cpsw->cpts))
+	if (priv->rx_ts_enabled)
 		ts_en |= CPSW_V1_TS_RX_EN;
 
 	slave_write(slave, ts_en, CPSW1_TS_CTL);
@@ -2207,20 +2212,20 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
 	case CPSW_VERSION_2:
 		ctrl &= ~CTRL_V2_ALL_TS_MASK;
 
-		if (cpts_is_tx_enabled(cpsw->cpts))
+		if (priv->tx_ts_enabled)
 			ctrl |= CTRL_V2_TX_TS_BITS;
 
-		if (cpts_is_rx_enabled(cpsw->cpts))
+		if (priv->rx_ts_enabled)
 			ctrl |= CTRL_V2_RX_TS_BITS;
 		break;
 	case CPSW_VERSION_3:
 	default:
 		ctrl &= ~CTRL_V3_ALL_TS_MASK;
 
-		if (cpts_is_tx_enabled(cpsw->cpts))
+		if (priv->tx_ts_enabled)
 			ctrl |= CTRL_V3_TX_TS_BITS;
 
-		if (cpts_is_rx_enabled(cpsw->cpts))
+		if (priv->rx_ts_enabled)
 			ctrl |= CTRL_V3_RX_TS_BITS;
 		break;
 	}
@@ -2237,7 +2242,6 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 	struct cpsw_priv *priv = netdev_priv(dev);
 	struct hwtstamp_config cfg;
 	struct cpsw_common *cpsw = priv->cpsw;
-	struct cpts *cpts = cpsw->cpts;
 
 	if (cpsw->version != CPSW_VERSION_1 &&
 	    cpsw->version != CPSW_VERSION_2 &&
@@ -2256,7 +2260,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 
 	switch (cfg.rx_filter) {
 	case HWTSTAMP_FILTER_NONE:
-		cpts_rx_enable(cpts, 0);
+		priv->rx_ts_enabled = 0;
 		break;
 	case HWTSTAMP_FILTER_ALL:
 	case HWTSTAMP_FILTER_NTP_ALL:
@@ -2264,7 +2268,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
 	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
 	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-		cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
+		priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
 		break;
 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -2276,18 +2280,18 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
-		cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT);
+		priv->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
 		break;
 	default:
 		return -ERANGE;
 	}
 
-	cpts_tx_enable(cpts, cfg.tx_type == HWTSTAMP_TX_ON);
+	priv->tx_ts_enabled = cfg.tx_type == HWTSTAMP_TX_ON;
 
 	switch (cpsw->version) {
 	case CPSW_VERSION_1:
-		cpsw_hwtstamp_v1(cpsw);
+		cpsw_hwtstamp_v1(priv);
 		break;
 	case CPSW_VERSION_2:
 	case CPSW_VERSION_3:
@@ -2303,7 +2307,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
 {
 	struct cpsw_common *cpsw = ndev_to_cpsw(dev);
-	struct cpts *cpts = cpsw->cpts;
+	struct cpsw_priv *priv = netdev_priv(dev);
 	struct hwtstamp_config cfg;
 
 	if (cpsw->version != CPSW_VERSION_1 &&
@@ -2312,10 +2316,8 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
 		return -EOPNOTSUPP;
 
 	cfg.flags = 0;
-	cfg.tx_type = cpts_is_tx_enabled(cpts) ?
-		      HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-	cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
-			 cpts->rx_enable : HWTSTAMP_FILTER_NONE);
+	cfg.tx_type = priv->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+	cfg.rx_filter = priv->rx_ts_enabled;
 
 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
index 63232b35024e..054f78295d1d 100644
--- a/drivers/net/ethernet/ti/cpts.c
+++ b/drivers/net/ethernet/ti/cpts.c
@@ -430,8 +430,6 @@ void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 	u64 ns;
 	struct skb_shared_hwtstamps *ssh;
 
-	if (!cpts->rx_enable)
-		return;
 	ns = cpts_find_ts(cpts, skb, CPTS_EV_RX);
 	if (!ns)
 		return;
diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h
index 73d73faf0f38..d2c7decd59b6 100644
--- a/drivers/net/ethernet/ti/cpts.h
+++ b/drivers/net/ethernet/ti/cpts.h
@@ -136,26 +136,6 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
 			 struct device_node *node);
 void cpts_release(struct cpts *cpts);
 
-static inline void cpts_rx_enable(struct cpts *cpts, int enable)
-{
-	cpts->rx_enable = enable;
-}
-
-static inline bool cpts_is_rx_enabled(struct cpts *cpts)
-{
-	return !!cpts->rx_enable;
-}
-
-static inline void cpts_tx_enable(struct cpts *cpts, int enable)
-{
-	cpts->tx_enable = enable;
-}
-
-static inline bool cpts_is_tx_enabled(struct cpts *cpts)
-{
-	return !!cpts->tx_enable;
-}
-
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
 	unsigned int class = ptp_classify_raw(skb);
@@ -197,24 +177,6 @@ static inline void cpts_unregister(struct cpts *cpts)
 {
 }
 
-static inline void cpts_rx_enable(struct cpts *cpts, int enable)
-{
-}
-
-static inline bool cpts_is_rx_enabled(struct cpts *cpts)
-{
-	return false;
-}
-
-static inline void cpts_tx_enable(struct cpts *cpts, int enable)
-{
-}
-
-static inline bool cpts_is_tx_enabled(struct cpts *cpts)
-{
-	return false;
-}
-
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
 	return false;
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 0397ccb6597e..20d81e0b1c29 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -763,6 +763,8 @@ struct gbe_priv {
 
 	int                             cpts_registered;
 	struct cpts                     *cpts;
+	int				rx_ts_enabled;
+	int				tx_ts_enabled;
 };
 
 struct gbe_intf {
@@ -2564,7 +2566,7 @@ static int gbe_txtstamp_mark_pkt(struct gbe_intf *gbe_intf,
 	struct gbe_priv *gbe_dev = gbe_intf->gbe_dev;
 
 	if (!(skb_shinfo(p_info->skb)->tx_flags & SKBTX_HW_TSTAMP) ||
-	    !cpts_is_tx_enabled(gbe_dev->cpts))
+	    !gbe_dev->tx_ts_enabled)
 		return 0;
 
 	/* If phy has the txtstamp api, assume it will do it.
@@ -2598,7 +2600,9 @@ static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info)
 		return 0;
 	}
 
-	cpts_rx_timestamp(gbe_dev->cpts, p_info->skb);
+	if (gbe_dev->rx_ts_enabled)
+		cpts_rx_timestamp(gbe_dev->cpts, p_info->skb);
+
 	p_info->rxtstamp_complete = true;
 
 	return 0;
@@ -2614,10 +2618,8 @@ static int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 		return -EOPNOTSUPP;
 
 	cfg.flags = 0;
-	cfg.tx_type = cpts_is_tx_enabled(cpts) ?
-		      HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-	cfg.rx_filter = (cpts_is_rx_enabled(cpts) ?
-			 cpts->rx_enable : HWTSTAMP_FILTER_NONE);
+	cfg.tx_type = gbe_dev->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+	cfg.rx_filter = gbe_dev->rx_ts_enabled;
 
 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
@@ -2628,8 +2630,8 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
 	struct gbe_slave *slave = gbe_intf->slave;
 	u32 ts_en, seq_id, ctl;
 
-	if (!cpts_is_rx_enabled(gbe_dev->cpts) &&
-	    !cpts_is_tx_enabled(gbe_dev->cpts)) {
+	if (!gbe_dev->rx_ts_enabled &&
+	    !gbe_dev->tx_ts_enabled) {
 		writel(0, GBE_REG_ADDR(slave, port_regs, ts_ctl));
 		return;
 	}
@@ -2641,10 +2643,10 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
 		(slave->ts_ctl.uni ?  TS_UNI_EN :
 			slave->ts_ctl.maddr_map << TS_CTL_MADDR_SHIFT);
 
-	if (cpts_is_tx_enabled(gbe_dev->cpts))
+	if (gbe_dev->tx_ts_enabled)
 		ts_en |= (TS_TX_ANX_ALL_EN | TS_TX_VLAN_LT1_EN);
 
-	if (cpts_is_rx_enabled(gbe_dev->cpts))
+	if (gbe_dev->rx_ts_enabled)
 		ts_en |= (TS_RX_ANX_ALL_EN | TS_RX_VLAN_LT1_EN);
 
 	writel(ts_en,  GBE_REG_ADDR(slave, port_regs, ts_ctl));
@@ -2670,10 +2672,10 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 
 	switch (cfg.tx_type) {
 	case HWTSTAMP_TX_OFF:
-		cpts_tx_enable(cpts, 0);
+		gbe_dev->tx_ts_enabled = 0;
 		break;
 	case HWTSTAMP_TX_ON:
-		cpts_tx_enable(cpts, 1);
+		gbe_dev->tx_ts_enabled = 1;
 		break;
 	default:
 		return -ERANGE;
@@ -2681,12 +2683,12 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 
 	switch (cfg.rx_filter) {
 	case HWTSTAMP_FILTER_NONE:
-		cpts_rx_enable(cpts, 0);
+		gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_NONE;
 		break;
 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
 	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
 	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
-		cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V1_L4_EVENT);
+		gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
 		break;
 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -2698,7 +2700,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
-		cpts_rx_enable(cpts, HWTSTAMP_FILTER_PTP_V2_EVENT);
+		gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
 		break;
 	default:
-- 
2.17.1

^ permalink raw reply related

* [PATCH net-next 1/4] net: ethernet: ti: cpts: correct debug for expired txq skb
From: Ivan Khoronzhuk @ 2018-11-12 14:00 UTC (permalink / raw)
  To: grygorii.strashko, davem
  Cc: linux-omap, netdev, linux-kernel, Ivan Khoronzhuk
In-Reply-To: <20181112140023.12407-1-ivan.khoronzhuk@linaro.org>

The msgtype and seqid that is smth that belongs to event for
comparison but not for staled txq skb.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 drivers/net/ethernet/ti/cpts.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
index b96b93c686bf..dac4c528a1ff 100644
--- a/drivers/net/ethernet/ti/cpts.c
+++ b/drivers/net/ethernet/ti/cpts.c
@@ -119,9 +119,7 @@ static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
 
 		if (time_after(jiffies, skb_cb->tmo)) {
 			/* timeout any expired skbs over 1s */
-			dev_dbg(cpts->dev,
-				"expiring tx timestamp mtype %u seqid %04x\n",
-				mtype, seqid);
+			dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
 			__skb_unlink(skb, &cpts->txq);
 			dev_consume_skb_any(skb);
 		}
-- 
2.17.1

^ permalink raw reply related

* [PATCH net-next 0/4] net: ethernet: ti: cpsw: allow vlan h/w timestamping
From: Ivan Khoronzhuk @ 2018-11-12 14:00 UTC (permalink / raw)
  To: grygorii.strashko, davem
  Cc: linux-omap, netdev, linux-kernel, Ivan Khoronzhuk

The patchset adds several improvements and allows vlan h/w ts.

Based on net-next/master

Ivan Khoronzhuk (4):
  net: ethernet: ti: cpts: correct debug for expired txq skb
  net: ethernet: ti: cpts: purge staled skbs from txq
  net: ethernet: ti: cpts: move enable/disable flags outside of cpts
    module
  net: ethernet: ti: cpsw: allow vlan tagged packets to be timestamped

 drivers/net/ethernet/ti/cpsw.c        | 51 ++++++++++++++-------------
 drivers/net/ethernet/ti/cpts.c        | 32 +++++++++++++----
 drivers/net/ethernet/ti/cpts.h        | 38 --------------------
 drivers/net/ethernet/ti/netcp_ethss.c | 32 +++++++++--------
 4 files changed, 69 insertions(+), 84 deletions(-)

-- 
2.17.1

^ permalink raw reply

* Re: (2) [PATCH net] ip: hash fragments consistently
From: Eric Dumazet @ 2018-11-12  3:37 UTC (permalink / raw)
  To: soukjin.bae, Paolo Abeni, netdev@vger.kernel.org; +Cc: David S. Miller
In-Reply-To: <20181112004054epcms1p3db41e04dad77c59e675a769e07f4615a@epcms1p3>



On 11/11/2018 04:40 PM, 배석진 wrote:
>> Also we might note that flow dissector itself is buggy as
>> found by Soukjin Bae ( https://patchwork.ozlabs.org/patch/994601/ )
>>  
>> I will send a v2 of his patch with a different changelog.
>>  
>> Defrag is fixed [1] but the bug in flow dissector is adding
>> extra work and hash inconsistencies.
>>  
>> [1] https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=0d5b9311baf27bb545f187f12ecfd558220c607d
> 
> 
> Dear all,
> 
> it's good news to mee too.
> thanks for accept my work :)

Sure thing ;)

^ permalink raw reply

* Re: [PATCH net-next] NFC: nci: remove redundant variable 'status'
From: YueHaibing @ 2018-11-12 13:20 UTC (permalink / raw)
  To: davem, sameo; +Cc: linux-kernel, netdev
In-Reply-To: <c168a8ff-0736-d21d-9c5c-bde001ad018c@huawei.com>

Ping.

On 2018/9/13 20:06, YueHaibing wrote:
> Ping.
> 
> On 2018/8/1 15:20, YueHaibing wrote:
>> After commit d8cd37ed2fc8 ("NFC: nci: Fix improper management of HCI return code")
>> variable 'status' is being assigned but never used,
>> so can be removed. Also make a trival cleanup.
>>
>> Signed-off-by: YueHaibing <yuehaibing@huawei.com>
>> ---
>>  net/nfc/nci/hci.c | 10 ++--------
>>  1 file changed, 2 insertions(+), 8 deletions(-)
>>
>> diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c
>> index ddfc52a..4debba8 100644
>> --- a/net/nfc/nci/hci.c
>> +++ b/net/nfc/nci/hci.c
>> @@ -370,17 +370,11 @@ static void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe,
>>  				  u8 result, struct sk_buff *skb)
>>  {
>>  	struct nci_conn_info    *conn_info;
>> -	u8 status = result;
>>  
>>  	conn_info = ndev->hci_dev->conn_info;
>> -	if (!conn_info) {
>> -		status = NCI_STATUS_REJECTED;
>> -		goto exit;
>> -	}
>> -
>> -	conn_info->rx_skb = skb;
>> +	if (conn_info)
>> +		conn_info->rx_skb = skb;
>>  
>> -exit:
>>  	nci_req_complete(ndev, NCI_STATUS_OK);
>>  }
>>  
>>
> 
> 
> .
> 

^ permalink raw reply

* Re: [PATCH tip/core/rcu 2/7] sfc: Replace spin_is_locked() with lockdep
From: Edward Cree @ 2018-11-12 13:02 UTC (permalink / raw)
  To: Paul E. McKenney, linux-kernel
  Cc: mingo, jiangshanlai, dipankar, akpm, mathieu.desnoyers, josh,
	tglx, peterz, rostedt, dhowells, edumazet, fweisbec, oleg, joel,
	Lance Roy, Solarflare linux maintainers, Bert Kenward,
	David S. Miller, netdev
In-Reply-To: <20181111200443.10772-2-paulmck@linux.ibm.com>

On 11/11/18 20:04, Paul E. McKenney wrote:
> From: Lance Roy <ldr709@gmail.com>
>
> lockdep_assert_held() is better suited to checking locking requirements,
> since it only checks if the current thread holds the lock regardless of
> whether someone else does. This is also a step towards possibly removing
> spin_is_locked().
>
> Signed-off-by: Lance Roy <ldr709@gmail.com>
> Cc: Solarflare linux maintainers <linux-net-drivers@solarflare.com>
> Cc: Edward Cree <ecree@solarflare.com>
> Cc: Bert Kenward <bkenward@solarflare.com>
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: <netdev@vger.kernel.org>
> Signed-off-by: Paul E. McKenney <paulmck@linux.ibm.com>
> ---
Acked-by: Edward Cree <ecree@solarflare.com>
>  drivers/net/ethernet/sfc/efx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
> index 98fe7e762e17..3643015a55cf 100644
> --- a/drivers/net/ethernet/sfc/efx.c
> +++ b/drivers/net/ethernet/sfc/efx.c
> @@ -3167,7 +3167,7 @@ struct hlist_head *efx_rps_hash_bucket(struct efx_nic *efx,
>  {
>  	u32 hash = efx_filter_spec_hash(spec);
>  
> -	WARN_ON(!spin_is_locked(&efx->rps_hash_lock));
> +	lockdep_assert_held(&efx->rps_hash_lock);
>  	if (!efx->rps_hash_table)
>  		return NULL;
>  	return &efx->rps_hash_table[hash % EFX_ARFS_HASH_TABLE_SIZE];

^ permalink raw reply

* Re: [RFC PATCH 0/3] acpi: Add acpi mdio support code
From: Wang, Dongsheng @ 2018-11-12  2:52 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: timur@kernel.org, Zheng, Joey, f.fainelli@gmail.com,
	rjw@rjwysocki.net, linux-acpi@vger.kernel.org,
	netdev@vger.kernel.org
In-Reply-To: <20181108232353.GL5259@lunn.ch>

On 2018/11/9 7:23, Andrew Lunn wrote:
> I'm just trying to ensure whatever is defined is flexible enough that
> we really can later support everything which DT does. We have PHYs on
> MDIO busses, inside switches, which are on MDIO busses, which are
> inside Ethernet interfaces, etc.
>
> An MDIO bus is very similar to an i2c bus. How is that described in
> ACPI? Anything we can learn from that?

All the documents I've seen are from UEFII/ACPICA and I have not seen any
independent description of MDIO in the ACPI documents. Maybe I missed a
document, too.

I think not all devices need to be described in ACPI. The ACPI defines what
should be included in a device, as well as some special definitions. Our
MDIO
doesn't need a special definition, because it's enough to use a generic ACPI
description. I think that's why MDIO doesn't have a separate description.

Cheers,
Dongsheng

^ permalink raw reply

* [PATCH net v2 1/1] bnx2x: Assign unique DMAE channel number for FW DMAE transactions.
From: Sudarsana Reddy Kalluru @ 2018-11-12  2:27 UTC (permalink / raw)
  To: davem; +Cc: netdev, Michal.Kalderon

Driver assigns DMAE channel 0 for FW as part of START_RAMROD command. FW
uses this channel for DMAE operations (e.g., TIME_SYNC implementation).
Driver also uses the same channel 0 for DMAE operations for some of the PFs
(e.g., PF0 on Port0). This could lead to concurrent access to the DMAE
channel by FW and driver which is not legal. Hence need to assign unique
DMAE id for FW.
Currently following DMAE channels are used by the clients,
  MFW - OCBB/OCSD functionality uses DMAE channel 14/15
  Driver 0-3 and 8-11 (for PF dmae operations)
         4 and 12 (for stats requests)
Assigning unique dmae_id '13' to the FW.

Changes from previous version:
------------------------------
v2: Incorporated the review comments.

Signed-off-by: Sudarsana Reddy Kalluru <Sudarsana.Kalluru@cavium.com>
Signed-off-by: Michal Kalderon <Michal.Kalderon@cavium.com>
---
 drivers/net/ethernet/broadcom/bnx2x/bnx2x.h    | 7 +++++++
 drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index be15061..0de487a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -2191,6 +2191,13 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id,
 #define PMF_DMAE_C(bp)			(BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \
 					 E1HVN_MAX)
 
+/* Following is the DMAE channel number allocation for the clients.
+ *   MFW: OCBB/OCSD implementations use DMAE channels 14/15 respectively.
+ *   Driver: 0-3 and 8-11 (for PF dmae operations)
+ *           4 and 12 (for stats requests)
+ */
+#define BNX2X_FW_DMAE_C                 13 /* Channel for FW DMAE operations */
+
 /* PCIE link and speed */
 #define PCICFG_LINK_WIDTH		0x1f00000
 #define PCICFG_LINK_WIDTH_SHIFT		20
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index 3f4d2c8..a9eaaf3 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -6149,6 +6149,7 @@ static inline int bnx2x_func_send_start(struct bnx2x *bp,
 	rdata->sd_vlan_tag	= cpu_to_le16(start_params->sd_vlan_tag);
 	rdata->path_id		= BP_PATH(bp);
 	rdata->network_cos_mode	= start_params->network_cos_mode;
+	rdata->dmae_cmd_id	= BNX2X_FW_DMAE_C;
 
 	rdata->vxlan_dst_port	= cpu_to_le16(start_params->vxlan_dst_port);
 	rdata->geneve_dst_port	= cpu_to_le16(start_params->geneve_dst_port);
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH RFC net-next] net: SAIL based FIB lookup for XDP
From: Md. Islam @ 2018-11-12  2:25 UTC (permalink / raw)
  To: Netdev, David Ahern, Jesper Dangaard Brouer, David Miller,
	Stephen Hemminger, john fastabend, Alexey Kuznetsov

This patch implements SAIL[1] based routing table lookup for XDP. I
however made some changes from the original proposal (details are
described in the patch). This changes decreased the memory consumption
from 21.94 MB to 4.97 MB for my example routing table with 400K
routes.

This patch can perform FIB lookup in 50 CPU cycles for the example
routing table (with 400K routes) whereas LC-trie takes around 350 CPU
cycles.

I tried to follow all the advice I got from my last patch. Looking
forward to your review.

1. Yang, Tong, et al. "Guarantee IP lookup performance with FIB
explosion." ACM SIGCOMM Computer Communication Review. Vol. 44. No. 4.
ACM, 2014.

Thanks
Tamim

Signed-off-by: MD Iftakharul Islam (Tamim) <mislam4@kent.edu>
---
 MAINTAINERS             |   5 +
 include/net/ip_fib.h    |  60 ++++
 net/core/filter.c       |  49 +++
 net/ipv4/Kconfig        |  11 +
 net/ipv4/Makefile       |   1 +
 net/ipv4/fib_sail_xdp.c | 939 ++++++++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/fib_trie.c     |  12 +
 7 files changed, 1077 insertions(+)
 create mode 100644 net/ipv4/fib_sail_xdp.c

diff --git a/MAINTAINERS b/MAINTAINERS
index b22e7fd..969e625 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7630,6 +7630,11 @@ M:    Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar>
 S:    Maintained
 F:    net/ipv4/netfilter/ipt_MASQUERADE.c

+SAIL FIB LOOKUP
+M:    MD Iftakharul Islam (Tamim) <tamim@csebuet.org>
+S:    Maintained
+F:    net/ipv4/fib_sail_xdp.c
+
 IPMI SUBSYSTEM
 M:    Corey Minyard <minyard@acm.org>
 L:    openipmi-developer@lists.sourceforge.net (moderated for non-subscribers)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 69c91d1..cc275c7 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -197,6 +197,62 @@ struct fib_entry_notifier_info {
     u32 tb_id;
 };

+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+/*
+ * The router can have upto 255 ports. This limitation
+ * allows us to represent netdev_index as an u8
+ */
+#define NETDEV_COUNT_MAX 255
+
+struct chunk {
+    /*256-bit bitmap. Here i-th bit (from LSB) is set to 1 if C24[i] > 0 */
+    u64 bitmap[4];
+    /*
+     * Index to C24 where chunk is started. A chunk corresponds
+     * to 256 elements. Instead of having just one start index for the
+     * whole chunk, we divide the chunk into 4 parts and save start
+     * index for each part.
+     */
+    u64 start_index[4];
+};
+
+struct sail {
+    /*default next-hop (Level 0)*/
+    u8    def_nh;
+
+    /*Level 16*/
+    u8 __rcu *N16;
+    u8 __rcu *P16;
+    u16 __rcu *C16;
+
+    /*Level 24*/
+    u8 __rcu *N24;
+    u8 __rcu *P24;
+    struct chunk __rcu *CK24;
+    u32 __rcu *C24;
+    u32 cnk24_count;/*Number of chunks in level 24*/
+
+    /*Level 32*/
+    u8 __rcu *N32;
+    u8 __rcu *P32;
+    u32 cnk32_count;/*Number of chunks in level 32*/
+
+    /*Index to this array is stored in N16, N24 and N32*/
+    struct net_device    *netdevs[NETDEV_COUNT_MAX];
+    u8 netdev_count;/*Number of netdevs*/
+
+    spinlock_t lock;
+};
+
+int sail_insert(struct sail *s, u32 key,
+        u8 prefix_len, struct net_device *dev);
+int sail_delete(struct sail *s, u32 key,
+        u8 prefix_len);
+int sail_flush(struct sail *s);
+int sail_lookup(const struct sail *s, const __be32 dest,
+        struct net_device **dev);
+#endif
+
 struct fib_nh_notifier_info {
     struct fib_notifier_info info; /* must be first */
     struct fib_nh *fib_nh;
@@ -219,6 +275,10 @@ struct fib_table {
     int            tb_num_default;
     struct rcu_head        rcu;
     unsigned long         *tb_data;
+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+    /*Each FIB table will have its own SAIL structure.*/
+    struct sail    sail;
+#endif
     unsigned long        __data[0];
 };

diff --git a/net/core/filter.c b/net/core/filter.c
index 5e00f2b..e89b4bb 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -4236,6 +4236,49 @@ static int bpf_fib_set_fwd_params(struct
bpf_fib_lookup *params,
 }
 #endif

+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+static int sail_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
+               u32 flags, bool check_mtu)
+{
+    struct net_device *dev_in, *dev_out;
+    struct fib_table *tb;
+    struct neighbour *neigh;
+    u32 tbid;
+    int err;
+    u32 mtu;
+
+    if (flags & BPF_FIB_LOOKUP_DIRECT) {
+        dev_in = dev_get_by_index_rcu(net, params->ifindex);
+        if (unlikely(!dev_in))
+            return -ENODEV;
+        tbid = l3mdev_fib_table_rcu(dev_in) ? : RT_TABLE_MAIN;
+    } else {
+        tbid = RT_TABLE_MAIN;
+    }
+
+    tb = fib_get_table(net, tbid);
+    if (unlikely(!tb))
+        return BPF_FIB_LKUP_RET_NOT_FWDED;
+
+    err = sail_lookup(&tb->sail, params->ipv4_dst, &dev_out);
+    if (err)
+        return -ENOENT;
+
+    if (check_mtu) {
+        mtu = min(READ_ONCE(dev_out->mtu), IP_MAX_MTU);
+        if (params->tot_len > mtu)
+            return BPF_FIB_LKUP_RET_FRAG_NEEDED;
+    }
+
+    neigh = __ipv4_neigh_lookup_noref(dev_out,
+                      (__force u32)params->ipv4_dst);
+    if (!neigh)
+        return BPF_FIB_LKUP_RET_NO_NEIGH;
+
+    return bpf_fib_set_fwd_params(params, neigh, dev_out);
+}
+#endif
+
 #if IS_ENABLED(CONFIG_INET)
 static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
                    u32 flags, bool check_mtu)
@@ -4468,9 +4511,15 @@ BPF_CALL_4(bpf_xdp_fib_lookup, struct xdp_buff *, ctx,
     switch (params->family) {
 #if IS_ENABLED(CONFIG_INET)
     case AF_INET:
+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+        return sail_fib_lookup(dev_net(ctx->rxq->dev), params,
+                       flags, true);
+#else
         return bpf_ipv4_fib_lookup(dev_net(ctx->rxq->dev), params,
                        flags, true);
 #endif
+
+#endif
 #if IS_ENABLED(CONFIG_IPV6)
     case AF_INET6:
         return bpf_ipv6_fib_lookup(dev_net(ctx->rxq->dev), params,
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 32cae39..5c41071 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -52,6 +52,17 @@ config IP_ADVANCED_ROUTER

       If unsure, say N here.

+config FIB_SAIL_XDP
+    bool "SAIL based FIB lookup for XDP"
+    depends on !IP_ROUTE_MULTIPATH && !LWTUNNEL
+    default y
+    help
+      This option will enable SAIL based routing table lookup for XDP
+      forwarding. This will store FIB table as a SAIL (along with
+      LC-trie). Currently it only supports up to 255 ports. Currently
+      it does not support multi-path routing and light-weight
+      tunnels such as MPLS.
+
 config IP_FIB_TRIE_STATS
     bool "FIB TRIE statistics"
     depends on IP_ADVANCED_ROUTER
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 7446b98..55ff5e7 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
 obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
 obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
 obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
+obj-$(CONFIG_FIB_SAIL_XDP) += fib_sail_xdp.o

 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
               xfrm4_output.o xfrm4_protocol.o
diff --git a/net/ipv4/fib_sail_xdp.c b/net/ipv4/fib_sail_xdp.c
new file mode 100644
index 0000000..f3f56c5
--- /dev/null
+++ b/net/ipv4/fib_sail_xdp.c
@@ -0,0 +1,939 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018-19 MD Iftakharul Islam (Tamim) <mislam4@kent.edu>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version
+ *   2 of the License, or (at your option) any later version.
+ *
+ *
+ * This is SAIL_L based routing table lookup which was initially proposed in:
+ *
+ * Yang, Tong, Gaogang Xie, YanBiao Li, Qiaobin Fu, Alex X. Liu, Qi Li,
+ * and Laurent Mathy. "Guarantee IP lookup performance with FIB explosion."
+ * In ACM SIGCOMM Computer Communication Review, vol. 44, no. 4, pp. 39-50.
+ * ACM, 2014.
+ *
+ * It however deviates from the SAIL_L in three ways:
+ *
+ * 1. It pushes all the solid nodes in level 1~15 to level 16 whereas SAIL_L
+ * pushes them to either level 16, level 24 or level 32.
+ *
+ * 2. It pushes all the solid nodes in level 17~23 to level 24 whereas SAIL_L
+ * pushes them to either level 24 or level 32.
+ *
+ * 3. It adds a bitmap array, CK24 in addition to C24. This reduces the memory
+ * memory requirement of original C24 from 17.08 MB to 110KB for our example
+ * routing table.
+ */
+
+#include <net/ip_fib.h>
+
+/*The length of N16, P16 and C16 is 2^16*/
+#define LEVEL16_SIZE 65536
+
+/*Length of C24.*/
+#define C24_SIZE 1048576
+
+/*chunk size is 2^8*/
+#define CHUNK_SIZE 256
+
+/*Total number of chunks preallocated for level 24 and 32*/
+#define NUM_CHUNKS 16384
+
+/*Calculates the number of bits set to 1*/
+#define POPCNT(X) (hweight64(X))
+
+/*POPCNT of left-most N bits of X*/
+#define POPCNT_OFF(X, N) (hweight64(((1ULL << (N)) - 1) & (X)))
+
+/*Calculate index to C24 from CK26 chunk and chunk offset */
+static u64 calc_c24_idx(struct chunk c, u32 cnk_off)
+{
+    u8 part_idx, part_off;
+
+    part_idx = cnk_off / 64;
+    part_off = cnk_off % 64;
+
+    return c.start_index[part_idx] +
+        POPCNT_OFF(c.bitmap[part_idx], part_off);
+}
+
+/*Converts a net_device to corresponding netdev_index*/
+static u8 get_netdev_index(struct sail *s, struct net_device *dev)
+{
+    u8 i;
+
+    /*checks if the net_device is already seen; if yes then return the
+     *corresponding index
+     */
+    for (i = 0; i < s->netdev_count; i++) {
+        if (s->netdevs[i] == dev)
+            return i;
+    }
+    /*If the net_device is not previously seen, then add it to the array*/
+    s->netdevs[s->netdev_count++] = dev;
+    return s->netdev_count - 1;
+}
+
+/* Insert a new chunk to N24 and P24 at index chunk_id-1*/
+static int N24_insert(struct sail *s, u16 chunk_id)
+{
+    long long m;
+
+    if (chunk_id > (s->cnk24_count + 1) || chunk_id < 1) {
+        pr_err("Invalid chunk_id for level 26");
+        return -EINVAL;
+    }
+
+    if (s->cnk24_count >= NUM_CHUNKS) {
+        pr_err("Cannot insert a chunk. The chunk array is full");
+        return -EINVAL;
+    }
+
+    /*shift each element one step right to make
+     *space for the new one
+     */
+    m = (long long)s->cnk24_count * CHUNK_SIZE - 1;
+    for (; m >= (chunk_id - 1) * CHUNK_SIZE; m--) {
+        s->N24[m + CHUNK_SIZE] = s->N24[m];
+        s->P24[m + CHUNK_SIZE] = s->P24[m];
+    }
+
+    /*Reset the newly created chunk*/
+    m = (chunk_id - 1) * CHUNK_SIZE;
+    for (; m < chunk_id * CHUNK_SIZE; m++) {
+        s->N24[m] = 0;
+        s->P24[m] = 0;
+    }
+    return 0;
+}
+
+/* Insert a new chunk to CK24 at index chunk_id-1*/
+static int CK24_insert(struct sail *s, u16 chunk_id)
+{
+    long long m;
+
+    if (chunk_id > (s->cnk24_count + 1) || chunk_id < 1) {
+        pr_err("Invalid chunk_id for level 26");
+        return -EINVAL;
+    }
+
+    if (s->cnk24_count >= NUM_CHUNKS) {
+        pr_err("Cannot insert a chunk. The chunk array is full");
+        return -EINVAL;
+    }
+
+    /*shift each chunk one step right to make
+     *space for the new one
+     */
+    m = (long long)s->cnk24_count - 1;
+    for (; m >= (chunk_id - 1); m--)
+        s->CK24[m + 1] = s->CK24[m];
+
+    /*Reset the newly created empty chunk*/
+    for (m = 0; m < 4; m++) {
+        s->CK24[chunk_id - 1].bitmap[m] = 0;
+        s->CK24[chunk_id - 1].start_index[m] = 0;
+    }
+    return 0;
+}
+
+/* Insert a new chunk to level 24 at chunk_id-1. Note that Chunk ID
+ * starts from 1, not 0
+ */
+static int chunk24_insert(struct sail *s, u16 chunk_id)
+{
+    int err = 0;
+
+    err = N24_insert(s, chunk_id);
+    if (!err)
+        err = CK24_insert(s, chunk_id);
+    if (!err)
+        ++s->cnk24_count;
+    else
+        pr_err("Error in level 24 insertion");
+    return err;
+}
+
+/* Insert a new chunk to level 32 at chunk_id-1. Note that Chunk ID
+ * starts from 1, not 0
+ */
+static int chunk32_insert(struct sail *s, u32 chunk_id)
+{
+    long long m;
+
+    if (chunk_id > (s->cnk32_count + 1) || chunk_id < 1) {
+        pr_err("Invalid chunk_id for level 32");
+        return -EINVAL;
+    }
+
+    if (s->cnk32_count >= NUM_CHUNKS) {
+        pr_err("Cannot insert a new chunk. The chunk array is full");
+        return -EINVAL;
+    }
+
+    /*shift each element one step right to make
+     *space for the new one
+     */
+    m = (long long)s->cnk32_count * CHUNK_SIZE - 1;
+    for (; m >= (chunk_id - 1) * CHUNK_SIZE; m--) {
+        s->N32[m + CHUNK_SIZE] = s->N32[m];
+        s->P32[m + CHUNK_SIZE] = s->P32[m];
+    }
+
+    /*Reset the newly created empty chunk*/
+    m = (chunk_id - 1) * CHUNK_SIZE;
+    for (; m < chunk_id * CHUNK_SIZE; m++) {
+        s->N32[m] = 0;
+        s->P32[m] = 0;
+    }
+
+    ++s->cnk32_count;
+    return 0;
+}
+
+static int N24_delete(struct sail *s, u16 chunk_id)
+{
+    long long m;
+    u64 end_idx;
+
+    if (chunk_id > s->cnk24_count) {
+        pr_err("Invalid chunk_id to level 26");
+        return -EINVAL;
+    }
+
+    if (chunk_id < s->cnk24_count) {
+        /*shift each chunk one step left*/
+        m = (long long)chunk_id * CHUNK_SIZE;
+        end_idx = s->cnk24_count * CHUNK_SIZE - 1;
+        for (; m <= end_idx; m++) {
+            s->N24[m - CHUNK_SIZE] = s->N24[m];
+            s->P24[m - CHUNK_SIZE] = s->P24[m];
+        }
+    }
+
+    /*Reset the the last chunk*/
+    end_idx = s->cnk24_count * CHUNK_SIZE - 1;
+    m = (s->cnk24_count - 1) * CHUNK_SIZE;
+    for (; m <= end_idx; m++) {
+        s->N24[m] = 0;
+        s->P24[m] = 0;
+    }
+
+    return 0;
+}
+
+static int CK24_delete(struct sail *s, u16 chunk_id)
+{
+    long long m;
+
+    if (chunk_id > s->cnk24_count) {
+        pr_err("Invalid chunk_id to level 24");
+        return -EINVAL;
+    }
+
+    if (chunk_id < s->cnk24_count) {
+        /*shift each chunk one step left*/
+        m = (long long)chunk_id;
+        for (; m <= s->cnk24_count - 1; m++)
+            s->CK24[m - 1] = s->CK24[m];
+    }
+
+    /*Reset the the last chunk*/
+    for (m = 0; m < 4; m++) {
+        s->CK24[s->cnk24_count - 1].start_index[m] = 0;
+        s->CK24[s->cnk24_count - 1].bitmap[m] = 0;
+    }
+
+    return 0;
+}
+
+static int chunk24_delete(struct sail *s, u16 chunk_id)
+{
+    N24_delete(s, chunk_id);
+    CK24_delete(s, chunk_id);
+    --s->cnk24_count;
+    return 0;
+}
+
+static int chunk32_delete(struct sail *s, u32 chunk_id)
+{
+    long long m;
+    u64 end_idx;
+
+    if (chunk_id > s->cnk32_count) {
+        pr_err("Invalid chunk_id to level 32");
+        return -EINVAL;
+    }
+
+    if (chunk_id < s->cnk32_count) {
+        /*shift each chunk one step left*/
+        m = (long long)chunk_id * CHUNK_SIZE;
+        end_idx = s->cnk32_count * CHUNK_SIZE - 1;
+        for (; m <= end_idx; m++) {
+            s->N32[m - CHUNK_SIZE] = s->N32[m];
+            s->P32[m - CHUNK_SIZE] = s->P32[m];
+        }
+    }
+
+    /*Reset the the last chunk*/
+    end_idx = s->cnk32_count * CHUNK_SIZE - 1;
+    m = (s->cnk32_count - 1) * CHUNK_SIZE;
+    for (; m <= end_idx; m++) {
+        s->N32[m] = 0;
+        s->P32[m] = 0;
+    }
+
+    --s->cnk32_count;
+    return 0;
+}
+
+/*Calculate the chunk ID for level 24 based on C16*/
+static u16 calc_ckid24_from_C16(u16 *c16, u32 c16_size, u16 idx16)
+{
+    long long i;
+
+    if (idx16 >= c16_size) {
+        pr_err("Index needs to be smaller than arr_size");
+        return 0;
+    }
+
+    /*Find the first chunk ID to the left and increment that by 1*/
+    for (i = (long long)idx16 - 1; i >= 0; i--) {
+        if (c16[i] > 0)
+            return c16[i] + 1;
+    }
+
+    /*If there is no chunk to the left, then this is the first chunk*/
+    return 1;
+}
+
+/*Update C16 based on the newly inserted chunk*/
+static int update_C16(u16 *c16, u32 c16_size,
+              u16 idx16, u16 chunk_id)
+{
+    long long i;
+
+    if (idx16 >= c16_size) {
+        pr_err("Invalid index");
+        return -EINVAL;
+    }
+
+    c16[idx16] = chunk_id;
+
+    /* Increment chunk ID to the right */
+    for (i = idx16 + 1; i < c16_size; i++) {
+        if (c16[i] > 0)
+            c16[i]++;
+    }
+
+    return 0;
+}
+
+/*Remove Chunk ID for level 16*/
+static int C16_remove_chunkid(u16 *c16, u32 c16_size, u16 chunk_id)
+{
+    long long i;
+    bool found = false;
+
+    /*Check if the chunk ID already exists*/
+    for (i = 0; i < c16_size; i++) {
+        if (found && c16[i] > 0) {
+            c16[i]--;
+        } else if (c16[i] == chunk_id) {
+            c16[i] = 0;
+            found = true;
+        }
+    }
+
+    return 0;
+}
+
+static int C24_remove_chunkid(struct sail *s, u16 cnk_idx, u64 cnk_off)
+{
+    long long i;
+    bool found = false;
+    u32 chunk_id;
+    u8 part_idx, part_off;
+
+    part_idx = cnk_off / 64;
+    part_off = cnk_off % 64;
+
+    chunk_id = s->C24[calc_c24_idx(s->CK24[cnk_idx], cnk_off)];
+    s->CK24[cnk_idx].bitmap[part_idx] &= ~(1ULL << part_off);
+
+    for (i = 0; i < C24_SIZE; i++) {
+        if (s->C24[i] == chunk_id)
+            found = true;
+        else if (found && s->C24[i] > 0)
+            s->C24[i - 1] = s->C24[i];
+        else if (found && !s->C24[i])
+            break;
+    }
+
+    return 0;
+}
+
+ /* Check if a chunk in level 32 is being unused */
+static bool is_N32_chunk_unused(u8 *nh, u32 chunk_id)
+{
+    long long i;
+    long long start_index, end_index;
+
+    if (chunk_id < 1) {
+        pr_err("Invalid chunk ID");
+        return -EINVAL;
+    }
+
+    start_index = (long long)(chunk_id - 1) * CHUNK_SIZE;
+    end_index = (long long)chunk_id * CHUNK_SIZE - 1;
+    for (i = start_index; i <= end_index; i++) {
+        /*The chunk is being used*/
+        if (nh[i] > 0)
+            return false;
+    }
+
+    return true;
+}
+
+ /* Check if a chunk in level 24 is being unused*/
+static bool is_CK24_N24_chunk_unused(u8 *nh, struct chunk *ck, u16 chunk_id)
+{
+    long long i;
+    long long start_index, end_index;
+
+    if (chunk_id < 1) {
+        pr_err("Invalid chunk ID");
+        return -EINVAL;
+    }
+
+    start_index = (long long)(chunk_id - 1) * CHUNK_SIZE;
+    end_index = (long long)chunk_id * CHUNK_SIZE - 1;
+    for (i = start_index; i <= end_index; i++) {
+        /*The chunk is being used*/
+        if (nh[i] > 0)
+            return false;
+    }
+
+    if (ck->bitmap[0] || ck->bitmap[1] || ck->bitmap[2] || ck->bitmap[3])
+        return false;
+
+    return true;
+}
+
+/*Calculate the chunk ID for level 32 based on CK24 and C24*/
+static u32 calc_ckid32_from_ck24(struct chunk *ck24,
+                 u32 ck24_idx, u32 ck24_off, u32 *c24)
+{
+    long long i, j;
+    long long index = 0;
+    u8 part_idx, part_off;
+    struct chunk c;
+
+    part_idx = ck24_off / 64;
+    part_off = ck24_off % 64;
+    c = ck24[ck24_idx];
+
+    /*find the index to C24 where previous chunk ID would be found*/
+    if (c.bitmap[part_idx]) {
+        index = c.start_index[part_idx] +
+            POPCNT_OFF(c.bitmap[part_idx], part_off) - 1;
+        if (index >= 0)
+            goto index_found;
+    }
+
+    j = part_idx - 1;
+    for (i = (long long)ck24_idx; i >= 0; i--) {
+        for (; j >= 0; j--) {
+            if (ck24[i].bitmap[j]) {
+                index = ck24[i].start_index[j] +
+                    POPCNT(ck24[i].bitmap[j]) - 1;
+                goto index_found;
+            }
+        }
+        j = 3;
+    }
+
+    /*If there is no chunk to the left, then this is the first chunk*/
+    return 1;
+index_found:
+    if (index >= (C24_SIZE - 1)) {
+        pr_err("CK24 array is full. Cannot insert");
+        return 0;
+    } else if (c24[index] <= 0) {
+        pr_err("Invalid chunk ID");
+        return 0;
+    } else {
+        return c24[index] + 1;
+    }
+}
+
+/*Update CK24 and C24 based on the newly inserted chunk*/
+static int update_ck24_c24(struct chunk *ck24, u32 ck24_idx, u32 ck24_off,
+               u32 *c24, u32 cnk24_count, u32 chunk_id)
+{
+    long long i, j;
+    u64 index = 0;
+    u8 part_idx, part_off;
+
+    part_idx = ck24_off / 64;
+    part_off = ck24_off % 64;
+
+    if (ck24[ck24_idx].bitmap[part_idx] & (1ULL << part_off)) {
+        pr_err("Error: bitmap is already set");
+        return -EINVAL;
+    }
+
+    /*find the index where Chunk ID should be copied to*/
+    if (ck24[ck24_idx].bitmap[part_idx]) {
+        index = ck24[ck24_idx].start_index[part_idx] +
+            POPCNT_OFF(ck24[ck24_idx].bitmap[part_idx], part_off);
+        goto index_found;
+    }
+
+    /*Find a chunk to the left which is not empty*/
+    j = part_idx - 1;
+    for (i = (long long)ck24_idx; i >= 0; i--) {
+        for (; j >= 0; j--) {
+            if (ck24[i].bitmap[j]) {
+                index = ck24[i].start_index[j] +
+                    POPCNT(ck24[i].bitmap[j]);
+                goto index_found;
+            }
+        }
+        j = 3;
+    }
+
+index_found:
+    /* Move each element one step to the right to create a space for
+     * the new element. Also increment each element by 1
+     */
+    for (i = C24_SIZE - 2 ; i >= (long long)index; i--)
+        c24[i + 1] = (c24[i] > 0) ? c24[i] + 1 : c24[i];
+    /*Set the new Chunk ID*/
+    c24[index] = chunk_id;
+
+    /*This is the first element of this chunk*/
+    if (!ck24[ck24_idx].bitmap[part_idx])
+        ck24[ck24_idx].start_index[part_idx] = index;
+
+    ck24[ck24_idx].bitmap[part_idx] |= (1ULL << part_off);
+
+    /*Update offset of the chunks to the right*/
+    j = part_idx + 1;
+    for (i = (long long)ck24_idx; i < cnk24_count; i++) {
+        for (; j < 4; j++) {
+            if (ck24[i].bitmap[j])
+                ck24[i].start_index[j]++;
+        }
+        j = 0;
+    }
+
+    return 0;
+}
+
+int sail_insert(struct sail *s, u32 key, u8 prefix_len,
+        struct net_device *dev)
+{
+    int i;
+    u8 *n16, *p16, *n24, *p24, *n32, *p32;
+    u16 *c16;
+    u32 *c24;
+    struct chunk *ck24;
+    u16 chunk_id;
+    u16 n16_idx;/*Index to N16, P16 and C16*/
+    u64 n24_idx;/*Index to N24 and P24*/
+    u64 c24_idx;/*Index to C24*/
+    u32 ck24_idx;/*Index to CK24*/
+    u64 ck24_off;/*offset inside a chunk*/
+    u8 part_idx, part_off;
+    u64 n32_idx;/*Index to N32 and P32*/
+    u32 num_leafs;/*Number of leafs need to be inserted for this prefix*/
+    u8 netdev_index = get_netdev_index(s, dev);
+    int err = 0;
+
+    spin_lock(&s->lock);
+
+    /* Default route */
+    if (prefix_len == 0) {
+        s->def_nh = netdev_index;
+        goto finish;
+    }
+
+    /* Preallocate all the arrays at once*/
+    if (!s->N16) {
+        n16 = kcalloc(LEVEL16_SIZE, sizeof(*n16), GFP_ATOMIC);
+        p16 = kcalloc(LEVEL16_SIZE, sizeof(*p16), GFP_ATOMIC);
+        c16 = kcalloc(LEVEL16_SIZE, sizeof(*c16), GFP_ATOMIC);
+        n24 = kcalloc(NUM_CHUNKS * CHUNK_SIZE, sizeof(*n24),
+                  GFP_ATOMIC);
+        p24 = kcalloc(NUM_CHUNKS * CHUNK_SIZE, sizeof(*p24),
+                  GFP_ATOMIC);
+        ck24 = kcalloc(NUM_CHUNKS, sizeof(*ck24), GFP_ATOMIC);
+        c24 = kcalloc(C24_SIZE, sizeof(*c24), GFP_ATOMIC);
+        n32 = kcalloc(NUM_CHUNKS * CHUNK_SIZE, sizeof(*n32),
+                  GFP_ATOMIC);
+        p32 = kcalloc(NUM_CHUNKS * CHUNK_SIZE, sizeof(*p32),
+                  GFP_ATOMIC);
+
+        if (!n16 || !c16 || !p16 || !n24 || !p24 || !ck24 ||
+            !c24 || !n32 || !p32) {
+            kfree(n16);
+            kfree(c16);
+            kfree(p16);
+            kfree(n24);
+            kfree(p24);
+            kfree(ck24);
+            kfree(c24);
+            kfree(n32);
+            kfree(p32);
+            pr_err("Out of memory while preallocating  SAIL");
+            goto error;
+        }
+
+        RCU_INIT_POINTER(s->N16, n16);
+        RCU_INIT_POINTER(s->P16, p16);
+        RCU_INIT_POINTER(s->C16, c16);
+        RCU_INIT_POINTER(s->N24, n24);
+        RCU_INIT_POINTER(s->P24, p24);
+        RCU_INIT_POINTER(s->CK24, ck24);
+        RCU_INIT_POINTER(s->C24, c24);
+        RCU_INIT_POINTER(s->N32, n32);
+        RCU_INIT_POINTER(s->P32, p32);
+
+        synchronize_rcu();
+    }
+
+    /*Eextract 16 bits from LSB.*/
+    n16_idx = key >> 16;
+
+    if (prefix_len <= 16) {
+        /*All the leafs in level 1~16 will be stored in level 16.*/
+        num_leafs = 1U << (16 - prefix_len);
+        for (i = 0; i < num_leafs; i++) {
+            /*Longer prefix exists*/
+            if (s->P16[n16_idx + i] > prefix_len)
+                continue;
+            s->N16[n16_idx + i] = netdev_index;
+            s->P16[n16_idx + i] = prefix_len;
+        }
+        goto finish;
+    }
+
+    /* The length of the prefix is 21~32. So need to check if there is a
+     * chunk for this prefix in level 24. C16[..] = 0 indicates that there
+     * is no chunk, so need to insert one. The insertion works as
+     * following:
+     * 1. Calculate the chunk ID to level 24 from C16. The Chunk ID
+     * indicates where the new chunk should be inserted.
+     * 2. Insert a new chunk in level 24
+     * 3. Update the C16[] based on the newly inserted chunk
+     */
+    if (s->C16[n16_idx] == 0) {
+        /*Step 1*/
+        chunk_id = calc_ckid24_from_C16(s->C16, LEVEL16_SIZE, n16_idx);
+        if (!chunk_id)
+            goto error;
+        /*Step 2*/
+        err = chunk24_insert(s, chunk_id);
+        if (err)
+            goto error;
+        /*Step 3*/
+        err = update_C16(s->C16, LEVEL16_SIZE, n16_idx, chunk_id);
+        if (err)
+            goto error;
+    }
+
+    /*Extract bit 17~24 and calculate index to level 24*/
+    n24_idx = (s->C16[n16_idx] - 1) * CHUNK_SIZE + ((key & 65280) >> 8);
+    ck24_idx = s->C16[n16_idx] - 1;
+    ck24_off = (key & 65280) >> 8;
+
+    if (prefix_len <= 24) {
+        /*All the leafs in level 17~24 will be stored in level 24.*/
+        num_leafs = 1U << (24 - prefix_len);
+        for (i = 0; i < num_leafs; i++) {
+            /*Longer prefix exists*/
+            if (s->P24[n24_idx + i] > prefix_len)
+                continue;
+            s->N24[n24_idx + i] = netdev_index;
+            s->P24[n24_idx + i] = prefix_len;
+        }
+        goto finish;
+    }
+
+    /* The length of the prefix is 25~32, but there is no chunk for
+     * this prefix in level 32. So need to insert a new one.
+     * The insertion works as following:
+     * 1. Calculate the chunk ID to level 32 from CK24 and C24. The
+     * Chunk ID indicates where the new chunk should be inserted.
+     * 2. Insert a new chunk in level 32
+     * 3. Update the CK24 and C24 based on the newly inserted chunk
+     */
+    part_idx = ck24_off / 64;
+    part_off = ck24_off % 64;
+    if (!(s->CK24[ck24_idx].bitmap[part_idx] & (1ULL << part_off))) {
+        /*Step 1*/
+        chunk_id = calc_ckid32_from_ck24(s->CK24, ck24_idx,
+                         ck24_off, s->C24);
+        if (!chunk_id)
+            goto error;
+        /*Step 2*/
+        err = chunk32_insert(s, chunk_id);
+        if (err)
+            goto error;
+        /*Step 3*/
+        err = update_ck24_c24(s->CK24, ck24_idx, ck24_off,
+                      s->C24, s->cnk24_count, chunk_id);
+        if (err)
+            goto error;
+    }
+
+    c24_idx = calc_c24_idx(s->CK24[ck24_idx], ck24_off);
+    /*Extract 8 bit from MSB and calculate index to level 32*/
+    n32_idx = (s->C24[c24_idx] - 1) * CHUNK_SIZE + (key & 255);
+
+    if (prefix_len <= 32) {
+        /*All the leafs in level 25~32 will be stored in level 32.*/
+        num_leafs = 1U << (32 - prefix_len);
+        for (i = 0; i < num_leafs; i++) {
+            /*Longer prefix exists*/
+            if (s->P32[n32_idx + i] > prefix_len)
+                continue;
+            s->N32[n32_idx + i] = netdev_index;
+            s->P32[n32_idx + i] = prefix_len;
+        }
+        goto finish;
+    }
+
+error:
+    pr_err("Something went wrong in route insertion");
+finish:
+    spin_unlock(&s->lock);
+    return err;
+}
+
+int sail_delete(struct sail *s, u32 key, u8 prefix_len)
+{
+    int i;
+    u32 n16_idx;/*Index to N16, P16 and C16*/
+    u64 n24_idx;/*Index to level N24 and P24*/
+    u16 ck24_idx;/*Index to CK24*/
+    u64 ck24_off;/*Offset inside chunk in level 24*/
+    u64 n32_idx;/*Index to level N32 and P32*/
+    u32 consecutive_leafs;
+    int err = 0;
+    u32 chunkid_level32;
+    u8 part_idx, part_off;
+
+    spin_lock(&s->lock);
+
+    /* Simply ignore */
+    if (prefix_len == 0 || !s->N16)
+        goto error;
+
+    /*Eextract 16 bits from LSB.*/
+    n16_idx = key >> 16;
+
+    if (prefix_len <= 16) {
+        /*Level pushing*/
+        consecutive_leafs = 1U << (16 - prefix_len);
+        for (i = 0; i < consecutive_leafs; i++) {
+            /*Prefix len doesn't match*/
+            if (s->P16[n16_idx + i] != prefix_len)
+                continue;
+            s->N16[n16_idx + i] = 0;
+            s->P16[n16_idx + i] = 0;
+        }
+        goto finish;
+    }
+
+    /* The prefix_len is 17~32 but no chunk for the prefix*/
+    if (s->C16[n16_idx] == 0)
+        goto error;
+
+    ck24_idx = s->C16[n16_idx] - 1;
+    ck24_off = (key & 65280) >> 8;/*Extract bit 17~24 from the prefix*/
+    n24_idx = ck24_idx * CHUNK_SIZE + ck24_off;
+
+    if (prefix_len <= 24) {
+        /*Level pushing*/
+        consecutive_leafs = 1U << (24 - prefix_len);
+        for (i = 0; i < consecutive_leafs; i++) {
+            /*Prefix len doesn't match*/
+            if (s->P24[n24_idx + i] != prefix_len)
+                continue;
+            s->N24[n24_idx + i] = 0;
+            s->P24[n24_idx + i] = 0;
+        }
+        if (is_CK24_N24_chunk_unused(s->N24, &s->CK24[ck24_idx],
+                         s->C16[n16_idx])) {
+            chunk24_delete(s, s->C16[n16_idx]);
+            C16_remove_chunkid(s->C16, LEVEL16_SIZE,
+                       s->C16[n16_idx]);
+        }
+        goto finish;
+    }
+
+    part_idx = ck24_off / 64;
+    part_off = ck24_off % 64;
+    /* The prefix_len is 25~32. but no chunk for the prefix */
+    if (!(s->CK24[ck24_idx].bitmap[part_idx] & (1ULL << part_off)))
+        goto error;
+
+    chunkid_level32 = s->C24[calc_c24_idx(s->CK24[ck24_idx], ck24_off)];
+
+    /*Extract 8 bit from MSB and calculate index to level 32*/
+    n32_idx = (chunkid_level32 - 1) * CHUNK_SIZE + (key & 255);
+
+    if (prefix_len <= 32) {
+        /*Level pushing*/
+        consecutive_leafs = 1U << (32 - prefix_len);
+
+        for (i = 0; i < consecutive_leafs; i++) {
+            /*Prefix len doesn't match*/
+            if (s->P32[n32_idx + i] != prefix_len)
+                continue;
+            s->N32[n32_idx + i] = 0;
+            s->P32[n32_idx + i] = 0;
+        }
+        if (is_N32_chunk_unused(s->N32, chunkid_level32)) {
+            chunk32_delete(s, chunkid_level32);
+            C24_remove_chunkid(s, ck24_idx, ck24_off);
+        }
+        goto finish;
+    }
+
+/*The prefix was not found*/
+error:
+    err = -ENOENT;
+finish:
+    spin_unlock(&s->lock);
+    return err;
+}
+
+int sail_flush(struct sail *s)
+{
+    u8 *n16_old, *p16_old, *n24_old, *p24_old, *n32_old, *p32_old;
+    u16 *c16_old;
+    u32 *c24_old;
+    struct chunk *ck24_old;
+
+    spin_lock(&s->lock);
+
+    /*Save old pointers*/
+    n16_old = s->N16;
+    p16_old = s->P16;
+    c16_old = s->C16;
+    n24_old = s->N24;
+    p24_old = s->P24;
+    c24_old = s->C24;
+    ck24_old = s->CK24;
+    n32_old = s->N32;
+    p32_old = s->P32;
+
+    /*Set the counter before the chunk are deleted*/
+    s->cnk32_count = 0;
+    s->cnk24_count = 0;
+
+    /*Set the pointers to NULL*/
+    rcu_assign_pointer(s->N16, NULL);
+    rcu_assign_pointer(s->P16, NULL);
+    rcu_assign_pointer(s->C16, NULL);
+
+    rcu_assign_pointer(s->N24, NULL);
+    rcu_assign_pointer(s->P24, NULL);
+    rcu_assign_pointer(s->C24, NULL);
+    rcu_assign_pointer(s->CK24, NULL);
+
+    rcu_assign_pointer(s->N32, NULL);
+    rcu_assign_pointer(s->P32, NULL);
+
+    /* Wait for all references to be released */
+    synchronize_rcu();
+
+    /* Deallocate old references after setting them NULL*/
+    kfree(n16_old);
+    kfree(p16_old);
+    kfree(c16_old);
+    kfree(n24_old);
+    kfree(p24_old);
+    kfree(c24_old);
+    kfree(ck24_old);
+    kfree(n32_old);
+    kfree(p32_old);
+
+    spin_unlock(&s->lock);
+    return 0;
+}
+
+int sail_lookup(const struct sail *s,
+        const __be32 dest, struct net_device **dev)
+{
+    u8 *n16, *n24, *n32;
+    u16 *c16;
+    u32 *c24;
+    struct chunk *ck24;
+    const u32 key = ntohl(dest);
+    u8 netdev_index = s->def_nh;
+    u16 n16_idx;/*Index to N16 and C16*/
+    u64 n24_idx;/*Index to N24*/
+    u32 ck24_idx;/*Index to CK24*/
+    u64 ck24_off;/*Offset inside a chunk*/
+    u64 c24_idx;/*Index to C24*/
+    u64 n32_idx;/*Index to N32*/
+    u8 part_idx, part_off;
+
+    rcu_read_lock();
+
+    /*extract 16 bits from LSB*/
+    n16_idx = key >> 16;
+    n16 = rcu_dereference(s->N16);
+    if (unlikely(!n16))
+        goto finish;
+
+    if (likely(n16[n16_idx] != 0))
+        netdev_index = n16[n16_idx];
+
+    /*Check if there is a longer prefix; if yes, extract bit 17~24
+     *  and calculate index to N24
+     */
+    c16 = rcu_dereference(s->C16);
+    if (likely(c16[n16_idx] != 0)) {
+        ck24_idx = c16[n16_idx] - 1;
+        ck24_off = (key & 65280) >> 8;
+        n24_idx = ck24_idx * CHUNK_SIZE + ck24_off;
+    } else {
+        goto finish;
+    }
+
+    /*Find corresponding next-hop in level 24*/
+    n24 = rcu_dereference(s->N24);
+    if (likely(n24[n24_idx] != 0))
+        netdev_index = n24[n24_idx];
+
+    /*Check if there is a longer prefix; if yes, extract 8 bits
+     * from MSB and calculate index to N32
+     */
+    ck24 = rcu_dereference(s->CK24);
+    part_idx = ck24_off / 64;
+    part_off = ck24_off % 64;
+    if (likely(ck24[ck24_idx].bitmap[part_idx] & (1ULL << part_off))) {
+        c24 = rcu_dereference(s->C24);
+        c24_idx = ck24[ck24_idx].start_index[part_idx] +
+            POPCNT_OFF(ck24[ck24_idx].bitmap[part_idx], part_off);
+        n32_idx = (c24[c24_idx] - 1) * CHUNK_SIZE + (key & 255);
+    } else {
+        goto finish;
+    }
+
+    n32 = rcu_dereference(s->N32);
+    if (likely(n32[n32_idx] != 0))
+        netdev_index = n32[n32_idx];
+
+finish:
+    *dev = s->netdevs[netdev_index];
+    rcu_read_unlock();
+    return 0;
+}
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 5bc0c89..d60fa45 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1280,6 +1280,10 @@ int fib_table_insert(struct net *net, struct
fib_table *tb,
     if (err)
         goto out_fib_notif;

+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+    sail_insert(&tb->sail, key, plen, fi->fib_dev);
+#endif
+
     if (!plen)
         tb->tb_num_default++;

@@ -1568,6 +1572,10 @@ int fib_table_delete(struct net *net, struct
fib_table *tb,

     pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t);

+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+    sail_delete(&tb->sail, key, plen);
+#endif
+
     fa_to_delete = NULL;
     hlist_for_each_entry_from(fa, fa_list) {
         struct fib_info *fi = fa->fa_info;
@@ -1929,6 +1937,10 @@ int fib_table_flush(struct net *net, struct
fib_table *tb)
         }
     }

+#if IS_ENABLED(CONFIG_FIB_SAIL_XDP)
+    sail_flush(&tb->sail);
+#endif
+
     pr_debug("trie_flush found=%d\n", found);
     return found;
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH 20/20] can: flexcan: use can_rx_offload_queue_sorted() for flexcan_irq_bus_*()
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Oleksij Rempel, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Oleksij Rempel <o.rempel@pengutronix.de>

Currently, in case of bus error, driver will generate error message and put
in the tail of the message queue. To avoid confusions, this change should
place the bus related messages in proper order.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/flexcan.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 5923bd0ec118..75ce11395ee8 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -561,9 +561,13 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
 static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
 {
 	struct flexcan_priv *priv = netdev_priv(dev);
+	struct flexcan_regs __iomem *regs = priv->regs;
 	struct sk_buff *skb;
 	struct can_frame *cf;
 	bool rx_errors = false, tx_errors = false;
+	u32 timestamp;
+
+	timestamp = priv->read(&regs->timer) << 16;
 
 	skb = alloc_can_err_skb(dev, &cf);
 	if (unlikely(!skb))
@@ -610,17 +614,21 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
 	if (tx_errors)
 		dev->stats.tx_errors++;
 
-	can_rx_offload_queue_tail(&priv->offload, skb);
+	can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
 }
 
 static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
 {
 	struct flexcan_priv *priv = netdev_priv(dev);
+	struct flexcan_regs __iomem *regs = priv->regs;
 	struct sk_buff *skb;
 	struct can_frame *cf;
 	enum can_state new_state, rx_state, tx_state;
 	int flt;
 	struct can_berr_counter bec;
+	u32 timestamp;
+
+	timestamp = priv->read(&regs->timer) << 16;
 
 	flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
 	if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
@@ -650,7 +658,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
 	if (unlikely(new_state == CAN_STATE_BUS_OFF))
 		can_bus_off(dev);
 
-	can_rx_offload_queue_tail(&priv->offload, skb);
+	can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
 }
 
 static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
-- 
2.19.1

^ permalink raw reply related

* [PATCH 19/20] can: rx-offload: rename can_rx_offload_irq_queue_err_skb() to can_rx_offload_queue_tail()
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Oleksij Rempel, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Oleksij Rempel <o.rempel@pengutronix.de>

This function has nothing todo with error.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/flexcan.c      | 4 ++--
 drivers/net/can/rx-offload.c   | 5 +++--
 include/linux/can/rx-offload.h | 3 ++-
 3 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 41a175f80c4b..5923bd0ec118 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -610,7 +610,7 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr)
 	if (tx_errors)
 		dev->stats.tx_errors++;
 
-	can_rx_offload_irq_queue_err_skb(&priv->offload, skb);
+	can_rx_offload_queue_tail(&priv->offload, skb);
 }
 
 static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
@@ -650,7 +650,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr)
 	if (unlikely(new_state == CAN_STATE_BUS_OFF))
 		can_bus_off(dev);
 
-	can_rx_offload_irq_queue_err_skb(&priv->offload, skb);
+	can_rx_offload_queue_tail(&priv->offload, skb);
 }
 
 static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c
index c368686e2164..2ce4fa8698c7 100644
--- a/drivers/net/can/rx-offload.c
+++ b/drivers/net/can/rx-offload.c
@@ -257,7 +257,8 @@ unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
 }
 EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb);
 
-int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb)
+int can_rx_offload_queue_tail(struct can_rx_offload *offload,
+			      struct sk_buff *skb)
 {
 	if (skb_queue_len(&offload->skb_queue) >
 	    offload->skb_queue_len_max)
@@ -268,7 +269,7 @@ int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_b
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(can_rx_offload_irq_queue_err_skb);
+EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
 
 static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight)
 {
diff --git a/include/linux/can/rx-offload.h b/include/linux/can/rx-offload.h
index 01a7c9e5d8d8..8268811a697e 100644
--- a/include/linux/can/rx-offload.h
+++ b/include/linux/can/rx-offload.h
@@ -45,7 +45,8 @@ int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
 				struct sk_buff *skb, u32 timestamp);
 unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
 					 unsigned int idx, u32 timestamp);
-int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb);
+int can_rx_offload_queue_tail(struct can_rx_offload *offload,
+			      struct sk_buff *skb);
 void can_rx_offload_reset(struct can_rx_offload *offload);
 void can_rx_offload_del(struct can_rx_offload *offload);
 void can_rx_offload_enable(struct can_rx_offload *offload);
-- 
2.19.1

^ permalink raw reply related

* [PATCH 18/20] can: flexcan: handle tx-complete CAN frames via rx-offload infrastructure
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Oleksij Rempel, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Oleksij Rempel <o.rempel@pengutronix.de>

Current flexcan driver will put TX-ECHO in regular unsorted way, in
this case TX-ECHO can come after the response to the same TXed message.
In some cases, for example for J1939 stack, things will break.
This patch is using new rx-offload API to put the messages just in the
right place.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/flexcan.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 68b46395c580..41a175f80c4b 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -787,8 +787,11 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 
 	/* transmission complete interrupt */
 	if (reg_iflag2 & FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB)) {
+		u32 reg_ctrl = priv->read(&regs->mb[FLEXCAN_TX_MB].can_ctrl);
+
 		handled = IRQ_HANDLED;
-		stats->tx_bytes += can_get_echo_skb(dev, 0);
+		stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload,
+							       0, reg_ctrl << 16);
 		stats->tx_packets++;
 		can_led_event(dev, CAN_LED_EVENT_TX);
 
-- 
2.19.1

^ permalink raw reply related

* [PATCH 17/20] can: rx-offload: introduce can_rx_offload_get_echo_skb() and can_rx_offload_queue_sorted() functions
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Oleksij Rempel, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Oleksij Rempel <o.rempel@pengutronix.de>

Current CAN framework can't guarantee proper/chronological order
of RX and TX-ECHO messages. To make this possible, drivers should use
this functions instead of can_get_echo_skb().

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/rx-offload.c   | 46 ++++++++++++++++++++++++++++++++++
 include/linux/can/rx-offload.h |  4 +++
 2 files changed, 50 insertions(+)

diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c
index c7d05027a7a0..c368686e2164 100644
--- a/drivers/net/can/rx-offload.c
+++ b/drivers/net/can/rx-offload.c
@@ -211,6 +211,52 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
 }
 EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
 
+int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
+				struct sk_buff *skb, u32 timestamp)
+{
+	struct can_rx_offload_cb *cb;
+	unsigned long flags;
+
+	if (skb_queue_len(&offload->skb_queue) >
+	    offload->skb_queue_len_max)
+		return -ENOMEM;
+
+	cb = can_rx_offload_get_cb(skb);
+	cb->timestamp = timestamp;
+
+	spin_lock_irqsave(&offload->skb_queue.lock, flags);
+	__skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare);
+	spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
+
+	can_rx_offload_schedule(offload);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted);
+
+unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
+					 unsigned int idx, u32 timestamp)
+{
+	struct net_device *dev = offload->dev;
+	struct net_device_stats *stats = &dev->stats;
+	struct sk_buff *skb;
+	u8 len;
+	int err;
+
+	skb = __can_get_echo_skb(dev, idx, &len);
+	if (!skb)
+		return 0;
+
+	err = can_rx_offload_queue_sorted(offload, skb, timestamp);
+	if (err) {
+		stats->rx_errors++;
+		stats->tx_fifo_errors++;
+	}
+
+	return len;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb);
+
 int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb)
 {
 	if (skb_queue_len(&offload->skb_queue) >
diff --git a/include/linux/can/rx-offload.h b/include/linux/can/rx-offload.h
index cb31683bbe15..01a7c9e5d8d8 100644
--- a/include/linux/can/rx-offload.h
+++ b/include/linux/can/rx-offload.h
@@ -41,6 +41,10 @@ int can_rx_offload_add_timestamp(struct net_device *dev, struct can_rx_offload *
 int can_rx_offload_add_fifo(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight);
 int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 reg);
 int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
+int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
+				struct sk_buff *skb, u32 timestamp);
+unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
+					 unsigned int idx, u32 timestamp);
 int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb);
 void can_rx_offload_reset(struct can_rx_offload *offload);
 void can_rx_offload_del(struct can_rx_offload *offload);
-- 
2.19.1

^ permalink raw reply related

* [PATCH 16/20] can: dev: __can_get_echo_skb(): print error message, if trying to echo non existing skb
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-can, kernel, Marc Kleine-Budde, linux-stable
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

Prior to echoing a successfully transmitted CAN frame (by calling
can_get_echo_skb()), CAN drivers have to put the CAN frame (by calling
can_put_echo_skb() in the transmit function). These put and get function
take an index as parameter, which is used to identify the CAN frame.

A driver calling can_get_echo_skb() with a index not pointing to a skb
is a BUG, so add an appropriate error message.

Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/dev.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index c05e4d50d43d..3b3f88ffab53 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -480,6 +480,8 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb);
 struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr)
 {
 	struct can_priv *priv = netdev_priv(dev);
+	struct sk_buff *skb = priv->echo_skb[idx];
+	struct canfd_frame *cf;
 
 	if (idx >= priv->echo_skb_max) {
 		netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
@@ -487,21 +489,20 @@ struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8
 		return NULL;
 	}
 
-	if (priv->echo_skb[idx]) {
-		/* Using "struct canfd_frame::len" for the frame
-		 * length is supported on both CAN and CANFD frames.
-		 */
-		struct sk_buff *skb = priv->echo_skb[idx];
-		struct canfd_frame *cf = (struct canfd_frame *)skb->data;
-		u8 len = cf->len;
-
-		*len_ptr = len;
-		priv->echo_skb[idx] = NULL;
-
-		return skb;
+	if (!skb) {
+		netdev_err(dev, "%s: BUG! Trying to echo non existing skb: can_priv::echo_skb[%u]\n",
+			   __func__, idx);
+		return NULL;
 	}
 
-	return NULL;
+	/* Using "struct canfd_frame::len" for the frame
+	 * length is supported on both CAN and CANFD frames.
+	 */
+	cf = (struct canfd_frame *)skb->data;
+	*len_ptr = cf->len;
+	priv->echo_skb[idx] = NULL;
+
+	return skb;
 }
 
 /*
-- 
2.19.1

^ permalink raw reply related

* [PATCH 15/20] can: dev: __can_get_echo_skb(): Don't crash the kernel if can_priv::echo_skb is accessed out of bounds
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-can, kernel, Marc Kleine-Budde, linux-stable
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

If the "struct can_priv::echo_skb" is accessed out of bounds would lead
to a kernel crash. Better print a sensible warning message instead and
try to recover.

Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/dev.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 46cc5fec4043..c05e4d50d43d 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -481,7 +481,11 @@ struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8
 {
 	struct can_priv *priv = netdev_priv(dev);
 
-	BUG_ON(idx >= priv->echo_skb_max);
+	if (idx >= priv->echo_skb_max) {
+		netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
+			   __func__, idx, priv->echo_skb_max);
+		return NULL;
+	}
 
 	if (priv->echo_skb[idx]) {
 		/* Using "struct canfd_frame::len" for the frame
-- 
2.19.1

^ permalink raw reply related

* [PATCH 14/20] can: dev: __can_get_echo_skb(): replace struct can_frame by canfd_frame to access frame length
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-can, kernel, Marc Kleine-Budde, linux-stable
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

This patch replaces the use of "struct can_frame::can_dlc" by "struct
canfd_frame::len" to access the frame's length. As it is ensured that
both structures have a compatible memory layout for this member this is
no functional change. Futher, this compatibility is documented in a
comment.

Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/dev.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 80530ab37b1e..46cc5fec4043 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -484,11 +484,14 @@ struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8
 	BUG_ON(idx >= priv->echo_skb_max);
 
 	if (priv->echo_skb[idx]) {
+		/* Using "struct canfd_frame::len" for the frame
+		 * length is supported on both CAN and CANFD frames.
+		 */
 		struct sk_buff *skb = priv->echo_skb[idx];
-		struct can_frame *cf = (struct can_frame *)skb->data;
-		u8 dlc = cf->can_dlc;
+		struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+		u8 len = cf->len;
 
-		*len_ptr = dlc;
+		*len_ptr = len;
 		priv->echo_skb[idx] = NULL;
 
 		return skb;
-- 
2.19.1

^ permalink raw reply related

* [PATCH 12/20] can: flexcan: remove not needed struct flexcan_priv::tx_mb and struct flexcan_priv::tx_mb_idx
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-can, kernel, Marc Kleine-Budde, linux-stable
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

The previous patch changes the TX path to always use the last mailbox
regardless of the used offload scheme (rx-fifo or timestamp based). This
means members "tx_mb" and "tx_mb_idx" of the struct flexcan_priv don't
depend on the offload scheme, so replace them by compile time constants.

Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/flexcan.c | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 677c41701cf3..68b46395c580 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -258,9 +258,7 @@ struct flexcan_priv {
 	struct can_rx_offload offload;
 
 	struct flexcan_regs __iomem *regs;
-	struct flexcan_mb __iomem *tx_mb;
 	struct flexcan_mb __iomem *tx_mb_reserved;
-	u8 tx_mb_idx;
 	u32 reg_ctrl_default;
 	u32 reg_imask1_default;
 	u32 reg_imask2_default;
@@ -514,6 +512,7 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
 static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	const struct flexcan_priv *priv = netdev_priv(dev);
+	struct flexcan_regs __iomem *regs = priv->regs;
 	struct can_frame *cf = (struct can_frame *)skb->data;
 	u32 can_id;
 	u32 data;
@@ -536,17 +535,17 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
 
 	if (cf->can_dlc > 0) {
 		data = be32_to_cpup((__be32 *)&cf->data[0]);
-		priv->write(data, &priv->tx_mb->data[0]);
+		priv->write(data, &regs->mb[FLEXCAN_TX_MB].data[0]);
 	}
 	if (cf->can_dlc > 4) {
 		data = be32_to_cpup((__be32 *)&cf->data[4]);
-		priv->write(data, &priv->tx_mb->data[1]);
+		priv->write(data, &regs->mb[FLEXCAN_TX_MB].data[1]);
 	}
 
 	can_put_echo_skb(skb, dev, 0);
 
-	priv->write(can_id, &priv->tx_mb->can_id);
-	priv->write(ctrl, &priv->tx_mb->can_ctrl);
+	priv->write(can_id, &regs->mb[FLEXCAN_TX_MB].can_id);
+	priv->write(ctrl, &regs->mb[FLEXCAN_TX_MB].can_ctrl);
 
 	/* Errata ERR005829 step8:
 	 * Write twice INACTIVE(0x8) code to first MB.
@@ -737,7 +736,7 @@ static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
 	u32 iflag1, iflag2;
 
 	iflag2 = priv->read(&regs->iflag2) & priv->reg_imask2_default &
-		~FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+		~FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB);
 	iflag1 = priv->read(&regs->iflag1) & priv->reg_imask1_default;
 
 	return (u64)iflag2 << 32 | iflag1;
@@ -787,7 +786,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 	reg_iflag2 = priv->read(&regs->iflag2);
 
 	/* transmission complete interrupt */
-	if (reg_iflag2 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) {
+	if (reg_iflag2 & FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB)) {
 		handled = IRQ_HANDLED;
 		stats->tx_bytes += can_get_echo_skb(dev, 0);
 		stats->tx_packets++;
@@ -795,8 +794,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 
 		/* after sending a RTR frame MB is in RX mode */
 		priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-			    &priv->tx_mb->can_ctrl);
-		priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), &regs->iflag2);
+			    &regs->mb[FLEXCAN_TX_MB].can_ctrl);
+		priv->write(FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB), &regs->iflag2);
 		netif_wake_queue(dev);
 	}
 
@@ -938,7 +937,7 @@ static int flexcan_chip_start(struct net_device *dev)
 	reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
 	reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV |
 		FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ |
-		FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
+		FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_MB);
 
 	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
 		reg_mcr &= ~FLEXCAN_MCR_FEN;
@@ -1006,7 +1005,7 @@ static int flexcan_chip_start(struct net_device *dev)
 
 	/* mark TX mailbox as INACTIVE */
 	priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
-		    &priv->tx_mb->can_ctrl);
+		    &regs->mb[FLEXCAN_TX_MB].can_ctrl);
 
 	/* acceptance mask/acceptance code (accept everything) */
 	priv->write(0x0, &regs->rxgmask);
@@ -1365,11 +1364,9 @@ static int flexcan_probe(struct platform_device *pdev)
 		priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP];
 	else
 		priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO];
-	priv->tx_mb_idx = FLEXCAN_TX_MB;
-	priv->tx_mb = &regs->mb[priv->tx_mb_idx];
 
 	priv->reg_imask1_default = 0;
-	priv->reg_imask2_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+	priv->reg_imask2_default = FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB);
 
 	priv->offload.mailbox_read = flexcan_mailbox_read;
 
-- 
2.19.1

^ permalink raw reply related

* [PATCH 11/20] can: flexcan: Always use last mailbox for TX
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Alexander Stein, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Alexander Stein <alexander.stein@systec-electronic.com>

Essentially this patch moves the TX mailbox to position 63, regardless
of timestamp based offloading or RX FIFO. So mainly the iflag register
usage regarding TX has changed. The rest is consolidating RX FIFO and
timestamp offloading as they now use both the same TX mailbox.

The reason is a very annoying behavior regarding sending RTR frames when
_not_ using RX FIFO:

If a TX mailbox sent a RTR frame it becomes a RX mailbox. For that
reason flexcan_irq disables the TX mailbox again. But if during the time
the RTR was sent and the TX mailbox is disabled a new CAN frames is
received, it is lost without notice. The reason is that so-called
"Move-in" process starts from the lowest mailbox which happen to be a TX
mailbox set to EMPTY.

Steps to reproduce (I used an imx7d):
1. generate regular bursts of messages
2. send a RTR from flexcan with higher priority than burst messages every
   1ms, e.g. cangen -I 0x100 -L 0 -g 1 -R can0
3. notice a lost message without notification after some seconds

When running an iperf in parallel this problem is occurring even more
frequently. Using filters is not possible as at least one single CAN-ID
is allowed. Handling the TX MB during RX is also not possible as there
is no race-free disable of RX MB.

There is still a slight window when the described problem can occur. But
for that all RX MB must be in use which is essentially next to an
overrun. Still there will be no indication if it ever occurs.

Signed-off-by: Alexander Stein <alexander.stein@systec-electronic.com>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/flexcan.c | 67 +++++++++++++++++++--------------------
 1 file changed, 33 insertions(+), 34 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 0431f8d05518..677c41701cf3 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -135,13 +135,12 @@
 
 /* FLEXCAN interrupt flag register (IFLAG) bits */
 /* Errata ERR005829 step7: Reserve first valid MB */
-#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO	8
-#define FLEXCAN_TX_MB_OFF_FIFO		9
+#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO		8
 #define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP	0
-#define FLEXCAN_TX_MB_OFF_TIMESTAMP		1
-#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST	(FLEXCAN_TX_MB_OFF_TIMESTAMP + 1)
-#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST	63
-#define FLEXCAN_IFLAG_MB(x)		BIT(x)
+#define FLEXCAN_TX_MB				63
+#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST	(FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP + 1)
+#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST	(FLEXCAN_TX_MB - 1)
+#define FLEXCAN_IFLAG_MB(x)		BIT(x & 0x1f)
 #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW	BIT(7)
 #define FLEXCAN_IFLAG_RX_FIFO_WARN	BIT(6)
 #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE	BIT(5)
@@ -737,9 +736,9 @@ static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
 	struct flexcan_regs __iomem *regs = priv->regs;
 	u32 iflag1, iflag2;
 
-	iflag2 = priv->read(&regs->iflag2) & priv->reg_imask2_default;
-	iflag1 = priv->read(&regs->iflag1) & priv->reg_imask1_default &
+	iflag2 = priv->read(&regs->iflag2) & priv->reg_imask2_default &
 		~FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
+	iflag1 = priv->read(&regs->iflag1) & priv->reg_imask1_default;
 
 	return (u64)iflag2 << 32 | iflag1;
 }
@@ -751,11 +750,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 	struct flexcan_priv *priv = netdev_priv(dev);
 	struct flexcan_regs __iomem *regs = priv->regs;
 	irqreturn_t handled = IRQ_NONE;
-	u32 reg_iflag1, reg_esr;
+	u32 reg_iflag2, reg_esr;
 	enum can_state last_state = priv->can.state;
 
-	reg_iflag1 = priv->read(&regs->iflag1);
-
 	/* reception interrupt */
 	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
 		u64 reg_iflag;
@@ -769,6 +766,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 				break;
 		}
 	} else {
+		u32 reg_iflag1;
+
+		reg_iflag1 = priv->read(&regs->iflag1);
 		if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
 			handled = IRQ_HANDLED;
 			can_rx_offload_irq_offload_fifo(&priv->offload);
@@ -784,8 +784,10 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 		}
 	}
 
+	reg_iflag2 = priv->read(&regs->iflag2);
+
 	/* transmission complete interrupt */
-	if (reg_iflag1 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) {
+	if (reg_iflag2 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) {
 		handled = IRQ_HANDLED;
 		stats->tx_bytes += can_get_echo_skb(dev, 0);
 		stats->tx_packets++;
@@ -794,7 +796,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 		/* after sending a RTR frame MB is in RX mode */
 		priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
 			    &priv->tx_mb->can_ctrl);
-		priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), &regs->iflag1);
+		priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), &regs->iflag2);
 		netif_wake_queue(dev);
 	}
 
@@ -936,15 +938,13 @@ static int flexcan_chip_start(struct net_device *dev)
 	reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
 	reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV |
 		FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ |
-		FLEXCAN_MCR_IDAM_C;
+		FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
 
-	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
+	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
 		reg_mcr &= ~FLEXCAN_MCR_FEN;
-		reg_mcr |= FLEXCAN_MCR_MAXMB(priv->offload.mb_last);
-	} else {
-		reg_mcr |= FLEXCAN_MCR_FEN |
-			FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
-	}
+	else
+		reg_mcr |= FLEXCAN_MCR_FEN;
+
 	netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
 	priv->write(reg_mcr, &regs->mcr);
 
@@ -987,16 +987,17 @@ static int flexcan_chip_start(struct net_device *dev)
 		priv->write(reg_ctrl2, &regs->ctrl2);
 	}
 
-	/* clear and invalidate all mailboxes first */
-	for (i = priv->tx_mb_idx; i < ARRAY_SIZE(regs->mb); i++) {
-		priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
-			    &regs->mb[i].can_ctrl);
-	}
-
 	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
-		for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++)
+		for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) {
 			priv->write(FLEXCAN_MB_CODE_RX_EMPTY,
 				    &regs->mb[i].can_ctrl);
+		}
+	} else {
+		/* clear and invalidate unused mailboxes first */
+		for (i = FLEXCAN_TX_MB_RESERVED_OFF_FIFO; i <= ARRAY_SIZE(regs->mb); i++) {
+			priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
+				    &regs->mb[i].can_ctrl);
+		}
 	}
 
 	/* Errata ERR005829: mark first TX mailbox as INACTIVE */
@@ -1360,17 +1361,15 @@ static int flexcan_probe(struct platform_device *pdev)
 	priv->devtype_data = devtype_data;
 	priv->reg_xceiver = reg_xceiver;
 
-	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
-		priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_TIMESTAMP;
+	if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP)
 		priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP];
-	} else {
-		priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_FIFO;
+	else
 		priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO];
-	}
+	priv->tx_mb_idx = FLEXCAN_TX_MB;
 	priv->tx_mb = &regs->mb[priv->tx_mb_idx];
 
-	priv->reg_imask1_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
-	priv->reg_imask2_default = 0;
+	priv->reg_imask1_default = 0;
+	priv->reg_imask2_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
 
 	priv->offload.mailbox_read = flexcan_mailbox_read;
 
-- 
2.19.1

^ permalink raw reply related

* [PATCH 09/20] can: hi311x: Use level-triggered interrupt
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Lukas Wunner, Mathias Duckeck,
	Akshay Bhat, Casey Fitzpatrick, linux-stable, Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Lukas Wunner <lukas@wunner.de>

If the hi3110 shares the SPI bus with another traffic-intensive device
and packets are received in high volume (by a separate machine sending
with "cangen -g 0 -i -x"), reception stops after a few minutes and the
counter in /proc/interrupts stops incrementing.  Bus state is "active".
Bringing the interface down and back up reconvenes the reception.  The
issue is not observed when the hi3110 is the sole device on the SPI bus.

Using a level-triggered interrupt makes the issue go away and lets the
hi3110 successfully receive 2 GByte over the course of 5 days while a
ks8851 Ethernet chip on the same SPI bus handles 6 GByte of traffic.

Unfortunately the hi3110 datasheet is mum on the trigger type.  The pin
description on page 3 only specifies the polarity (active high):
http://www.holtic.com/documents/371-hi-3110_v-rev-kpdf.do

Cc: Mathias Duckeck <m.duckeck@kunbus.de>
Cc: Akshay Bhat <akshay.bhat@timesys.com>
Cc: Casey Fitzpatrick <casey.fitzpatrick@timesys.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 Documentation/devicetree/bindings/net/can/holt_hi311x.txt | 2 +-
 drivers/net/can/spi/hi311x.c                              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/can/holt_hi311x.txt b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
index 903a78da65be..3a9926f99937 100644
--- a/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
+++ b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
@@ -17,7 +17,7 @@ Example:
 		reg = <1>;
 		clocks = <&clk32m>;
 		interrupt-parent = <&gpio4>;
-		interrupts = <13 IRQ_TYPE_EDGE_RISING>;
+		interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
 		vdd-supply = <&reg5v0>;
 		xceiver-supply = <&reg5v0>;
 	};
diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c
index 53e320c92a8b..ddaf46239e39 100644
--- a/drivers/net/can/spi/hi311x.c
+++ b/drivers/net/can/spi/hi311x.c
@@ -760,7 +760,7 @@ static int hi3110_open(struct net_device *net)
 {
 	struct hi3110_priv *priv = netdev_priv(net);
 	struct spi_device *spi = priv->spi;
-	unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING;
+	unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_HIGH;
 	int ret;
 
 	ret = open_candev(net);
-- 
2.19.1

^ permalink raw reply related

* [PATCH 03/20] can: kvaser_usb: Fix accessing freed memory in kvaser_usb_start_xmit()
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Jimmy Assarsson, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Jimmy Assarsson <jimmyassarsson@gmail.com>

The call to can_put_echo_skb() may result in the skb being freed. The skb
is later used in the call to dev->ops->dev_frame_to_cmd().

This is avoided by moving the call to can_put_echo_skb() after
dev->ops->dev_frame_to_cmd().

Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Jimmy Assarsson <jimmyassarsson@gmail.com>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
index b939a4c10b84..c89c7d4900d7 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
@@ -528,7 +528,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 			context = &priv->tx_contexts[i];
 
 			context->echo_index = i;
-			can_put_echo_skb(skb, netdev, context->echo_index);
 			++priv->active_tx_contexts;
 			if (priv->active_tx_contexts >= (int)dev->max_tx_urbs)
 				netif_stop_queue(netdev);
@@ -553,7 +552,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 		dev_kfree_skb(skb);
 		spin_lock_irqsave(&priv->tx_contexts_lock, flags);
 
-		can_free_echo_skb(netdev, context->echo_index);
 		context->echo_index = dev->max_tx_urbs;
 		--priv->active_tx_contexts;
 		netif_wake_queue(netdev);
@@ -564,6 +562,8 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 
 	context->priv = priv;
 
+	can_put_echo_skb(skb, netdev, context->echo_index);
+
 	usb_fill_bulk_urb(urb, dev->udev,
 			  usb_sndbulkpipe(dev->udev,
 					  dev->bulk_out->bEndpointAddress),
-- 
2.19.1

^ permalink raw reply related

* [PATCH 02/20] can: kvaser_usb: Fix potential uninitialized variable use
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Jimmy Assarsson, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Jimmy Assarsson <jimmyassarsson@gmail.com>

If alloc_can_err_skb() fails, cf is never initialized.
Move assignment of cf inside check.

Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Jimmy Assarsson <jimmyassarsson@gmail.com>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
index c084bae5ec0a..5fc0be564274 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
@@ -1019,6 +1019,11 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv,
 					new_state : CAN_STATE_ERROR_ACTIVE;
 
 			can_change_state(netdev, cf, tx_state, rx_state);
+
+			if (priv->can.restart_ms &&
+			    old_state >= CAN_STATE_BUS_OFF &&
+			    new_state < CAN_STATE_BUS_OFF)
+				cf->can_id |= CAN_ERR_RESTARTED;
 		}
 
 		if (new_state == CAN_STATE_BUS_OFF) {
@@ -1028,11 +1033,6 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv,
 
 			can_bus_off(netdev);
 		}
-
-		if (priv->can.restart_ms &&
-		    old_state >= CAN_STATE_BUS_OFF &&
-		    new_state < CAN_STATE_BUS_OFF)
-			cf->can_id |= CAN_ERR_RESTARTED;
 	}
 
 	if (!skb) {
-- 
2.19.1

^ permalink raw reply related

* [PATCH 01/20] can: raw: check for CAN FD capable netdev in raw_sendmsg()
From: Marc Kleine-Budde @ 2018-11-12 11:57 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Oliver Hartkopp, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20181112115728.18331-1-mkl@pengutronix.de>

From: Oliver Hartkopp <socketcan@hartkopp.net>

When the socket is CAN FD enabled it can handle CAN FD frame
transmissions.  Add an additional check in raw_sendmsg() as a CAN2.0 CAN
driver (non CAN FD) should never see a CAN FD frame. Due to the commonly
used can_dropped_invalid_skb() function the CAN 2.0 driver would drop
that CAN FD frame anyway - but with this patch the user gets a proper
-EINVAL return code.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 net/can/raw.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/net/can/raw.c b/net/can/raw.c
index 1051eee82581..3aab7664933f 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -745,18 +745,19 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
 	} else
 		ifindex = ro->ifindex;
 
-	if (ro->fd_frames) {
+	dev = dev_get_by_index(sock_net(sk), ifindex);
+	if (!dev)
+		return -ENXIO;
+
+	err = -EINVAL;
+	if (ro->fd_frames && dev->mtu == CANFD_MTU) {
 		if (unlikely(size != CANFD_MTU && size != CAN_MTU))
-			return -EINVAL;
+			goto put_dev;
 	} else {
 		if (unlikely(size != CAN_MTU))
-			return -EINVAL;
+			goto put_dev;
 	}
 
-	dev = dev_get_by_index(sock_net(sk), ifindex);
-	if (!dev)
-		return -ENXIO;
-
 	skb = sock_alloc_send_skb(sk, size + sizeof(struct can_skb_priv),
 				  msg->msg_flags & MSG_DONTWAIT, &err);
 	if (!skb)
-- 
2.19.1

^ permalink raw reply related

* Re: [RFC] mm: Replace all open encodings for NUMA_NO_NODE
From: Anshuman Khandual @ 2018-11-12 11:47 UTC (permalink / raw)
  To: Hans Verkuil, linux-mm, linux-kernel
  Cc: ocfs2-devel, linux-fbdev, dri-devel, netdev, intel-wired-lan,
	linux-media, iommu, linux-rdma, dmaengine, linux-block,
	sparclinux, linuxppc-dev, linux-ia64, linux-alpha
In-Reply-To: <de754de5-cdf9-87d2-7ab2-a3630c034121@xs4all.nl>



On 11/12/2018 02:13 PM, Hans Verkuil wrote:
> On 11/12/2018 03:41 AM, Anshuman Khandual wrote:
>> At present there are multiple places where invalid node number is encoded
>> as -1. Even though implicitly understood it is always better to have macros
>> in there. Replace these open encodings for an invalid node number with the
>> global macro NUMA_NO_NODE. This helps remove NUMA related assumptions like
>> 'invalid node' from various places redirecting them to a common definition.
>>
>> Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
>> ---
>> Build tested this with multiple cross compiler options like alpha, sparc,
>> arm64, x86, powerpc64le etc with their default config which might not have
>> compiled tested all driver related changes. I will appreciate folks giving
>> this a test in their respective build environment.
>>
>> All these places for replacement were found by running the following grep
>> patterns on the entire kernel code. Please let me know if this might have
>> missed some instances. This might also have replaced some false positives.
>> I will appreciate suggestions, inputs and review.
> The 'node' in the drivers/media and the drivers/video sources has nothing to
> do with numa. It's an index for a framebuffer instead (i.e. the X in /dev/fbX).

Thanks for the input. Will drop the changes there.

^ permalink raw reply

* Re: [PATCH v2 4/5] phy: mvebu-cp110-comphy: convert to use eth phy mode and submode
From: Kishon Vijay Abraham I @ 2018-11-12 10:48 UTC (permalink / raw)
  To: Grygorii Strashko, David S. Miller, Russell King - ARM Linux,
	Antoine Tenart
  Cc: netdev, Sekhar Nori, linux-kernel, linux-arm-kernel,
	Tony Lindgren, linux-amlogic, linux-mediatek, Alexandre Belloni,
	Quentin Schulz, Vivek Gautam, Maxime Ripard, Chen-Yu Tsai,
	Carlo Caione, Chunfeng Yun, Matthias Brugger, Manu Gautam
In-Reply-To: <20181109234755.21687-5-grygorii.strashko@ti.com>

Hi Antoine, David Miller

Please ACK this patch if it looks okay.

Thanks
Kishon

On 10/11/18 5:17 AM, Grygorii Strashko wrote:
> Convert mvebu-cp110-comphy PHY driver to use recently introduced
> PHY_MODE_ETHERNET and phy_set_mode_ext().
> 
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> ---
>  drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +-----
>  drivers/phy/marvell/phy-mvebu-cp110-comphy.c    | 83 ++++++++++++++-----------
>  2 files changed, 48 insertions(+), 54 deletions(-)
> 
> diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> index 7a37a37..731793a 100644
> --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
> @@ -1165,28 +1165,13 @@ static void mvpp22_gop_setup_irq(struct mvpp2_port *port)
>   */
>  static int mvpp22_comphy_init(struct mvpp2_port *port)
>  {
> -	enum phy_mode mode;
>  	int ret;
>  
>  	if (!port->comphy)
>  		return 0;
>  
> -	switch (port->phy_interface) {
> -	case PHY_INTERFACE_MODE_SGMII:
> -	case PHY_INTERFACE_MODE_1000BASEX:
> -		mode = PHY_MODE_SGMII;
> -		break;
> -	case PHY_INTERFACE_MODE_2500BASEX:
> -		mode = PHY_MODE_2500SGMII;
> -		break;
> -	case PHY_INTERFACE_MODE_10GKR:
> -		mode = PHY_MODE_10GKR;
> -		break;
> -	default:
> -		return -EINVAL;
> -	}
> -
> -	ret = phy_set_mode(port->comphy, mode);
> +	ret = phy_set_mode_ext(port->comphy, PHY_MODE_ETHERNET,
> +			       port->phy_interface);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
> index 79b52c3..7dee72b 100644
> --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
> +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
> @@ -9,6 +9,7 @@
>  #include <linux/iopoll.h>
>  #include <linux/mfd/syscon.h>
>  #include <linux/module.h>
> +#include <linux/phy.h>
>  #include <linux/phy/phy.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
> @@ -131,26 +132,26 @@ struct mvebu_comhy_conf {
>  
>  static const struct mvebu_comhy_conf mvebu_comphy_cp110_modes[] = {
>  	/* lane 0 */
> -	MVEBU_COMPHY_CONF(0, 1, PHY_MODE_SGMII, 0x1),
> -	MVEBU_COMPHY_CONF(0, 1, PHY_MODE_2500SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(0, 1, PHY_INTERFACE_MODE_2500BASEX, 0x1),
>  	/* lane 1 */
> -	MVEBU_COMPHY_CONF(1, 2, PHY_MODE_SGMII, 0x1),
> -	MVEBU_COMPHY_CONF(1, 2, PHY_MODE_2500SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(1, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1),
>  	/* lane 2 */
> -	MVEBU_COMPHY_CONF(2, 0, PHY_MODE_SGMII, 0x1),
> -	MVEBU_COMPHY_CONF(2, 0, PHY_MODE_2500SGMII, 0x1),
> -	MVEBU_COMPHY_CONF(2, 0, PHY_MODE_10GKR, 0x1),
> +	MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_2500BASEX, 0x1),
> +	MVEBU_COMPHY_CONF(2, 0, PHY_INTERFACE_MODE_10GKR, 0x1),
>  	/* lane 3 */
> -	MVEBU_COMPHY_CONF(3, 1, PHY_MODE_SGMII, 0x2),
> -	MVEBU_COMPHY_CONF(3, 1, PHY_MODE_2500SGMII, 0x2),
> +	MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_SGMII, 0x2),
> +	MVEBU_COMPHY_CONF(3, 1, PHY_INTERFACE_MODE_2500BASEX, 0x2),
>  	/* lane 4 */
> -	MVEBU_COMPHY_CONF(4, 0, PHY_MODE_SGMII, 0x2),
> -	MVEBU_COMPHY_CONF(4, 0, PHY_MODE_2500SGMII, 0x2),
> -	MVEBU_COMPHY_CONF(4, 0, PHY_MODE_10GKR, 0x2),
> -	MVEBU_COMPHY_CONF(4, 1, PHY_MODE_SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_SGMII, 0x2),
> +	MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_2500BASEX, 0x2),
> +	MVEBU_COMPHY_CONF(4, 0, PHY_INTERFACE_MODE_10GKR, 0x2),
> +	MVEBU_COMPHY_CONF(4, 1, PHY_INTERFACE_MODE_SGMII, 0x1),
>  	/* lane 5 */
> -	MVEBU_COMPHY_CONF(5, 2, PHY_MODE_SGMII, 0x1),
> -	MVEBU_COMPHY_CONF(5, 2, PHY_MODE_2500SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_SGMII, 0x1),
> +	MVEBU_COMPHY_CONF(5, 2, PHY_INTERFACE_MODE_2500BASEX, 0x1),
>  };
>  
>  struct mvebu_comphy_priv {
> @@ -163,10 +164,12 @@ struct mvebu_comphy_lane {
>  	struct mvebu_comphy_priv *priv;
>  	unsigned id;
>  	enum phy_mode mode;
> +	int submode;
>  	int port;
>  };
>  
> -static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
> +static int mvebu_comphy_get_mux(int lane, int port,
> +				enum phy_mode mode, int submode)
>  {
>  	int i, n = ARRAY_SIZE(mvebu_comphy_cp110_modes);
>  
> @@ -177,7 +180,7 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
>  	for (i = 0; i < n; i++) {
>  		if (mvebu_comphy_cp110_modes[i].lane == lane &&
>  		    mvebu_comphy_cp110_modes[i].port == port &&
> -		    mvebu_comphy_cp110_modes[i].mode == mode)
> +		    mvebu_comphy_cp110_modes[i].mode == submode)
>  			break;
>  	}
>  
> @@ -187,8 +190,7 @@ static int mvebu_comphy_get_mux(int lane, int port, enum phy_mode mode)
>  	return mvebu_comphy_cp110_modes[i].mux;
>  }
>  
> -static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
> -					     enum phy_mode mode)
> +static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane)
>  {
>  	struct mvebu_comphy_priv *priv = lane->priv;
>  	u32 val;
> @@ -206,14 +208,14 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
>  		 MVEBU_COMPHY_SERDES_CFG0_HALF_BUS |
>  		 MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xf) |
>  		 MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xf));
> -	if (mode == PHY_MODE_10GKR)
> +	if (lane->submode == PHY_INTERFACE_MODE_10GKR)
>  		val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0xe) |
>  		       MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0xe);
> -	else if (mode == PHY_MODE_2500SGMII)
> +	else if (lane->submode == PHY_INTERFACE_MODE_2500BASEX)
>  		val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x8) |
>  		       MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x8) |
>  		       MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
> -	else if (mode == PHY_MODE_SGMII)
> +	else if (lane->submode == PHY_INTERFACE_MODE_SGMII)
>  		val |= MVEBU_COMPHY_SERDES_CFG0_GEN_RX(0x6) |
>  		       MVEBU_COMPHY_SERDES_CFG0_GEN_TX(0x6) |
>  		       MVEBU_COMPHY_SERDES_CFG0_HALF_BUS;
> @@ -243,7 +245,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
>  	/* refclk selection */
>  	val = readl(priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id));
>  	val &= ~MVEBU_COMPHY_MISC_CTRL0_REFCLK_SEL;
> -	if (mode == PHY_MODE_10GKR)
> +	if (lane->submode == PHY_INTERFACE_MODE_10GKR)
>  		val |= MVEBU_COMPHY_MISC_CTRL0_ICP_FORCE;
>  	writel(val, priv->base + MVEBU_COMPHY_MISC_CTRL0(lane->id));
>  
> @@ -261,8 +263,7 @@ static void mvebu_comphy_ethernet_init_reset(struct mvebu_comphy_lane *lane,
>  	writel(val, priv->base + MVEBU_COMPHY_LOOPBACK(lane->id));
>  }
>  
> -static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
> -				  enum phy_mode mode)
> +static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane)
>  {
>  	struct mvebu_comphy_priv *priv = lane->priv;
>  	u32 val;
> @@ -303,13 +304,13 @@ static int mvebu_comphy_init_plls(struct mvebu_comphy_lane *lane,
>  	return 0;
>  }
>  
> -static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
> +static int mvebu_comphy_set_mode_sgmii(struct phy *phy)
>  {
>  	struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
>  	struct mvebu_comphy_priv *priv = lane->priv;
>  	u32 val;
>  
> -	mvebu_comphy_ethernet_init_reset(lane, mode);
> +	mvebu_comphy_ethernet_init_reset(lane);
>  
>  	val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
>  	val &= ~MVEBU_COMPHY_RX_CTRL1_CLK8T_EN;
> @@ -330,7 +331,7 @@ static int mvebu_comphy_set_mode_sgmii(struct phy *phy, enum phy_mode mode)
>  	val |= MVEBU_COMPHY_GEN1_S0_TX_EMPH(0x1);
>  	writel(val, priv->base + MVEBU_COMPHY_GEN1_S0(lane->id));
>  
> -	return mvebu_comphy_init_plls(lane, PHY_MODE_SGMII);
> +	return mvebu_comphy_init_plls(lane);
>  }
>  
>  static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
> @@ -339,7 +340,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
>  	struct mvebu_comphy_priv *priv = lane->priv;
>  	u32 val;
>  
> -	mvebu_comphy_ethernet_init_reset(lane, PHY_MODE_10GKR);
> +	mvebu_comphy_ethernet_init_reset(lane);
>  
>  	val = readl(priv->base + MVEBU_COMPHY_RX_CTRL1(lane->id));
>  	val |= MVEBU_COMPHY_RX_CTRL1_RXCLK2X_SEL |
> @@ -469,7 +470,7 @@ static int mvebu_comphy_set_mode_10gkr(struct phy *phy)
>  	val |= MVEBU_COMPHY_EXT_SELV_RX_SAMPL(0x1a);
>  	writel(val, priv->base + MVEBU_COMPHY_EXT_SELV(lane->id));
>  
> -	return mvebu_comphy_init_plls(lane, PHY_MODE_10GKR);
> +	return mvebu_comphy_init_plls(lane);
>  }
>  
>  static int mvebu_comphy_power_on(struct phy *phy)
> @@ -479,7 +480,8 @@ static int mvebu_comphy_power_on(struct phy *phy)
>  	int ret, mux;
>  	u32 val;
>  
> -	mux = mvebu_comphy_get_mux(lane->id, lane->port, lane->mode);
> +	mux = mvebu_comphy_get_mux(lane->id, lane->port,
> +				   lane->mode, lane->submode);
>  	if (mux < 0)
>  		return -ENOTSUPP;
>  
> @@ -492,12 +494,12 @@ static int mvebu_comphy_power_on(struct phy *phy)
>  	val |= mux << MVEBU_COMPHY_SELECTOR_PHY(lane->id);
>  	regmap_write(priv->regmap, MVEBU_COMPHY_SELECTOR, val);
>  
> -	switch (lane->mode) {
> -	case PHY_MODE_SGMII:
> -	case PHY_MODE_2500SGMII:
> -		ret = mvebu_comphy_set_mode_sgmii(phy, lane->mode);
> +	switch (lane->submode) {
> +	case PHY_INTERFACE_MODE_SGMII:
> +	case PHY_INTERFACE_MODE_2500BASEX:
> +		ret = mvebu_comphy_set_mode_sgmii(phy);
>  		break;
> -	case PHY_MODE_10GKR:
> +	case PHY_INTERFACE_MODE_10GKR:
>  		ret = mvebu_comphy_set_mode_10gkr(phy);
>  		break;
>  	default:
> @@ -517,10 +519,17 @@ static int mvebu_comphy_set_mode(struct phy *phy,
>  {
>  	struct mvebu_comphy_lane *lane = phy_get_drvdata(phy);
>  
> -	if (mvebu_comphy_get_mux(lane->id, lane->port, mode) < 0)
> +	if (mode != PHY_MODE_ETHERNET)
> +		return -EINVAL;
> +
> +	if (submode == PHY_INTERFACE_MODE_1000BASEX)
> +		submode = PHY_INTERFACE_MODE_SGMII;
> +
> +	if (mvebu_comphy_get_mux(lane->id, lane->port, mode, submode) < 0)
>  		return -EINVAL;
>  
>  	lane->mode = mode;
> +	lane->submode = submode;
>  	return 0;
>  }
>  
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox