[PATCH v1 22/40] j1939: transport: add multi skb support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
---
 net/can/j1939/transport.c | 159 ++++++++++++++++++++++++++++++--------
 1 file changed, 126 insertions(+), 33 deletions(-)

diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index e0a324ffb19a..1970590a65d0 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -56,7 +56,7 @@ struct j1939_session {
 	 * this decreases locking problems a _lot_
 	 */
 	struct j1939_sk_buff_cb skcb;
-	struct sk_buff *skb;
+	struct sk_buff_head skb_queue;
 
 	/* all tx related stuff (last_txcmd, pkt.tx)
 	 * is protected (modified only) with the txtimer hrtimer
@@ -140,8 +140,7 @@ static void j1939_session_destroy(struct j1939_session *session)
 	j1939_session_list_lock(session->priv);
 	j1939_session_list_del(session);
 	j1939_session_list_unlock(session->priv);
-	WARN_ON_ONCE(skb_shared(session->skb));
-	kfree_skb(session->skb);
+	skb_queue_purge(&session->skb_queue);
 	j1939_priv_put(session->priv);
 	kfree(session);
 }
@@ -193,6 +192,79 @@ static inline void j1939_session_unlock(struct j1939_session *session)
 	spin_unlock_bh(&session->lock);
 }
 
+void j1939_session_skb_drop_old(struct j1939_session *session)
+{
+	struct sk_buff *do_skb;
+	struct j1939_sk_buff_cb *do_skcb;
+	unsigned int offset_start;
+	unsigned long flags;
+
+	if (skb_queue_len(&session->skb_queue) < 2)
+		return;
+
+	offset_start = session->pkt.done * 7;
+
+	spin_lock_irqsave(&session->skb_queue.lock, flags);
+	do_skb = skb_peek(&session->skb_queue);
+	do_skcb = j1939_skb_to_cb(do_skb);
+
+	if ((do_skcb->offset + do_skb->len) < offset_start) {
+		__skb_unlink(do_skb, &session->skb_queue);
+		kfree_skb(do_skb);
+	}
+	spin_unlock_irqrestore(&session->skb_queue.lock, flags);
+}
+
+
+void j1939_session_skb_queue(struct j1939_session *session,
+			     struct sk_buff *skb)
+{
+	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(skb);
+	struct j1939_priv *priv = session->priv;
+
+	j1939_ac_fixup(priv, skb);
+
+	if (j1939_address_is_unicast(skcb->addr.da) &&
+	    priv->ents[skcb->addr.da].nusers)
+		skcb->dst_flags |= J1939_ECU_LOCAL;
+
+	skcb->src_flags |= J1939_ECU_LOCAL;
+
+	skb_queue_tail(&session->skb_queue, skb);
+}
+
+static struct sk_buff *j1939_session_skb_find(struct j1939_session *session)
+{
+	struct j1939_priv *priv = session->priv;
+	struct sk_buff *skb = NULL;
+	struct sk_buff *do_skb;
+	struct j1939_sk_buff_cb *skcb, *do_skcb;
+	unsigned int offset_start;
+	unsigned long flags;
+
+	offset_start = session->pkt.dpo * 7;
+
+	spin_lock_irqsave(&session->skb_queue.lock, flags);
+	skb_queue_walk(&session->skb_queue, do_skb) {
+
+		do_skcb = j1939_skb_to_cb(do_skb);
+
+		if (offset_start >= do_skcb->offset
+		    && offset_start < (do_skcb->offset + do_skb->len)) {
+			skb = do_skb;
+			skcb = do_skcb;
+		}
+	}
+	spin_unlock_irqrestore(&session->skb_queue.lock, flags);
+
+	if (!skb)
+		netdev_warn(priv->ndev, "no skb found for start: %i, queue size: %i\n",
+			    offset_start,
+			    skb_queue_len(&session->skb_queue));
+
+	return skb;
+}
+
 /* see if we are receiver
  * returns 0 for broadcasts, although we will receive them
  */
@@ -389,10 +461,10 @@ static int j1939_tp_tx_dat(struct j1939_session *session,
 			   const u8 *dat, int len)
 {
 	struct j1939_priv *priv = session->priv;
-	struct sk_buff *skb;
+	struct sk_buff *skb, *se_skb;
 
-	skb = j1939_tp_tx_dat_new(session->skb,
-				  session->extd, false, false);
+	se_skb = j1939_session_skb_find(session);
+	skb = j1939_tp_tx_dat_new(se_skb, session->extd, false, false);
 	if (IS_ERR(skb))
 		return PTR_ERR(skb);
 
@@ -430,8 +502,9 @@ static inline int j1939_tp_tx_ctl(struct j1939_session *session,
 				  bool swap_src_dst, const u8 *dat)
 {
 	struct j1939_priv *priv = session->priv;
+	struct sk_buff *se_skb = j1939_session_skb_find(session);
 
-	return j1939_xtp_do_tx_ctl(priv, session->skb, session->extd,
+	return j1939_xtp_do_tx_ctl(priv, se_skb, session->extd,
 				   swap_src_dst,
 				   session->skcb.addr.pgn, dat);
 }
@@ -479,12 +552,14 @@ static int j1939_tp_txnext(struct j1939_session *session)
 	const u8 *tpdat;
 	int ret, offset, pkt_done, pkt_end;
 	unsigned int pkt, len, pdelay;
+	struct sk_buff *se_skb = j1939_session_skb_find(session);
+	struct j1939_sk_buff_cb *skcb = j1939_skb_to_cb(se_skb);
 
 	memset(dat, 0xff, sizeof(dat));
 
 	switch (session->last_cmd) {
 	case 0:
-		if (!j1939_tp_im_transmitter(session->skb))
+		if (!j1939_tp_im_transmitter(se_skb))
 			break;
 		dat[1] = (session->total_message_size >> 0);
 		dat[2] = (session->total_message_size >> 8);
@@ -517,7 +592,7 @@ static int j1939_tp_txnext(struct j1939_session *session)
 		break;
 	case J1939_TP_CMD_RTS:
 	case J1939_ETP_CMD_RTS: /* fallthrough */
-		if (!j1939_tp_im_receiver(session->skb))
+		if (!j1939_tp_im_receiver(se_skb))
 			break;
  tx_cts:
 		ret = 0;
@@ -548,12 +623,17 @@ static int j1939_tp_txnext(struct j1939_session *session)
 		j1939_tp_set_rxtimeout(session, 1250);
 		break;
 	case J1939_ETP_CMD_CTS:
-		if (j1939_tp_im_transmitter(session->skb) &&
+		if (j1939_tp_im_transmitter(se_skb) &&
 		    session->extd &&
 		    session->last_txcmd != J1939_ETP_CMD_DPO) {
 			/* do dpo */
 			dat[0] = J1939_ETP_CMD_DPO;
 			session->pkt.dpo = session->pkt.done;
+
+			/* Find new skb for updated session->pkt.dpo */
+			se_skb = j1939_session_skb_find(session);
+			skcb = j1939_skb_to_cb(se_skb);
+
 			pkt = session->pkt.dpo;
 			dat[1] = session->pkt.last - session->pkt.done;
 			dat[2] = (pkt >> 0);
@@ -572,7 +652,7 @@ static int j1939_tp_txnext(struct j1939_session *session)
 	case J1939_ETP_CMD_DPO: /* fallthrough */
 		if ((session->extd ||
 		     !j1939_cb_is_broadcast(&session->skcb)) &&
-		    j1939_tp_im_receiver(session->skb)) {
+		    j1939_tp_im_receiver(se_skb)) {
 			if (session->pkt.done >= session->pkt.total) {
 				if (session->extd) {
 					dat[0] = J1939_ETP_CMD_EOMA;
@@ -602,9 +682,9 @@ static int j1939_tp_txnext(struct j1939_session *session)
 			}
 		}
 	case J1939_TP_CMD_BAM: /* fallthrough */
-		if (!j1939_tp_im_transmitter(session->skb))
+		if (!j1939_tp_im_transmitter(se_skb))
 			break;
-		tpdat = session->skb->data;
+		tpdat = se_skb->data;
 		ret = 0;
 		pkt_done = 0;
 		if (!session->extd && j1939_cb_is_broadcast(&session->skcb))
@@ -614,8 +694,8 @@ static int j1939_tp_txnext(struct j1939_session *session)
 
 		while (session->pkt.tx < pkt_end) {
 			dat[0] = session->pkt.tx - session->pkt.dpo + 1;
-			offset = session->pkt.tx * 7;
-			len = session->total_message_size - offset;
+			offset = (session->pkt.tx * 7) - skcb->offset;
+			len =  se_skb->len - offset;
 			if (len > 7)
 				len = 7;
 			memcpy(&dat[1], &tpdat[offset], len);
@@ -664,18 +744,20 @@ static enum hrtimer_restart j1939_tp_txtimer(struct hrtimer *hrtimer)
 static void __j1939_session_drop(struct j1939_session *session)
 {
 	struct j1939_priv *priv = session->priv;
+	struct sk_buff *se_skb = j1939_session_skb_find(session);
 
 	if (session->transmission) {
-		if (session->skb && session->skb->sk)
-			j1939_sock_pending_del(session->skb->sk);
+		if (se_skb && se_skb->sk)
+			j1939_sock_pending_del(se_skb->sk);
 		wake_up_all(&priv->tp_wait);
 	}
 }
 
 static void j1939_session_completed(struct j1939_session *session)
 {
+	struct sk_buff *se_skb = j1939_session_skb_find(session);
 	/* distribute among j1939 receivers */
-	j1939_sk_recv(session->skb);
+	j1939_sk_recv(se_skb);
 	__j1939_session_drop(session);
 }
 
@@ -683,11 +765,12 @@ static void j1939_session_cancel(struct j1939_session *session,
 				 enum j1939_xtp_abort err)
 {
 	struct j1939_priv *priv = session->priv;
+	struct sk_buff *se_skb = j1939_session_skb_find(session);
 
 	/* do not send aborts on incoming broadcasts */
-	if (err && j1939_tp_im_involved_anydir(session->skb) &&
+	if (err && j1939_tp_im_involved_anydir(se_skb) &&
 	    !j1939_cb_is_broadcast(&session->skcb))
-		j1939_xtp_tx_abort(priv, session->skb, session->extd,
+		j1939_xtp_tx_abort(priv, se_skb, session->extd,
 				   !(session->skcb.src_flags & J1939_ECU_LOCAL),
 				   err, session->skcb.addr.pgn);
 
@@ -832,6 +915,7 @@ static void j1939_xtp_rx_cts(struct j1939_session *session, struct sk_buff *skb,
 	} else {
 		/* set packet counters only when not CTS(0) */
 		session->pkt.done = pkt - 1;
+		j1939_session_skb_drop_old(session);
 		session->pkt.last = session->pkt.done + dat[1];
 		if (session->pkt.last > session->pkt.total)
 			/* safety measure */
@@ -843,8 +927,10 @@ static void j1939_xtp_rx_cts(struct j1939_session *session, struct sk_buff *skb,
 	session->last_cmd = dat[0];
 	j1939_session_unlock(session);
 	if (dat[1]) {
+		struct sk_buff *se_skb = j1939_session_skb_find(session);
+
 		j1939_tp_set_rxtimeout(session, 1250);
-		if (j1939_tp_im_transmitter(session->skb))
+		if (j1939_tp_im_transmitter(se_skb))
 			j1939_tp_schedule_txtimer(session, 0);
 	} else {
 		/* CTS(0) */
@@ -876,9 +962,11 @@ static struct j1939_session *j1939_session_new(struct j1939_priv *priv,
 	j1939_priv_get(priv);
 	session->priv = priv;
 	session->total_message_size = skb->len;
-	/* corresponding skb_unref() is in j1939_session_fresh_new */
-	session->skb = skb_get(skb);
-	skcb = j1939_skb_to_cb(session->skb);
+
+	skb_queue_head_init(&session->skb_queue);
+	skb_queue_tail(&session->skb_queue, skb);
+
+	skcb = j1939_skb_to_cb(skb);
 	memcpy(&session->skcb, skcb, sizeof(session->skcb));
 
 	hrtimer_init(&session->txtimer, CLOCK_MONOTONIC,
@@ -922,17 +1010,17 @@ static struct j1939_session *j1939_session_fresh_new(struct j1939_priv *priv,
 	/* alloc data area */
 	skb_put(skb, size);
 	/* skb is recounted in j1939_session_new() */
-	WARN_ON_ONCE(skb_unref(skb));
 	return session;
 }
 
 static int j1939_session_insert(struct j1939_session *session)
 {
+	struct sk_buff *se_skb = j1939_session_skb_find(session);
 	struct j1939_priv *priv = session->priv;
 	struct j1939_session *pending;
 	int ret = 0;
 
-	pending = j1939_session_get_by_skb(priv, session->skb, session->extd,
+	pending = j1939_session_get_by_skb(priv, se_skb, session->extd,
 					   false);
 	if (pending) {
 		j1939_session_put(pending);
@@ -1094,7 +1182,9 @@ static void j1939_xtp_rx_dpo(struct j1939_session *session, struct sk_buff *skb)
 static void j1939_xtp_rx_dat(struct j1939_priv *priv, struct sk_buff *skb,
 			     bool extd)
 {
+	struct j1939_sk_buff_cb *skcb;
 	struct j1939_session *session;
+	struct sk_buff *se_skb;
 	const u8 *dat;
 	u8 *tpdat;
 	int offset;
@@ -1132,14 +1222,16 @@ static void j1939_xtp_rx_dat(struct j1939_priv *priv, struct sk_buff *skb,
 	}
 
 	packet = (dat[0] - 1 + session->pkt.dpo);
-	offset = packet * 7;
 	if (packet > session->pkt.total ||
 	    (session->pkt.done + 1) > session->pkt.total) {
 		netdev_info(priv->ndev, "%s: should have been completed\n",
 			    __func__);
 		goto out_session_unlock;
 	}
-	nbytes = session->total_message_size - offset;
+	se_skb = j1939_session_skb_find(session);
+	skcb = j1939_skb_to_cb(se_skb);
+	offset = packet * 7 - skcb->offset;
+	nbytes = se_skb->len - offset;
 	if (nbytes > 7)
 		nbytes = 7;
 	if (nbytes <= 0 || (nbytes + 1) > skb->len) {
@@ -1147,7 +1239,8 @@ static void j1939_xtp_rx_dat(struct j1939_priv *priv, struct sk_buff *skb,
 			    nbytes, skb->len);
 		goto out_session_unlock;
 	}
-	tpdat = session->skb->data;
+
+	tpdat = se_skb->data;
 	memcpy(&tpdat[offset], &dat[1], nbytes);
 	if (packet == session->pkt.done)
 		++session->pkt.done;
@@ -1166,7 +1259,7 @@ static void j1939_xtp_rx_dat(struct j1939_priv *priv, struct sk_buff *skb,
 		j1939_session_completed(session);
 	} else if (do_cts_eoma) {
 		j1939_tp_set_rxtimeout(session, 1250);
-		if (j1939_tp_im_receiver(session->skb))
+		if (j1939_tp_im_receiver(se_skb))
 			j1939_tp_schedule_txtimer(session, 0);
 	} else {
 		j1939_tp_set_rxtimeout(session, 250);
@@ -1239,8 +1332,6 @@ int j1939_tp_send(struct j1939_priv *priv, struct sk_buff *skb)
 		return -ENOMEM;
 
 	/* skb is recounted in j1939_session_new() */
-	WARN_ON_ONCE(skb_unref(skb));
-
 	session->extd = extd;
 	session->transmission = true;
 	session->pkt.total = (skb->len + 6) / 7;
@@ -1282,6 +1373,7 @@ static void j1939_tp_cmd_recv(struct j1939_priv *priv, struct sk_buff *skb,
 	struct j1939_session *session;
 	bool extd = J1939_REGULAR;
 	const u8 *dat = skb->data;
+	struct sk_buff *se_skb;
 
 	switch (*dat) {
 	case J1939_ETP_CMD_RTS:
@@ -1312,10 +1404,11 @@ static void j1939_tp_cmd_recv(struct j1939_priv *priv, struct sk_buff *skb,
 		}
 		session->last_cmd = dat[0];
 
+		se_skb = j1939_session_skb_find(session);
 		j1939_tp_set_rxtimeout(session, 1250);
 
 		if ((dat[0] != J1939_TP_CMD_BAM) &&
-		    j1939_tp_im_receiver(session->skb))
+		    j1939_tp_im_receiver(se_skb))
 			j1939_tp_schedule_txtimer(session, 0);
 
 		j1939_session_put(session);
-- 
2.19.1




[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux