[PATCH v1 18/25] j1939: socket: add non blocking ETP send support

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

 



to provide MSG_DONTWAIT support, and support for signals (SIGSTOP,
SIGCONT,...) we should be able to continue started ETP transfers.
This means, we have to keep track of complete transfer size and size of
allocated buffers.

Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
---
 net/can/j1939/socket.c | 97 ++++++++++++++++++++++++++++++++----------
 1 file changed, 75 insertions(+), 22 deletions(-)

diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
index c05e74a0abdb..8bca6c864fac 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -31,6 +31,9 @@ struct j1939_sock {
 	int nfilters;
 	pgn_t pgn_rx_filter;
 
+	size_t etp_tx_complete_size;
+	size_t etp_tx_done_size;
+
 	/* j1939 may emit equal PGN (!= equal CAN-id's) out of order
 	 * when transport protocol comes in.
 	 * To allow emitting in order, keep a 'pending' nr. of packets
@@ -236,6 +239,9 @@ static int j1939_sk_init(struct sock *sk)
 	jsk->addr.dst_pgn = J1939_NO_PGN;
 	jsk->pgn_rx_filter = J1939_NO_PGN;
 	atomic_set(&jsk->skb_pending, 0);
+	jsk->etp_tx_complete_size = 0;
+	jsk->etp_tx_done_size = 0;
+
 	return 0;
 }
 
@@ -709,15 +715,22 @@ void j1939_sk_send_multi_abort(struct j1939_priv *priv, struct sock *sk,
 }
 
 static int j1939_sk_send_multi(struct j1939_priv *priv,  struct sock *sk,
-			       struct msghdr *msg, size_t complete_size)
+			       struct msghdr *msg, size_t size)
 
 {
+	struct j1939_sock *jsk = j1939_sk(sk);
 	struct j1939_session *session = NULL;
 	struct sk_buff *skb;
-	size_t segment_size, todo_size, done_size = 0;
+	size_t segment_size, todo_size;
 	int ret = 0;
 
-	segment_size = todo_size = complete_size;
+	if (!jsk->etp_tx_done_size) {
+		j1939_sock_pending_add(&jsk->sk);
+		jsk->etp_tx_complete_size = size;
+	} else if (jsk->etp_tx_complete_size != jsk->etp_tx_done_size + size)
+		return -EIO;
+
+	todo_size = size;
 
 	while (todo_size) {
 		struct j1939_sk_buff_cb *skcb;
@@ -728,34 +741,75 @@ static int j1939_sk_send_multi(struct j1939_priv *priv,  struct sock *sk,
 			segment_size = todo_size;
 
 		/* Allocate skb for one segment */
-		skb = j1939_sk_alloc_skb(priv->ndev, sk, msg, segment_size, &ret);
+		skb = j1939_sk_alloc_skb(priv->ndev, sk, msg, segment_size,
+					 &ret);
 		if (ret)
 			break;
 
 		skcb = j1939_skb_to_cb(skb);
-		skcb->offset  = done_size;
-
-		todo_size -= segment_size;
-		done_size += segment_size;
+		skcb->offset = jsk->etp_tx_done_size;
 
 		if (!session) {
-			/* create new session with complete_size and attach
-			 * skb segment
-			 */
-			session = j1939_tp_send(priv, skb, complete_size);
-			if (IS_ERR(session))
-				/* FIXME: free skb? Who discards the skb in error case?
+			if (jsk->etp_tx_done_size) {
+				bool extd = J1939_REGULAR;
+
+				if (jsk->etp_tx_complete_size > J1939_MAX_TP_PACKET_SIZE)
+					extd = J1939_EXTENDED;
+
+				session = j1939_session_get_by_skcb(priv, skcb,
+								    extd, false);
+				if (IS_ERR_OR_NULL(session)) {
+					/* FIXME: free skb? Who discards the skb in error case?
+					 */
+					jsk->etp_tx_done_size = 0;
+					return PTR_ERR(session);
+				}
+
+				j1939_session_skb_queue(session, skb);
+			} else {
+				/* create new session with etp_tx_complete_size and attach
+				 * skb segment
 				 */
-				return PTR_ERR(session);
+				session = j1939_tp_send(priv, skb,
+							jsk->etp_tx_complete_size);
+				if (IS_ERR(session)) {
+					/* FIXME: free skb? Who discards the skb in error case?
+					 */
+					jsk->etp_tx_done_size = 0;
+					return PTR_ERR(session);
+				}
+			}
 		} else {
 			j1939_session_skb_queue(session, skb);
 		}
 
+		todo_size -= segment_size;
+		jsk->etp_tx_done_size += segment_size;
+	}
+
+	switch (ret) {
+	case 0: /* OK */
+		if (todo_size)
+			netdev_warn(priv->ndev, "no error found and not completely queued?! %i\n", todo_size);
+		ret = size;
+		jsk->etp_tx_done_size = 0;
+		break;
+	case -ERESTARTSYS:
+		ret = -EINTR;
+		/* fall trough */
+	case -EAGAIN: /* OK */
+		if (todo_size != size)
+			ret = size - todo_size;
+		break;
+	default: /* ERROR */
+		/* skb session queue will be purged if we are the last user */
+		jsk->etp_tx_done_size = 0;
 	}
 
-	j1939_session_put(session);
+	if (session)
+		j1939_session_put(session);
 
-	return 0;
+	return ret;
 }
 
 static int j1939_sk_send_one(struct j1939_priv *priv,  struct sock *sk,
@@ -769,7 +823,9 @@ static int j1939_sk_send_one(struct j1939_priv *priv,  struct sock *sk,
 	if (ret)
 		return ret;
 
-	return j1939_send_one(priv, skb);
+	ret = j1939_send_one(priv, skb);
+
+	return ret ? ret : size;
 }
 
 static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
@@ -829,7 +885,6 @@ static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
 	if (!priv)
 		return -EINVAL;
 
-	j1939_sock_pending_add(&jsk->sk);
 	if (size > 8)
 		/* re-route via transport protocol */
 		ret = j1939_sk_send_multi(priv, sk, msg, size);
@@ -837,11 +892,9 @@ static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
 		ret = j1939_sk_send_one(priv, sk, msg, size);
 
 	j1939_priv_put(priv);
-	if (ret < 0 || size <= 8)
-		j1939_sock_pending_del(&jsk->sk);
 
 	dev_put(ndev);
-	return (ret < 0) ? ret : size;
+	return ret;
 }
 
 void j1939_sk_netdev_event(struct net_device *ndev, int error_code)
-- 
2.20.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