* Performance regression in ip_set_swap on 6.1.69
@ 2024-01-05 22:45 Ale Crismani
2024-01-10 10:23 ` David Wang
0 siblings, 1 reply; 15+ messages in thread
From: Ale Crismani @ 2024-01-05 22:45 UTC (permalink / raw)
To: linux-kernel
Dear all,
When upgrading some of our Debian hosts that compose a Kubernetes cluster we found a regression in ip_set_swap on 6.1.69. Calls to ip_set_swap now take roughly 15ms, while they used to take just tens of microseconds before.
The issue is very visible for use, since we use kube-router as our Kubernetes networking interface, and it uses ipset swap all the time to populate sets that enforce firewall policies between containers.
We tracked the issue down with strace, and then took stats with bpftrace running:
---
kfunc:ip_set:ip_set_swap {
@start[tid] = nsecs;
}
kretfunc:ip_set:ip_set_swap {
if (@start[tid]) {
@srlat = hist((nsecs - @start[tid])/1000);
delete(@start[tid]);
}
}
interval:s:20 {
printf("ip_set_swap() latency, milliseconds:\n");
---
On 6.1.69 results look like:
ip_set_swap() latency, milliseconds:
[8K, 16K) 1848 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[16K, 32K) 1017 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[32K, 64K) 38 |@
while on 6.1.67:
ip_set_swap() latency, milliseconds:
[0] 166 |@
[1] 378 |@@
[2, 4) 762 |@@@@@
[4, 8) 1624 |@@@@@@@@@@@
[8, 16) 3493 |@@@@@@@@@@@@@@@@@@@@@@@@
[16, 32) 7308 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[32, 64) 6412 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[64, 128) 1 |
We tried compiling commits between 6.1.67 and 6.1.69 and it seems the performance regression was introduced by 875ee3a, ip_set_swap is fast on 602505 that precedes it, and slow on it.
First time I post here, hope the format is appropriate, and thanks for any help with this! Also, if possible, I'd appreciate if any reply could CC me, as I am not subscribed.
Alessandro Crismani
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: Performance regression in ip_set_swap on 6.1.69 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 0 siblings, 1 reply; 15+ messages in thread From: David Wang @ 2024-01-10 10:23 UTC (permalink / raw) To: ale.crismani, kadlec, xiaolinkui, pablo; +Cc: linux-kernel, netfilter-devel I confirmed this on 6.7 that this was introduced by commit 28628fa952fefc7f2072ce6e8016968cc452b1ba with following changes: static inline void @@ -1397,6 +1394,9 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, ip_set(inst, to_id) = from; write_unlock_bh(&ip_set_ref_lock); + /* Make sure all readers of the old set pointers are completed. */ + synchronize_rcu(); + return 0; } synchronize_rcu causes the delay, and its usage here is very confusing, there is no reclaimer code after it. FYI David ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.1.69 2024-01-10 10:23 ` David Wang @ 2024-01-10 10:35 ` Jozsef Kadlecsik 2024-01-10 11:05 ` David Wang 0 siblings, 1 reply; 15+ messages in thread From: Jozsef Kadlecsik @ 2024-01-10 10:35 UTC (permalink / raw) To: David Wang; +Cc: ale.crismani, xiaolinkui, pablo, linux-kernel, netfilter-devel On Wed, 10 Jan 2024, David Wang wrote: > I confirmed this on 6.7 that this was introduced by commit > 28628fa952fefc7f2072ce6e8016968cc452b1ba with following changes: > > static inline void > @@ -1397,6 +1394,9 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, > ip_set(inst, to_id) = from; > write_unlock_bh(&ip_set_ref_lock); > > + /* Make sure all readers of the old set pointers are completed. */ > + synchronize_rcu(); > + > return 0; > } > > synchronize_rcu causes the delay, and its usage here is very confusing, > there is no reclaimer code after it. As I'm seeing just the end of the discussion, please send a full report of the problem and how to reproduce it. Best regards, Jozsef -- E-mail : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.hu PGP key : https://wigner.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics H-1525 Budapest 114, POB. 49, Hungary ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.1.69 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 0 siblings, 2 replies; 15+ messages in thread From: David Wang @ 2024-01-10 11:05 UTC (permalink / raw) To: Jozsef Kadlecsik Cc: ale.crismani, xiaolinkui, pablo, linux-kernel, netfilter-devel At 2024-01-10 18:35:02, "Jozsef Kadlecsik" <kadlec@netfilter.org> wrote: >On Wed, 10 Jan 2024, David Wang wrote: > >> I confirmed this on 6.7 that this was introduced by commit >> 28628fa952fefc7f2072ce6e8016968cc452b1ba with following changes: >> >> static inline void >> @@ -1397,6 +1394,9 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, >> ip_set(inst, to_id) = from; >> write_unlock_bh(&ip_set_ref_lock); >> >> + /* Make sure all readers of the old set pointers are completed. */ >> + synchronize_rcu(); >> + >> return 0; >> } >> >> synchronize_rcu causes the delay, and its usage here is very confusing, >> there is no reclaimer code after it. > >As I'm seeing just the end of the discussion, please send a full report of >the problem and how to reproduce it. > This was reported in https://lore.kernel.org/lkml/C0829B10-EAA6-4809-874E-E1E9C05A8D84@automattic.com/ by ale.crismani@automattic.com Just out of interest of performance issues, I tried to reproduce it with a test stressing ipset_swap: My test code is as following, it would stress swapping ipset 'foo' with 'bar'; (foo/bar ipset needs to be created before the test.) With latest 6.7, the stress would take about 180 seconds to finish, but with `synchronize_rcu` removed, it only took 3seconds. ``` unsigned char mbuffer[4096]; 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; } unsigned char buf[64]; 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); 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; for (int i=0; i<10000; i++) { // stress swap foo bar phdr->nlmsg_seq++; sendto(sock, buf, total, 0, (struct sockaddr*)&addr, sizeof(addr)); recvmsg(sock, &msg, 0); } close(sock); return 0; } ``` >Best regards, >Jozsef David ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.1.69 2024-01-10 11:05 ` David Wang @ 2024-01-10 11:30 ` David Wang 2024-01-10 16:14 ` Jozsef Kadlecsik 1 sibling, 0 replies; 15+ messages in thread From: David Wang @ 2024-01-10 11:30 UTC (permalink / raw) To: Jozsef Kadlecsik Cc: ale.crismani, xiaolinkui, pablo, linux-kernel, netfilter-devel At 2024-01-10 19:05:18, "David Wang" <00107082@163.com> wrote: > > >My test code is as following, it would stress swapping ipset 'foo' with 'bar'; (foo/bar ipset needs to be created before the test.) >With latest 6.7, the stress would take about 180 seconds to finish, but with `synchronize_rcu` removed, it only took 3seconds. Correction, with 'synchronize_rcu' removed, it only took about 0.03 seconds > > >David ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.1.69 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 1 sibling, 1 reply; 15+ messages in thread From: Jozsef Kadlecsik @ 2024-01-10 16:14 UTC (permalink / raw) To: David Wang; +Cc: ale.crismani, xiaolinkui, pablo, linux-kernel, netfilter-devel Hi, On Wed, 10 Jan 2024, David Wang wrote: > At 2024-01-10 18:35:02, "Jozsef Kadlecsik" <kadlec@netfilter.org> wrote: > >On Wed, 10 Jan 2024, David Wang wrote: > > > >> I confirmed this on 6.7 that this was introduced by commit > >> 28628fa952fefc7f2072ce6e8016968cc452b1ba with following changes: > >> > >> static inline void > >> @@ -1397,6 +1394,9 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, > >> ip_set(inst, to_id) = from; > >> write_unlock_bh(&ip_set_ref_lock); > >> > >> + /* Make sure all readers of the old set pointers are completed. */ > >> + synchronize_rcu(); > >> + > >> return 0; > >> } > >> > >> synchronize_rcu causes the delay, and its usage here is very confusing, > >> there is no reclaimer code after it. > > > >As I'm seeing just the end of the discussion, please send a full report of > >the problem and how to reproduce it. > > > > This was reported in > https://lore.kernel.org/lkml/C0829B10-EAA6-4809-874E-E1E9C05A8D84@automattic.com/ > by ale.crismani@automattic.com Just out of interest of performance > issues, I tried to reproduce it with a test stressing ipset_swap: > > My test code is as following, it would stress swapping ipset 'foo' with > 'bar'; (foo/bar ipset needs to be created before the test.) With latest > 6.7, the stress would take about 180 seconds to finish, but with > `synchronize_rcu` removed, it only took 3seconds. > > > ``` > unsigned char mbuffer[4096]; > 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; > } > unsigned char buf[64]; > 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); > > 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; > > > for (int i=0; i<10000; i++) { > // stress swap foo bar > phdr->nlmsg_seq++; > sendto(sock, buf, total, 0, (struct sockaddr*)&addr, sizeof(addr)); > recvmsg(sock, &msg, 0); > } > > close(sock); > return 0; > } > ``` Thanks, I'll look into it. The race condition fix between swap/destroy and kernel side add/del/test had several versions, either penalizing destroy or swap. Finally swap seemed to be the less intrusive. I'm going to explore other possibilities. Best regards, Jozsef -- E-mail : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.hu PGP key : https://wigner.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics H-1525 Budapest 114, POB. 49, Hungary ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.1.69 2024-01-10 16:14 ` Jozsef Kadlecsik @ 2024-01-11 8:25 ` Jozsef Kadlecsik 2024-01-11 11:11 ` David Wang 0 siblings, 1 reply; 15+ messages in thread From: Jozsef Kadlecsik @ 2024-01-11 8:25 UTC (permalink / raw) To: David Wang Cc: ale.crismani, xiaolinkui, Pablo Neira Ayuso, linux-kernel, netfilter-devel Hi, On Wed, 10 Jan 2024, Jozsef Kadlecsik wrote: > On Wed, 10 Jan 2024, David Wang wrote: > > > At 2024-01-10 18:35:02, "Jozsef Kadlecsik" <kadlec@netfilter.org> wrote: > > >On Wed, 10 Jan 2024, David Wang wrote: > > > > > >> I confirmed this on 6.7 that this was introduced by commit > > >> 28628fa952fefc7f2072ce6e8016968cc452b1ba with following changes: > > >> > > >> static inline void > > >> @@ -1397,6 +1394,9 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, > > >> ip_set(inst, to_id) = from; > > >> write_unlock_bh(&ip_set_ref_lock); > > >> > > >> + /* Make sure all readers of the old set pointers are completed. */ > > >> + synchronize_rcu(); > > >> + > > >> return 0; > > >> } > > >> > > >> synchronize_rcu causes the delay, and its usage here is very confusing, > > >> there is no reclaimer code after it. > > > > > >As I'm seeing just the end of the discussion, please send a full report of > > >the problem and how to reproduce it. > > > > > > > This was reported in > > https://lore.kernel.org/lkml/C0829B10-EAA6-4809-874E-E1E9C05A8D84@automattic.com/ > > by ale.crismani@automattic.com Just out of interest of performance > > issues, I tried to reproduce it with a test stressing ipset_swap: > > > > My test code is as following, it would stress swapping ipset 'foo' with > > 'bar'; (foo/bar ipset needs to be created before the test.) With latest > > 6.7, the stress would take about 180 seconds to finish, but with > > `synchronize_rcu` removed, it only took 3seconds. > > > > > > ``` > > unsigned char mbuffer[4096]; > > 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; > > } > > unsigned char buf[64]; > > 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); > > > > 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; > > > > > > for (int i=0; i<10000; i++) { > > // stress swap foo bar > > phdr->nlmsg_seq++; > > sendto(sock, buf, total, 0, (struct sockaddr*)&addr, sizeof(addr)); > > recvmsg(sock, &msg, 0); > > } > > > > close(sock); > > return 0; > > } > > ``` > > Thanks, I'll look into it. The race condition fix between swap/destroy and > kernel side add/del/test had several versions, either penalizing destroy > or swap. Finally swap seemed to be the less intrusive. I'm going to > explore other possibilities. Could you check that the patch below fixes the performance regression? Instead of waiting for the RCU grace period at swapping, call_rcu() is used at destroying the set. diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e8c350a3ade1..912f750d0bea 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -242,6 +242,8 @@ extern void ip_set_type_unregister(struct ip_set_type *set_type); /* A generic IP set */ struct ip_set { + /* For call_cru in destroy */ + struct rcu_head rcu; /* The name of the set */ char name[IPSET_MAXNAMELEN]; /* Lock protecting the set data */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 4c133e06be1d..cd95f75dd720 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1009,6 +1009,14 @@ find_set(struct ip_set_net *inst, const char *name) return find_set_and_id(inst, name, &id); } +static void +ip_set_destroy_set_rcu(struct rcu_head *head) +{ + struct ip_set *set = container_of(head, struct ip_set, rcu); + + ip_set_destroy_set(set); +} + static int find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index, struct ip_set **set) @@ -1241,7 +1249,7 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, ip_set(inst, i) = NULL; read_unlock_bh(&ip_set_ref_lock); - ip_set_destroy_set(s); + call_rcu(&s->rcu, ip_set_destroy_set_rcu); } return 0; out: @@ -1394,9 +1402,6 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, ip_set(inst, to_id) = from; write_unlock_bh(&ip_set_ref_lock); - /* Make sure all readers of the old set pointers are completed. */ - synchronize_rcu(); - return 0; } Best regards, Jozsef -- E-mail : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.hu PGP key : https://wigner.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics H-1525 Budapest 114, POB. 49, Hungary ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re:Re: Performance regression in ip_set_swap on 6.1.69 2024-01-11 8:25 ` Jozsef Kadlecsik @ 2024-01-11 11:11 ` David Wang 2024-01-11 11:22 ` Jozsef Kadlecsik 0 siblings, 1 reply; 15+ messages in thread From: David Wang @ 2024-01-11 11:11 UTC (permalink / raw) To: Jozsef Kadlecsik Cc: ale.crismani, xiaolinkui, Pablo Neira Ayuso, linux-kernel, netfilter-devel At 2024-01-11 16:25:46, "Jozsef Kadlecsik" <kadlec@blackhole.kfki.hu> wrote: >Hi, > > >Could you check that the patch below fixes the performance regression? >Instead of waiting for the RCU grace period at swapping, call_rcu() is >used at destroying the set. Got a compiler error: net/netfilter/ipset/ip_set_core.c: In function ‘ip_set_destroy_set_rcu’: net/netfilter/ipset/ip_set_core.c:1017:9: error: implicit declaration of function ‘ip_set_destroy_set’; did you mean ‘ip_set_destroy_set_rcu’? [-Werror=implicit-function-declaration] 1017 | ip_set_destroy_set(set); | ^~~~~~~~~~~~~~~~~~ | ip_set_destroy_set_rcu net/netfilter/ipset/ip_set_core.c: At top level: net/netfilter/ipset/ip_set_core.c:1183:1: warning: conflicting types for ‘ip_set_destroy_set’; have ‘void(struct ip_set *)’ 1183 | ip_set_destroy_set(struct ip_set *set) | ^~~~~~~~~~~~~~~~~~ net/netfilter/ipset/ip_set_core.c:1183:1: error: static declaration of ‘ip_set_destroy_set’ follows non-static declaration I move the declaration of ip_set_destroy_set_rcu, make sure it is after the declaration of ip_set_destroy_set, With this path, the performance degradation of ipset_swap is gone, but my test only stress ipset_swap, not a swap/destroy sequence. I will adjust my code to stress a full swap/destroy/create/add sequence, and update later. Thanks David ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re:Re: Performance regression in ip_set_swap on 6.1.69 2024-01-11 11:11 ` David Wang @ 2024-01-11 11:22 ` Jozsef Kadlecsik 2024-01-11 14:53 ` Re:Performance regression in ip_set_swap on 6.7.0 David Wang 0 siblings, 1 reply; 15+ messages in thread From: Jozsef Kadlecsik @ 2024-01-11 11:22 UTC (permalink / raw) To: David Wang Cc: ale.crismani, xiaolinkui, Pablo Neira Ayuso, linux-kernel, netfilter-devel [-- Attachment #1: Type: text/plain, Size: 2745 bytes --] Hi, On Thu, 11 Jan 2024, David Wang wrote: > At 2024-01-11 16:25:46, "Jozsef Kadlecsik" <kadlec@blackhole.kfki.hu> wrote: > > > > > >Could you check that the patch below fixes the performance regression? > >Instead of waiting for the RCU grace period at swapping, call_rcu() is > >used at destroying the set. > > Got a compiler error: > net/netfilter/ipset/ip_set_core.c: In function ‘ip_set_destroy_set_rcu’: > net/netfilter/ipset/ip_set_core.c:1017:9: error: implicit declaration of function ‘ip_set_destroy_set’; did you mean ‘ip_set_destroy_set_rcu’? [-Werror=implicit-function-declaration] > 1017 | ip_set_destroy_set(set); > | ^~~~~~~~~~~~~~~~~~ > | ip_set_destroy_set_rcu > net/netfilter/ipset/ip_set_core.c: At top level: > net/netfilter/ipset/ip_set_core.c:1183:1: warning: conflicting types for ‘ip_set_destroy_set’; have ‘void(struct ip_set *)’ > 1183 | ip_set_destroy_set(struct ip_set *set) > | ^~~~~~~~~~~~~~~~~~ > net/netfilter/ipset/ip_set_core.c:1183:1: error: static declaration of ‘ip_set_destroy_set’ follows non-static declaration > > I move the declaration of ip_set_destroy_set_rcu, make sure it is after > the declaration of ip_set_destroy_set, With this path, the performance > degradation of ipset_swap is gone, Thanks! I wrote the patch for the ipset package code and while it applies to the vanilla kernel source, function definiton ordering got broken. Sorry, I should have to double check. > but my test only stress ipset_swap, not a swap/destroy sequence. I will > adjust my code to stress a full swap/destroy/create/add sequence, and > update later. The synchronize_rcu() in ip_set_swap() was added to exclude the parallel swap+destroy and kernel side add/del/test operations, which can be tested with the commands ipset create hash_ip1 hash:net family inet hashsize 1024 maxelem 1048576 ipset add hash_ip1 172.20.0.0/16 ipset add hash_ip1 192.168.0.0/16 iptables -A INPUT -m set --match-set hash_ip1 src -j ACCEPT while [ 1 ] do # ... Ongoing traffic... ipset create hash_ip2 hash:net family inet hashsize 1024 maxelem 1048576 ipset add hash_ip2 172.20.0.0/16 ipset swap hash_ip1 hash_ip2 ipset destroy hash_ip2 sleep 0.05 done I have checked it but the virtual machine on my dev laptop might be too slow to trigger the case. Best regards, Jozsef -- E-mail : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.hu PGP key : https://wigner.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics H-1525 Budapest 114, POB. 49, Hungary ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re:Performance regression in ip_set_swap on 6.7.0 2024-01-11 11:22 ` Jozsef Kadlecsik @ 2024-01-11 14:53 ` David Wang 2024-01-13 18:24 ` Jozsef Kadlecsik 0 siblings, 1 reply; 15+ messages in thread From: David Wang @ 2024-01-11 14:53 UTC (permalink / raw) To: kadlec Cc: 00107082, ale.crismani, linux-kernel, netfilter-devel, pablo, xiaolinkui 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 ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re:Performance regression in ip_set_swap on 6.7.0 2024-01-11 14:53 ` Re:Performance regression in ip_set_swap on 6.7.0 David Wang @ 2024-01-13 18:24 ` Jozsef Kadlecsik 2024-01-14 5:30 ` David Wang 0 siblings, 1 reply; 15+ messages in thread From: Jozsef Kadlecsik @ 2024-01-13 18:24 UTC (permalink / raw) To: David Wang Cc: ale.crismani, linux-kernel, netfilter-devel, Pablo Neira Ayuso, xiaolinkui On Thu, 11 Jan 2024, David Wang wrote: > 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... That's OK because ip_set_destroy() calls rcu_barrier() which is needed to handle flush in list type of sets. However, rcu_barrier() with call_rcu() together makes multiple destroys one after another slow. But rcu_barrier() is needed for list type of sets only and that can be handled separately. So could you test the patch below? According to my tests it is even a little bit faster than the original code before synchronize_rcu() was added to swap. diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index e8c350a3ade1..912f750d0bea 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -242,6 +242,8 @@ extern void ip_set_type_unregister(struct ip_set_type *set_type); /* A generic IP set */ struct ip_set { + /* For call_cru in destroy */ + struct rcu_head rcu; /* The name of the set */ char name[IPSET_MAXNAMELEN]; /* Lock protecting the set data */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 4c133e06be1d..3bf9bb345809 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1182,6 +1182,14 @@ ip_set_destroy_set(struct ip_set *set) kfree(set); } +static void +ip_set_destroy_set_rcu(struct rcu_head *head) +{ + struct ip_set *set = container_of(head, struct ip_set, rcu); + + ip_set_destroy_set(set); +} + static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const attr[]) { @@ -1193,8 +1201,6 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, if (unlikely(protocol_min_failed(attr))) return -IPSET_ERR_PROTOCOL; - /* Must wait for flush to be really finished in list:set */ - rcu_barrier(); /* Commands are serialized and references are * protected by the ip_set_ref_lock. @@ -1206,8 +1212,10 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, * counter, so if it's already zero, we can proceed * without holding the lock. */ - read_lock_bh(&ip_set_ref_lock); if (!attr[IPSET_ATTR_SETNAME]) { + /* Must wait for flush to be really finished in list:set */ + rcu_barrier(); + read_lock_bh(&ip_set_ref_lock); for (i = 0; i < inst->ip_set_max; i++) { s = ip_set(inst, i); if (s && (s->ref || s->ref_netlink)) { @@ -1228,6 +1236,9 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, inst->is_destroyed = false; } else { u32 flags = flag_exist(info->nlh); + u16 features = 0; + + read_lock_bh(&ip_set_ref_lock); s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), &i); if (!s) { @@ -1238,10 +1249,14 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, ret = -IPSET_ERR_BUSY; goto out; } + features = s->type->features; ip_set(inst, i) = NULL; read_unlock_bh(&ip_set_ref_lock); - - ip_set_destroy_set(s); + if (features & IPSET_TYPE_NAME) { + /* Must wait for flush to be really finished */ + rcu_barrier(); + } + call_rcu(&s->rcu, ip_set_destroy_set_rcu); } return 0; out: @@ -1394,9 +1409,6 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, ip_set(inst, to_id) = from; write_unlock_bh(&ip_set_ref_lock); - /* Make sure all readers of the old set pointers are completed. */ - synchronize_rcu(); - return 0; } @@ -2357,6 +2369,9 @@ ip_set_net_exit(struct net *net) inst->is_deleted = true; /* flag for ip_set_nfnl_put */ + /* Wait for call_rcu() in destroy */ + rcu_barrier(); + nfnl_lock(NFNL_SUBSYS_IPSET); for (i = 0; i < inst->ip_set_max; i++) { set = ip_set(inst, i); Best regards, Jozsef -- E-mail : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.hu PGP key : https://wigner.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics H-1525 Budapest 114, POB. 49, Hungary ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re:Performance regression in ip_set_swap on 6.7.0 2024-01-13 18:24 ` Jozsef Kadlecsik @ 2024-01-14 5:30 ` David Wang 2024-01-14 20:38 ` Performance " Ale Crismani 0 siblings, 1 reply; 15+ messages in thread From: David Wang @ 2024-01-14 5:30 UTC (permalink / raw) To: Jozsef Kadlecsik, ale.crismani Cc: linux-kernel, netfilter-devel, Pablo Neira Ayuso, xiaolinkui At 2024-01-14 02:24:07, "Jozsef Kadlecsik" <kadlec@blackhole.kfki.hu> wrote: >On Thu, 11 Jan 2024, David Wang wrote: > >> 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) >> >> They all call wait_for_completion, which may sleep on something on >> purpose, I guess... > >That's OK because ip_set_destroy() calls rcu_barrier() which is needed to >handle flush in list type of sets. > >However, rcu_barrier() with call_rcu() together makes multiple destroys >one after another slow. But rcu_barrier() is needed for list type of sets >only and that can be handled separately. So could you test the patch >below? According to my tests it is even a little bit faster than the >original code before synchronize_rcu() was added to swap. Confirmed~! This patch does fix the performance regression in my case. Hope it can fix ale.crismani@automattic.com's original issue. Thanks~ David ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.7.0 2024-01-14 5:30 ` David Wang @ 2024-01-14 20:38 ` Ale Crismani 2024-01-15 13:29 ` Ale Crismani 0 siblings, 1 reply; 15+ messages in thread From: Ale Crismani @ 2024-01-14 20:38 UTC (permalink / raw) To: David Wang Cc: Jozsef Kadlecsik, linux-kernel, netfilter-devel, Pablo Neira Ayuso, xiaolinkui > Il giorno 14 gen 2024, alle ore 06:30, David Wang <00107082@163.com> ha scritto: > > > At 2024-01-14 02:24:07, "Jozsef Kadlecsik" <kadlec@blackhole.kfki.hu> wrote: >> On Thu, 11 Jan 2024, David Wang wrote: >> >>> 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) > >>> >>> They all call wait_for_completion, which may sleep on something on >>> purpose, I guess... >> >> That's OK because ip_set_destroy() calls rcu_barrier() which is needed to >> handle flush in list type of sets. >> >> However, rcu_barrier() with call_rcu() together makes multiple destroys >> one after another slow. But rcu_barrier() is needed for list type of sets >> only and that can be handled separately. So could you test the patch >> below? According to my tests it is even a little bit faster than the >> original code before synchronize_rcu() was added to swap. > > Confirmed~! This patch does fix the performance regression in my case. > > Hope it can fix ale.crismani@automattic.com's original issue. > > > > Thanks~ > David Thanks for all the help on this, I'll try the patch tomorrow hopefully and will report back! best wishes, Ale ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.7.0 2024-01-14 20:38 ` Performance " Ale Crismani @ 2024-01-15 13:29 ` Ale Crismani 2024-01-16 7:57 ` Jozsef Kadlecsik 0 siblings, 1 reply; 15+ messages in thread From: Ale Crismani @ 2024-01-15 13:29 UTC (permalink / raw) To: Wang David Cc: Kadlecsik Jozsef, linux-kernel, netfilter-devel, Ayuso Pablo Neira, xiaolinkui > Il giorno 14 gen 2024, alle ore 21:38, Ale Crismani <ale.crismani@automattic.com> ha scritto: > > > >> Il giorno 14 gen 2024, alle ore 06:30, David Wang <00107082@163.com> ha scritto: >> >> >> At 2024-01-14 02:24:07, "Jozsef Kadlecsik" <kadlec@blackhole.kfki.hu> wrote: >>> On Thu, 11 Jan 2024, David Wang wrote: >>> >>>> 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) >> >>>> >>>> They all call wait_for_completion, which may sleep on something on >>>> purpose, I guess... >>> >>> That's OK because ip_set_destroy() calls rcu_barrier() which is needed to >>> handle flush in list type of sets. >>> >>> However, rcu_barrier() with call_rcu() together makes multiple destroys >>> one after another slow. But rcu_barrier() is needed for list type of sets >>> only and that can be handled separately. So could you test the patch >>> below? According to my tests it is even a little bit faster than the >>> original code before synchronize_rcu() was added to swap. >> >> Confirmed~! This patch does fix the performance regression in my case. >> >> Hope it can fix ale.crismani@automattic.com's original issue. >> >> >> >> Thanks~ >> David > > > Thanks for all the help on this, I'll try the patch tomorrow hopefully and will report back! > > best wishes, > Ale I applied the patch on 6.1.y on top of 875ee3a and I can confirm it fixes the performance issues in our case too. Thanks once more for having looked at this! Ale ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: Performance regression in ip_set_swap on 6.7.0 2024-01-15 13:29 ` Ale Crismani @ 2024-01-16 7:57 ` Jozsef Kadlecsik 0 siblings, 0 replies; 15+ messages in thread From: Jozsef Kadlecsik @ 2024-01-16 7:57 UTC (permalink / raw) To: Ale Crismani Cc: Wang David, linux-kernel, netfilter-devel, Ayuso Pablo Neira, xiaolinkui On Mon, 15 Jan 2024, Ale Crismani wrote: > > Il giorno 14 gen 2024, alle ore 21:38, Ale Crismani <ale.crismani@automattic.com> ha scritto: > > > >> Il giorno 14 gen 2024, alle ore 06:30, David Wang <00107082@163.com> ha scritto: > >> > >> > >> At 2024-01-14 02:24:07, "Jozsef Kadlecsik" <kadlec@blackhole.kfki.hu> wrote: > >>> On Thu, 11 Jan 2024, David Wang wrote: > >>> > >>>> 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) > >> > >>>> > >>>> They all call wait_for_completion, which may sleep on something on > >>>> purpose, I guess... > >>> > >>> That's OK because ip_set_destroy() calls rcu_barrier() which is needed to > >>> handle flush in list type of sets. > >>> > >>> However, rcu_barrier() with call_rcu() together makes multiple destroys > >>> one after another slow. But rcu_barrier() is needed for list type of sets > >>> only and that can be handled separately. So could you test the patch > >>> below? According to my tests it is even a little bit faster than the > >>> original code before synchronize_rcu() was added to swap. > >> > >> Confirmed~! This patch does fix the performance regression in my case. > >> > >> Hope it can fix ale.crismani@automattic.com's original issue. > > > > Thanks for all the help on this, I'll try the patch tomorrow hopefully > > and will report back! > > I applied the patch on 6.1.y on top of 875ee3a and I can confirm it > fixes the performance issues in our case too. Thanks for the testing, to both of you. I'm going to release the patch for kernel inclusion. Best regards, Jozsef -- E-mail : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.hu PGP key : https://wigner.hu/~kadlec/pgp_public_key.txt Address : Wigner Research Centre for Physics H-1525 Budapest 114, POB. 49, Hungary ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2024-01-16 7:57 UTC | newest] Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 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 ` Re:Performance regression in ip_set_swap on 6.7.0 David Wang 2024-01-13 18:24 ` 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
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®