[v3,10/15] unix: Resolve senders of packets in receive queue of DGRAM socket

Submitted by Kirill Tkhai on June 1, 2016, 3:12 p.m.

Details

Message ID 146479396393.19418.90262442045938840.stgit@pro
State Rejected
Series "Support for packet's msg_name in receive queue of promiscous DGRAM sockets"
Headers show

Commit Message

Kirill Tkhai June 1, 2016, 3:12 p.m.
Determine unique senders of packets in receive queue, and notify
every of them, that it has a receiver. Link the receiver ino in
unix_sk_info::receivers list of the sender.

Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com>
---
 criu/include/sk-queue.h |    6 ++++
 criu/sk-queue.c         |   73 +++++++++++++++++++++++++++++++++++++++++++++++
 criu/sk-unix.c          |   55 ++++++++++++++++++++++++++++++++++-
 3 files changed, 132 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/include/sk-queue.h b/criu/include/sk-queue.h
index 5fd2e01..0ad9da2 100644
--- a/criu/include/sk-queue.h
+++ b/criu/include/sk-queue.h
@@ -3,8 +3,14 @@ 
 
 #define SK_NONAME_SENDER	(~0ULL)
 
+struct sk_ino {
+	struct list_head list;
+	u32 ino;
+};
+
 extern struct collect_image_info sk_queues_cinfo;
 extern int dump_sk_queue(int sock_fd, int sock_id, u64 (*get_sender)(const char *, int));
 extern int restore_sk_queue(int fd, unsigned int peer_id);
+extern int resolve_senders(unsigned int id, u32 ino, int (*add_receiver)(u32, struct sk_ino *, bool, void *), void *);
 
 #endif /* __CR_SK_QUEUE_H__ */
diff --git a/criu/sk-queue.c b/criu/sk-queue.c
index ded9ca9..f40bc2d 100644
--- a/criu/sk-queue.c
+++ b/criu/sk-queue.c
@@ -17,6 +17,7 @@ 
 #include "servicefd.h"
 #include "cr_options.h"
 #include "util.h"
+#include "rst-malloc.h"
 #include "util-pie.h"
 #include "sockets.h"
 
@@ -194,6 +195,78 @@  int dump_sk_queue(int sock_fd, int sock_id, u64 (*get_sender)(const char *, int)
 	return ret;
 }
 
+/*
+ * Resolves senders and returns number of foreign senders, we should receive.
+ */
+int resolve_senders(unsigned int id, u32 ino, int (*add_receiver)(u32, struct sk_ino *, bool, void *), void *data)
+{
+	bool has_noname_sender = false, only_sender;
+	struct sk_ino *si, *tmp;
+	struct sk_packet *pkt;
+	int count = 0, ret;
+	u32 sender_ino;
+	LIST_HEAD(head);
+
+	list_for_each_entry(pkt, &packets_list, list) {
+		SkPacketEntry *entry = pkt->entry;
+		bool found = false;
+
+		if (entry->id_for != id)
+			continue;
+		if (entry->has_sender_ino)
+			sender_ino = entry->sender_ino;
+		else {
+			has_noname_sender = true;
+			continue;
+		}
+
+		list_for_each_entry(si, &head, list) {
+			if (si->ino == sender_ino) {
+				found = true;
+				break;
+			}
+		}
+
+		if (found)
+			continue;
+
+		si = shmalloc(sizeof(*si));
+		if (!si)
+			goto err;
+		si->ino = sender_ino;
+		list_add_tail(&si->list, &head);
+		count++;
+	}
+
+	if (count == 0) {
+		BUG_ON(!has_noname_sender);
+		return 0;
+	}
+
+	only_sender = !(count > 1 || (count && has_noname_sender));
+
+	list_for_each_entry_safe(si, tmp, &head, list) {
+		list_del(&si->list);
+
+		sender_ino = si->ino;
+		si->ino = ino; /* Reuse it as receiver */
+
+		ret = add_receiver(sender_ino, si, only_sender, data);
+		if (ret < 0)
+			goto err;
+		else if (ret == 1) {
+			BUG_ON(!only_sender);
+			shfree_last(si);
+			return 0;
+		}
+	}
+
+	return count;
+err:
+	pr_err("Resolving senders failed\n");
+	return -1;
+}
+
 int restore_sk_queue(int fd, unsigned int peer_id)
 {
 	struct sk_packet *pkt, *tmp;
diff --git a/criu/sk-unix.c b/criu/sk-unix.c
index 758eba6..7e717bc 100644
--- a/criu/sk-unix.c
+++ b/criu/sk-unix.c
@@ -821,6 +821,7 @@  int fix_external_unix_sockets(void)
 struct unix_sk_info {
 	UnixSkEntry *ue;
 	struct list_head list;
+	struct list_head receivers;
 	char *name;
 	char *name_dir;
 	unsigned flags;
@@ -840,6 +841,8 @@  struct unix_sk_info {
 	 * that should do the queueing.
 	 */
 	u32 queuer;
+
+	u32 nr_senders;
 };
 
 #define USK_PAIR_MASTER		0x1
@@ -1046,6 +1049,8 @@  static int open_unixsk_pair_master(struct unix_sk_info *ui)
 	struct unix_sk_info *peer = ui->peer;
 	struct fdinfo_list_entry *fle;
 
+	BUG_ON(ui->nr_senders);
+
 	pr_info("Opening pair master (id %#x ino %#x peer %#x)\n",
 			ui->ue->id, ui->ue->ino, ui->ue->peer);
 
@@ -1094,6 +1099,8 @@  static int open_unixsk_pair_slave(struct unix_sk_info *ui)
 	struct fdinfo_list_entry *fle;
 	int sk;
 
+	BUG_ON(ui->nr_senders);
+
 	fle = file_master(&ui->d);
 
 	pr_info("Opening pair slave (id %#x ino %#x peer %#x) on %d\n",
@@ -1336,7 +1343,7 @@  static int collect_one_unixsk(void *o, ProtobufCMessage *base, struct cr_img *i)
 	ui->ue = pb_msg(base, UnixSkEntry);
 	ui->name_dir = (void *)ui->ue->name_dir;
 
-	if (ui->ue->peer && !post_queued) {
+	if ((ui->ue->peer || !(ui->ue->uflags & USK_EMPTY_Q)) && !post_queued) {
 		post_queued = true;
 		if (add_post_prepare_cb(resolve_unix_peers, NULL))
 			return -1;
@@ -1356,6 +1363,8 @@  static int collect_one_unixsk(void *o, ProtobufCMessage *base, struct cr_img *i)
 
 	futex_init(&ui->prepared);
 	ui->queuer = 0;
+	INIT_LIST_HEAD(&ui->receivers);
+	ui->nr_senders = 0;
 	ui->peer = NULL;
 	ui->flags = 0;
 	pr_info(" `- Got %#x peer %#x (name %s dir %s)\n",
@@ -1394,11 +1403,49 @@  static void interconnected_pair(struct unix_sk_info *ui, struct unix_sk_info *pe
 	}
 }
 
+static int unix_add_receiver(u32 s_ino, struct sk_ino *ski, bool only_sender, void *r_ptr)
+{
+	struct unix_sk_info *s_ui, *r_ui;
+
+	s_ui = find_unix_sk_by_ino(s_ino);
+	if (!s_ui) {
+		pr_err("Can't find a sender: ino=%d\n", s_ino);
+		return -1;
+	}
+
+	if (s_ui->ue->peer != ski->ino || !only_sender) {
+		list_add(&ski->list, &s_ui->receivers);
+		pr_info("Add receiver %u to %u\n", ski->ino, s_ino);
+		return 0;
+	}
+
+	r_ui = r_ptr;
+	s_ui->peer = r_ui;
+	r_ui->queuer = s_ino;
+
+	if (s_ui->queuer == r_ui->ue->ino)
+		interconnected_pair(s_ui, r_ui);
+
+	return 1;
+}
+
 static int resolve_unix_peers(void *unused)
 {
 	struct unix_sk_info *ui, *peer;
 
 	list_for_each_entry(ui, &unix_sockets, list) {
+		if (ui->ue->type != SOCK_DGRAM ||
+		    (ui->ue->uflags & (USK_EMPTY_Q|USK_EXTERN)))
+			continue;
+
+		ui->nr_senders = resolve_senders(ui->ue->id, ui->ue->ino, unix_add_receiver, ui);
+		pr_info("Found a socket w/o queuer: ino=%d, senders=%d\n",
+			 ui->ue->ino, ui->nr_senders);
+		if (ui->nr_senders < 0)
+			return -1;
+	}
+
+	list_for_each_entry(ui, &unix_sockets, list) {
 		if (ui->peer)
 			continue;
 		if (!ui->ue->peer)
@@ -1413,15 +1460,19 @@  static int resolve_unix_peers(void *unused)
 		}
 
 		ui->peer = peer;
-		if (!peer->queuer)
+		if (!peer->queuer && !peer->nr_senders)
 			peer->queuer = ui->ue->ino;
 		if (ui == peer)
 			/* socket connected to self %) */
 			continue;
 		if (peer->ue->peer != ui->ue->ino)
 			continue;
+		if (!ui->queuer && !ui->nr_senders)
+			ui->queuer = peer->ue->ino;
 
 		peer->peer = ui;
+		if (!peer->queuer || !ui->queuer)
+			continue;
 
 		/* socketpair or interconnected sockets */
 		interconnected_pair(ui, peer);