[PATCH v1 1/2] j1939: add MSG_ERRQUEUE support

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

 



Signed-off-by: Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx>
---
 include/uapi/linux/can/j1939.h |  6 +++++
 net/can/j1939/j1939-priv.h     |  2 ++
 net/can/j1939/socket.c         | 48 +++++++++++++++++++++++++++++++---
 net/can/j1939/transport.c      | 33 ++++++++++++++++++++---
 4 files changed, 83 insertions(+), 6 deletions(-)

diff --git a/include/uapi/linux/can/j1939.h b/include/uapi/linux/can/j1939.h
index c7eb94d2ab10..77f8068bcc62 100644
--- a/include/uapi/linux/can/j1939.h
+++ b/include/uapi/linux/can/j1939.h
@@ -72,6 +72,8 @@ enum {
 	SCM_J1939_DEST_ADDR = 1,
 	SCM_J1939_DEST_NAME = 2,
 	SCM_J1939_PRIO = 3,
+	SCM_J1939_RECVERR = 4,
+	SCM_J1939_PKTINFO = 5,
 };
 
 struct j1939_filter {
@@ -83,6 +85,10 @@ struct j1939_filter {
 	__u8 addr_mask;
 };
 
+struct j1939_pktinfo {
+	__u64 cookie;
+};
+
 #define J1939_FILTER_MAX 512 /* maximum number of j1939_filter set via setsockopt() */
 
 #endif /* !_UAPI_CAN_J1939_H_ */
diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h
index 4cb2e63a86c4..cf42550de6d2 100644
--- a/net/can/j1939/j1939-priv.h
+++ b/net/can/j1939/j1939-priv.h
@@ -207,6 +207,8 @@ struct j1939_session {
 	bool transmission;
 	bool extd;
 	unsigned int total_message_size; /* Total message size, number of bytes */
+	int err;
+	u64 cookie;
 
 	/* Packets counters for a (extended) transfer session. The packet is
 	 * maximal of 7 bytes. */
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
index ea9ce6d99332..c88c67e93536 100644
--- a/net/can/j1939/socket.c
+++ b/net/can/j1939/socket.c
@@ -608,6 +608,10 @@ static int j1939_sk_recvmsg(struct socket *sock, struct msghdr *msg,
 	struct j1939_sk_buff_cb *skcb;
 	int ret = 0;
 
+	if (flags & MSG_ERRQUEUE)
+		return sock_recv_errqueue(sock->sk, msg, size, SOL_CAN_J1939,
+					  SCM_J1939_RECVERR);
+
 	skb = skb_recv_datagram(sk, flags, 0, &ret);
 	if (!skb)
 		return ret;
@@ -721,8 +725,9 @@ void j1939_sk_send_multi_abort(struct j1939_priv *priv, struct sock *sk,
 	sk->sk_error_report(sk);
 }
 
-static int j1939_sk_send_multi(struct j1939_priv *priv,  struct sock *sk,
-			       struct msghdr *msg, size_t size)
+static int j1939_sk_send_multi(struct j1939_priv *priv, struct sock *sk,
+			       struct msghdr *msg, size_t size,
+			       struct j1939_pktinfo *info)
 
 {
 	struct j1939_sock *jsk = j1939_sk(sk);
@@ -788,6 +793,7 @@ static int j1939_sk_send_multi(struct j1939_priv *priv,  struct sock *sk,
 					ret = PTR_ERR(session);
 					goto kfree_skb;
 				}
+				session->cookie = info->cookie;
 			}
 		} else {
 			j1939_session_skb_queue(session, skb);
@@ -845,6 +851,35 @@ static int j1939_sk_send_one(struct j1939_priv *priv,  struct sock *sk,
 	return ret ? ret : size;
 }
 
+static int j1939_sk_cmsg_send(struct sock *sk, struct msghdr *msg,
+			      struct j1939_pktinfo *info)
+{
+	struct cmsghdr *cmsg;
+
+	for_each_cmsghdr(cmsg, msg) {
+		if (!CMSG_OK(msg, cmsg))
+			return -EINVAL;
+
+		if (cmsg->cmsg_level != SOL_CAN_J1939)
+			continue;
+		switch (cmsg->cmsg_type) {
+		case SCM_J1939_PKTINFO:
+		{
+			struct j1939_pktinfo *tinfo;
+
+			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct j1939_pktinfo)))
+				return -EINVAL;
+			tinfo = (struct j1939_pktinfo *)CMSG_DATA(cmsg);
+			memcpy(info, tinfo, sizeof(*info));
+			break;
+		}
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
 static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
 			    size_t size)
 {
@@ -852,6 +887,7 @@ static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
 	struct j1939_sock *jsk = j1939_sk(sk);
 	struct j1939_priv *priv;
 	struct net_device *ndev;
+	struct j1939_pktinfo info;
 	int ifindex;
 	int ret;
 
@@ -894,6 +930,12 @@ static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
 			return -EACCES;
 	}
 
+	if (msg->msg_controllen) {
+		ret = j1939_sk_cmsg_send(sk, msg, &info);
+		if (unlikely(ret))
+			return ret;
+	}
+
 	ndev = dev_get_by_index(sock_net(sk), ifindex);
 	if (!ndev)
 		return -ENXIO;
@@ -904,7 +946,7 @@ static int j1939_sk_sendmsg(struct socket *sock, struct msghdr *msg,
 
 	if (size > 8)
 		/* re-route via transport protocol */
-		ret = j1939_sk_send_multi(priv, sk, msg, size);
+		ret = j1939_sk_send_multi(priv, sk, msg, size, &info);
 	else
 		ret = j1939_sk_send_one(priv, sk, msg, size);
 
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index 734b0fd23078..330368c3a9a0 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -5,6 +5,7 @@
 // Copyright (c) 2018 Protonic, Robin van der Gracht <robin@xxxxxxxxxxx>
 
 #include <linux/can/skb.h>
+#include <linux/errqueue.h>
 
 #include "j1939-priv.h"
 
@@ -247,6 +248,28 @@ void j1939_session_get(struct j1939_session *session)
 	kref_get(&session->kref);
 }
 
+static void j1939_sk_err_queue(struct j1939_session *session)
+{
+	struct sock *sk = session->sk;
+	struct j1939_pktinfo info;
+	struct sock_exterr_skb *serr;
+	struct sk_buff *skb;
+
+	skb = alloc_skb(sizeof(info), GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	info.cookie = session->cookie;
+	skb_put_data(skb, &info, sizeof(info));
+
+	serr = SKB_EXT_ERR(skb);
+	serr->ee.ee_errno = session->err;
+	serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
+
+	if (sock_queue_err_skb(sk, skb))
+		kfree_skb(skb);
+};
+
 /* session completion functions */
 static void __j1939_session_drop(struct j1939_session *session)
 {
@@ -261,6 +284,9 @@ static void __j1939_session_drop(struct j1939_session *session)
 
 static void j1939_session_destroy(struct j1939_session *session)
 {
+
+	if (session->sk)
+		j1939_sk_err_queue(session);
 	j1939_session_list_lock(session->priv);
 	j1939_session_list_del(session);
 	j1939_session_list_unlock(session->priv);
@@ -867,6 +893,7 @@ static void j1939_session_cancel(struct j1939_session *session,
 
 	WARN_ON_ONCE(!err);
 
+	session->err = j1939_xtp_abort_to_errno(priv, err);
 	/* do not send aborts on incoming broadcasts */
 	if (!j1939_cb_is_broadcast(&session->skcb))
 		j1939_xtp_tx_abort(priv, &session->skcb, session->extd,
@@ -874,8 +901,7 @@ static void j1939_session_cancel(struct j1939_session *session,
 				   err, session->skcb.addr.pgn);
 
 	if (session->sk)
-		j1939_sk_send_multi_abort(priv, session->sk,
-					  j1939_xtp_abort_to_errno(priv, err));
+		j1939_sk_send_multi_abort(priv, session->sk, session->err);
 }
 
 static enum hrtimer_restart j1939_tp_rxtimer(struct hrtimer *hrtimer)
@@ -950,9 +976,10 @@ static void j1939_xtp_rx_abort_one(struct j1939_priv *priv, struct sk_buff *skb,
 		u8 abort = skb->data[1];
 
 		j1939_session_timers_cancel(session);
+		session->err = j1939_xtp_abort_to_errno(priv, abort);
 		if (session->sk)
 			j1939_sk_send_multi_abort(priv, session->sk,
-						  j1939_xtp_abort_to_errno(priv, abort));
+						  session->err);
 	}
 
 	/* TODO: maybe cancel current connection
-- 
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