[RHEL8,COMMIT] netlink: add an ability to restore messages in a receive queue

Submitted by Konstantin Khorenko on March 10, 2020, 3:01 p.m.

Details

Message ID 202003101501.02AF1FUB024561@finist_co8.work.ct
State New
Series "fixes to VZ8 required for criu"
Headers show

Commit Message

Konstantin Khorenko March 10, 2020, 3:01 p.m.
The commit is pushed to "branch-rh8-4.18.0-80.1.2.vz8.3.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh8-4.18.0-80.1.2.vz8.3.2
------>
commit 1ec093f4e0c38810c9b30db1d12ad04416a9509a
Author: Andrey Vagin <avagin@openvz.org>
Date:   Tue Mar 10 18:01:15 2020 +0300

    netlink: add an ability to restore messages in a receive queue
    
    This patch adds an repair mode for netlink sockets. sendmsg queues
    messages into a receive queue if a socket is in the repair mode.
    
    https://jira.sw.ru/browse/PSBM-28386
    
    Signed-off-by: Andrey Vagin <avagin@virtuozzo.com>
    Reviewed-by: Cyrill Gorcunov <gorcunov@virtuozzo.com>
    
    https://jira.sw.ru/browse/PSBM-101289
    vz7 commit: 2f8d39b73cfca ("netlink: add an ability to restore messages in a
    receive queue")
    
    Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
 include/uapi/linux/netlink.h |  1 +
 net/netlink/af_netlink.c     | 49 ++++++++++++++++++++++++++++++--------------
 net/netlink/af_netlink.h     |  2 ++
 3 files changed, 37 insertions(+), 15 deletions(-)

Patch hide | download patch | download mbox

diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index 776bc92e9118..efc2a552585d 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -155,6 +155,7 @@  enum nlmsgerr_attrs {
 #define NETLINK_LIST_MEMBERSHIPS	9
 #define NETLINK_CAP_ACK			10
 #define NETLINK_EXT_ACK			11
+#define NETLINK_REPAIR			127
 
 struct nl_pktinfo {
 	__u32	group;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 15d1f2b1e339..b03f9e368bf4 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1351,6 +1351,7 @@  static int netlink_unicast_kernel(struct sock *sk, struct sk_buff *skb,
 int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
 		    u32 portid, int nonblock)
 {
+	struct netlink_sock *nlk = nlk_sk(ssk);
 	struct sock *sk;
 	int err;
 	long timeo;
@@ -1359,19 +1360,24 @@  int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
 
 	timeo = sock_sndtimeo(ssk, nonblock);
 retry:
-	sk = netlink_getsockbyportid(ssk, portid);
-	if (IS_ERR(sk)) {
-		kfree_skb(skb);
-		return PTR_ERR(sk);
-	}
-	if (netlink_is_kernel(sk))
-		return netlink_unicast_kernel(sk, skb, ssk);
+	if (nlk->flags & NETLINK_F_REPAIR) {
+		sk = ssk;
+		sock_hold(sk);
+	} else {
+		sk = netlink_getsockbyportid(ssk, portid);
+		if (IS_ERR(sk)) {
+			kfree_skb(skb);
+			return PTR_ERR(sk);
+		}
+		if (netlink_is_kernel(sk))
+			return netlink_unicast_kernel(sk, skb, ssk);
 
-	if (sk_filter(sk, skb)) {
-		err = skb->len;
-		kfree_skb(skb);
-		sock_put(sk);
-		return err;
+		if (sk_filter(sk, skb)) {
+			err = skb->len;
+			kfree_skb(skb);
+			sock_put(sk);
+			return err;
+		}
 	}
 
 	err = netlink_attachskb(sk, skb, &timeo, ssk);
@@ -1666,6 +1672,13 @@  static int netlink_setsockopt(struct socket *sock, int level, int optname,
 		return -EFAULT;
 
 	switch (optname) {
+	case NETLINK_REPAIR:
+		if (val)
+			nlk->flags |= NETLINK_F_REPAIR;
+		else
+			nlk->flags &= ~NETLINK_F_REPAIR;
+		err = 0;
+		break;
 	case NETLINK_PKTINFO:
 		if (val)
 			nlk->flags |= NETLINK_F_RECV_PKTINFO;
@@ -1866,6 +1879,7 @@  static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
 	int err;
 	struct scm_cookie scm;
 	u32 netlink_skb_flags = 0;
+	bool repair = nlk->flags & NETLINK_F_REPAIR;
 
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
@@ -1884,7 +1898,8 @@  static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
 		dst_group = ffs(addr->nl_groups);
 		err =  -EPERM;
 		if ((dst_group || dst_portid) &&
-		    !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND))
+		    !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND &&
+		    !repair))
 			goto out;
 		netlink_skb_flags |= NETLINK_SKB_DST;
 	} else {
@@ -1909,7 +1924,11 @@  static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
 	if (skb == NULL)
 		goto out;
 
-	NETLINK_CB(skb).portid	= nlk->portid;
+	if (unlikely(repair))
+		NETLINK_CB(skb).portid = dst_portid;
+	else
+		NETLINK_CB(skb).portid	= nlk->portid;
+
 	NETLINK_CB(skb).dst_group = dst_group;
 	NETLINK_CB(skb).creds	= scm.creds;
 	NETLINK_CB(skb).flags	= netlink_skb_flags;
@@ -1926,7 +1945,7 @@  static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
 		goto out;
 	}
 
-	if (dst_group) {
+	if (dst_group && !repair) {
 		refcount_inc(&skb->users);
 		netlink_broadcast(sk, skb, dst_portid, dst_group, GFP_KERNEL);
 	}
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index 962de7b3c023..e6bfca2f27af 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -16,6 +16,8 @@ 
 #define NETLINK_F_CAP_ACK		0x20
 #define NETLINK_F_EXT_ACK		0x40
 
+#define NETLINK_F_REPAIR		0x80000000
+
 #define NLGRPSZ(x)	(ALIGN(x, sizeof(unsigned long) * 8) / 8)
 #define NLGRPLONGS(x)	(NLGRPSZ(x)/sizeof(unsigned long))