mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: David Wang <00107082@163.com>
To: kadlec@blackhole.kfki.hu
Cc: 00107082@163.com, ale.crismani@automattic.com,
	linux-kernel@vger.kernel.org, netfilter-devel@vger.kernel.org,
	pablo@netfilter.org, xiaolinkui@kylinos.cn
Subject: Re:Performance regression in ip_set_swap on 6.7.0
Date: Thu, 11 Jan 2024 22:53:30 +0800	[thread overview]
Message-ID: <20240111145330.18474-1-00107082@163.com> (raw)
In-Reply-To: <b333bc85-83ea-8869-ccf7-374c9456d93c@blackhole.kfki.hu>

I tested the patch with code stressing swap->destroy->create->add 10000 times, the performance regression still happens, and now it is ip_set_destroy.
(I pasted the test code at the end of this mail)  
time show that most delay is 'off cpu':
	$ time sudo ./stressipset 

	real	2m45.115s
	user	0m0.019s
	sys	0m0.744s

Most time, callstack stuck in rcu_barrier:
	$ sudo cat /proc/2158/stack
	[<0>] rcu_barrier+0x1f6/0x2d0
	[<0>] ip_set_destroy+0x84/0x1d0 [ip_set]
	[<0>] nfnetlink_rcv_msg+0x2ac/0x2f0 [nfnetlink]
	[<0>] netlink_rcv_skb+0x57/0x100
	[<0>] netlink_unicast+0x19a/0x280
	[<0>] netlink_sendmsg+0x250/0x4d0
	[<0>] __sys_sendto+0x1be/0x1d0
	[<0>] __x64_sys_sendto+0x20/0x30
	[<0>] do_syscall_64+0x42/0xf0
	[<0>] entry_SYSCALL_64_after_hwframe+0x6e/0x76

perf_event_open profiling show similiar call signature for rcu_call and synchronize_rcu

	ip_set_destroy(49.651% 2133/4296)
	    rcu_barrier(80.684% 1721/2133)
		wait_for_completion(79.198% 1363/1721)
		    schedule_timeout(94.864% 1293/1363)
			schedule(96.520% 1248/1293)
			    __schedule(97.436% 1216/1248)
			    preempt_count_add(0.240% 3/1248)
			    srso_return_thunk(0.160% 2/1248)
			    preempt_count_sub(0.160% 2/1248)
			srso_return_thunk(0.077% 1/1293)
		    _raw_spin_unlock_irq(1.027% 14/1363)
		    _raw_spin_lock_irq(0.514% 7/1363)
		    __cond_resched(0.220% 3/1363)
		    srso_return_thunk(0.147% 2/1363)

	ip_set_swap(79.842% 709/888)  (this profiling was captured when synchronize_rcu is used in ip_set_swap)
	    synchronize_rcu(74.330% 527/709)
		__wait_rcu_gp(89.184% 470/527)
		    wait_for_completion(86.383% 406/470)
			schedule_timeout(91.133% 370/406)
			    schedule(95.135% 352/370)
			_raw_spin_unlock_irq(3.202% 13/406)
			_raw_spin_lock_irq(0.739% 3/406)
			srso_return_thunk(0.246% 1/406)
		    _raw_spin_unlock_irq(7.021% 33/470)
		    __call_rcu_common.constprop.0(3.830% 18/470)
		rcu_gp_is_expedited(3.036% 16/527)
		__cond_resched(0.569% 3/527)
		srso_return_thunk(0.190% 1/527)

They all call wait_for_completion, which may sleep on something on purpose, I guess...(Maybe rcu is not a good choice here?)


I made another test with 'synchronize_rcu' removed from ip_set_swap and no 'call_rcu' in ip_set_destroy, the performance is much better:
	$ time sudo ./stressipset 

	real	0m2.203s
	user	0m0.037s
	sys	0m0.188s


Here is my test code, it is very ugly...(I have no knowledge of netfilter messaging protocol, and just hack it out with the help of strace.)
And I was lazy, before run the test, foo/bar netset is needed:

	ipset create foo hash:net
	ipset create bar hash:net

Here come the ugly code, should be able to compile with gcc.

	#include <stdio.h>
	#include <stdlib.h>
	#include <sys/types.h>
	#include <sys/socket.h>
	#include <unistd.h>
	#include <arpa/inet.h>
	#include <linux/netlink.h>
	#include <linux/netfilter/nfnetlink.h>
	#include <linux/netfilter/ipset/ip_set.h>
	#include <string.h>

	unsigned char mbuffer[4096];
	unsigned char buf[128]; // cmd buffer  for swap
	unsigned char dbuf[128]; // cmd buffer for destroy
	unsigned char cbuf[128]; // cmd buffer for create
	unsigned char abuf[128]; // cmd buffer for add
	int main() {
		int err;
		int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
		if (sock<0) {
			perror("Fail to create socket");
			return 1;
		}
		struct sockaddr_nl addr = {
			.nl_family = AF_NETLINK,
			.nl_pad = 0,
			.nl_pid = 0,
			.nl_groups = 0
		};
		struct sockaddr raddr = {0};
		socklen_t rsize;
		int seq = 0x12345678;
		err = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
		if (err) {
			perror("Fail to bind");
			return 1;
		}
		err = getsockname(sock, &raddr, &rsize);
		if (err) {
			perror("Fail to getsockname");
			return 1;
		}
		struct nlmsghdr *phdr;
		struct nfgenmsg *pnfg;
		struct nlattr *pnla;
		unsigned int total;
		ssize_t rz;
		struct iovec iovs;
		iovs.iov_base = mbuffer;
		iovs.iov_len = sizeof(mbuffer);
		struct msghdr msg = {0};
		msg.msg_name = &addr;
		msg.msg_namelen = sizeof(addr);
		msg.msg_iov = &iovs;
		msg.msg_iovlen = 1;

		memset(buf, 0, sizeof(buf));
		total = 0;
		phdr = (struct nlmsghdr*)(buf+total);
		total += sizeof(struct nlmsghdr);
		phdr->nlmsg_type=NFNL_SUBSYS_IPSET<<8|IPSET_CMD_PROTOCOL;
		phdr->nlmsg_seq = seq;
		phdr->nlmsg_flags = NLM_F_REQUEST;
		pnfg = (struct nfgenmsg*)(buf+total);
		total += sizeof(struct nfgenmsg);
		pnfg->nfgen_family=AF_INET;
		pnfg->version= NFNETLINK_V0;
		pnfg->res_id=htons(0);
		pnla = (struct nlattr *)(buf+total);
		pnla->nla_len = 5;
		pnla->nla_type = 1;
		buf[total+sizeof(struct nlattr)]=0x06;
		total+=8;
		phdr->nlmsg_len = total;
		rz = sendto(sock, buf, total, 0, (struct sockaddr*)&addr, sizeof(addr));
		rz = recvmsg(sock, &msg, 0);

		int sz = total;
		// build swap
		pnla = (struct nlattr *)(buf+total);
		pnla->nla_len = 8;
		pnla->nla_type = 2;
		char *p = buf+(total+sizeof(struct nlattr));
		p[0]='f'; p[1]='o'; p[2]='o'; p[3]=0;
		total+=8;
		pnla = (struct nlattr *)(buf+total);
		pnla->nla_len = 8;
		pnla->nla_type = 3;
		p = buf+(total+sizeof(struct nlattr));
		p[0]='b'; p[1]='a'; p[2]='r'; p[3]=0;
		total+=8;
		phdr->nlmsg_type = NFNL_SUBSYS_IPSET<<8|IPSET_CMD_SWAP;
		phdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
		phdr->nlmsg_len = total;

		// build destroy
		char bar[] = "\x62\x61\x72\x00";
		memcpy(dbuf, buf, sz);
		total = sz;
		struct nlmsghdr *phdr_d = (struct nlmsghdr*)dbuf;
		pnla = (struct nlattr *)(dbuf+total);
		pnla->nla_len = 8;
		pnla->nla_type = 2;
		memcpy(dbuf+(total+sizeof(struct nlattr)), bar, sizeof(bar));
		total+=pnla->nla_len;
		phdr_d->nlmsg_type = NFNL_SUBSYS_IPSET<<8|IPSET_CMD_DESTROY;
		phdr_d->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
		phdr_d->nlmsg_len = total;

		// build create
		memcpy(cbuf, buf, sz);
		total = sz;
		struct nlmsghdr *phdr_c = (struct nlmsghdr*)cbuf;
		pnla = (struct nlattr *)(cbuf+total);
		pnla->nla_len = 8;
		pnla->nla_type = 2;
		memcpy(cbuf+(total+sizeof(struct nlattr)), bar, sizeof(bar));
		total+=(pnla->nla_len+3)/4*4;
		char hashnet[] = "\x68\x61\x73\x68\x3a\x6e\x65\x74\x00";
		pnla = (struct nlattr *)(cbuf+total);
		pnla->nla_len = 13;
		pnla->nla_type = 3;
		memcpy(cbuf+(total+sizeof(struct nlattr)), hashnet, sizeof(hashnet));
		total+=(pnla->nla_len+3)/4*4;
		pnla = (struct nlattr *)(cbuf+total);
		pnla->nla_len = 5;
		pnla->nla_type = 4;
		cbuf[total+sizeof(struct nlattr)]=6;
		total+=(pnla->nla_len+3)/4*4;
		pnla = (struct nlattr *)(cbuf+total);
		pnla->nla_len = 5;
		pnla->nla_type = 5;
		cbuf[total+sizeof(struct nlattr)]=2;
		total+=(pnla->nla_len+3)/4*4;
		pnla = (struct nlattr *)(cbuf+total);
		pnla->nla_len = 4;
		pnla->nla_type = NLA_F_NESTED|0x7;
		total+=(pnla->nla_len+3)/4*4;

		phdr_c->nlmsg_type = NFNL_SUBSYS_IPSET<<8|IPSET_CMD_CREATE;
		phdr_c->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
		phdr_c->nlmsg_len = total;

		// build add 
		memcpy(abuf, buf, sz);
		total = sz;
		struct nlmsghdr *phdr_a = (struct nlmsghdr*)abuf;
		pnla = (struct nlattr *)(abuf+total);
		pnla->nla_len = 8;
		pnla->nla_type = 2;
		memcpy(abuf+(total+sizeof(struct nlattr)), bar, sizeof(bar));
		total+=(pnla->nla_len+3)/4*4;
		char ip[] = "\x0c\x00\x01\x80\x08\x00\x01\x40\x0a\x01\x00\x00\x05\x00\x03\x00\x10\x00\x00\x00\x08\x00\x09\x40\x00\x00\x00\x00";
		pnla = (struct nlattr *)(abuf+total);
		pnla->nla_len = 32;
		pnla->nla_type = NLA_F_NESTED|0x7;
		memcpy(abuf+(total+sizeof(struct nlattr)), ip, sizeof(ip));
		total+=(pnla->nla_len+3)/4*4;
		phdr_a->nlmsg_type = NFNL_SUBSYS_IPSET<<8|IPSET_CMD_ADD;
		phdr_a->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
		phdr_a->nlmsg_len = total;

		
		for (int i=0; i<10000; i++) {
			// swap foo bar
			seq++;
			phdr->nlmsg_seq = seq;
			sendto(sock, buf, phdr->nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr));
			recvmsg(sock, &msg, 0);
			err=*(short*)(mbuffer+(sizeof(struct nlmsghdr)-2)); if (err) { printf("fail to swap %d\n", err); break; }
			// destroy bar
			seq++;
			phdr_d->nlmsg_seq = seq;
			sendto(sock, dbuf, phdr_d->nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr));
			recvmsg(sock, &msg, 0);
			err=*(short*)(mbuffer+(sizeof(struct nlmsghdr)-2)); if (err) { printf("fail to destroy\n"); break; }
			// create bar
			seq++;
			phdr_c->nlmsg_seq = seq;
			sendto(sock, cbuf, phdr_c->nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr));
			recvmsg(sock, &msg, 0);
			err=*(short*)(mbuffer+(sizeof(struct nlmsghdr)-2)); if (err) { printf("fail to create\n"); break; }
			// add bar
			seq++;
			phdr_a->nlmsg_seq = seq;
			sendto(sock, abuf, phdr_a->nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr));
			recvmsg(sock, &msg, 0);
			err=*(short*)(mbuffer+(sizeof(struct nlmsghdr)-2)); if (err) { printf("fail to add\n"); break; }
		}

		close(sock);
		return 0;
	}


Thanks
David


  reply	other threads:[~2024-01-11 14:54 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-05 22:45 Performance regression in ip_set_swap on 6.1.69 Ale Crismani
2024-01-10 10:23 ` David Wang
2024-01-10 10:35   ` Jozsef Kadlecsik
2024-01-10 11:05     ` David Wang
2024-01-10 11:30       ` David Wang
2024-01-10 16:14       ` Jozsef Kadlecsik
2024-01-11  8:25         ` Jozsef Kadlecsik
2024-01-11 11:11           ` David Wang
2024-01-11 11:22             ` Jozsef Kadlecsik
2024-01-11 14:53               ` David Wang [this message]
2024-01-13 18:24                 ` Re:Performance regression in ip_set_swap on 6.7.0 Jozsef Kadlecsik
2024-01-14  5:30                   ` David Wang
2024-01-14 20:38                     ` Performance " Ale Crismani
2024-01-15 13:29                       ` Ale Crismani
2024-01-16  7:57                         ` Jozsef Kadlecsik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240111145330.18474-1-00107082@163.com \
    --to=00107082@163.com \
    --cc=ale.crismani@automattic.com \
    --cc=kadlec@blackhole.kfki.hu \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pablo@netfilter.org \
    --cc=xiaolinkui@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®