From: Mihai Maruseac <hidden> Date: 2011-10-20 08:02:22
Instead of using the dev->next chain and trying to resync at each call to
dev_seq_start, use the name hash, keeping the bucket and the offset in
seq->private field.
The bucket number and the offset in the bucket are kept in a single unsigned
int value: pos = bucket << (32 - NETDEV_HASHBITS) + offset. When we reach the
end of the hash, both the bucket and the offset reach 0, so we need to test for
this in dev_seq_start to prevent endless loops.
Tests revealed the following results for ifconfig > /dev/null
* 1000 interfaces:
* 0.114s without patch
* 0.089s with patch
* 3000 interfaces:
* 0.489s without patch
* 0.110s with patch
* 5000 interfaces:
* 1.363s without patch
* 0.250s with patch
* 128000 interfaces (other setup):
* ~100s without patch
* ~30s with patch
Signed-off-by: Mihai Maruseac <redacted>
---
net/core/dev.c | 83 +++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 68 insertions(+), 15 deletions(-)
@@ -4048,33 +4101,33 @@ static int dev_ifconf(struct net *net, char __user *arg)void*dev_seq_start(structseq_file*seq,loff_t*pos)__acquires(RCU){-structnet*net=seq_file_net(seq);-loff_toff;-structnet_device*dev;+structdev_iter_state*state=seq->private;rcu_read_lock();if(!*pos)returnSEQ_START_TOKEN;-off=1;-for_each_netdev_rcu(net,dev)-if(off++==*pos)-returndev;+/* check for end of the hash */+if(state->pos==0&&*pos>1)+returnNULL;-returnNULL;+returndev_from_new_bucket(seq);}void*dev_seq_next(structseq_file*seq,void*v,loff_t*pos){-structnet_device*dev=v;+structnet_device*dev;++++*pos;if(v==SEQ_START_TOKEN)-dev=first_net_device_rcu(seq_file_net(seq));-else-dev=next_net_device_rcu(dev);+returndev_from_new_bucket(seq);-++*pos;-returndev;+dev=dev_from_same_bucket(seq);+if(dev)+returndev;++returndev_from_new_bucket(seq);}voiddev_seq_stop(structseq_file*seq,void*v)
From: David Miller <davem@davemloft.net> Date: 2011-10-20 20:18:00
From: Mihai Maruseac <redacted>
Date: Thu, 20 Oct 2011 11:01:57 +0300
Instead of using the dev->next chain and trying to resync at each call to
dev_seq_start, use the name hash, keeping the bucket and the offset in
seq->private field.
I'm totally fine with this patch from a technical perspective, but I'd
like one small thing tidied up before I apply this.
+ unsigned int pos; /* bucket << 24 + offset */
Please don't mention this as a constant in the comment, if we ever
change NETDEV_HASHBITS this comment will be inaccurate.
I'd suggest putting the BUCKET_SPACE define before the dev_iter_state
definition, and using BUCKET_SPACE in the comment instead of 24.
Thanks.
From: Mihai Maruseac <hidden> Date: 2011-10-21 06:45:28
Instead of using the dev->next chain and trying to resync at each call to
dev_seq_start, use the name hash, keeping the bucket and the offset in
seq->private field.
Tests revealed the following results for ifconfig > /dev/null
* 1000 interfaces:
* 0.114s without patch
* 0.089s with patch
* 3000 interfaces:
* 0.489s without patch
* 0.110s with patch
* 5000 interfaces:
* 1.363s without patch
* 0.250s with patch
* 128000 interfaces (other setup):
* ~100s without patch
* ~30s with patch
Signed-off-by: Mihai Maruseac <redacted>
---
net/core/dev.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 69 insertions(+), 15 deletions(-)
@@ -4048,33 +4102,33 @@ static int dev_ifconf(struct net *net, char __user *arg)void*dev_seq_start(structseq_file*seq,loff_t*pos)__acquires(RCU){-structnet*net=seq_file_net(seq);-loff_toff;-structnet_device*dev;+structdev_iter_state*state=seq->private;rcu_read_lock();if(!*pos)returnSEQ_START_TOKEN;-off=1;-for_each_netdev_rcu(net,dev)-if(off++==*pos)-returndev;+/* check for end of the hash */+if(state->pos==0&&*pos>1)+returnNULL;-returnNULL;+returndev_from_new_bucket(seq);}void*dev_seq_next(structseq_file*seq,void*v,loff_t*pos){-structnet_device*dev=v;+structnet_device*dev;++++*pos;if(v==SEQ_START_TOKEN)-dev=first_net_device_rcu(seq_file_net(seq));-else-dev=next_net_device_rcu(dev);+returndev_from_new_bucket(seq);-++*pos;-returndev;+dev=dev_from_same_bucket(seq);+if(dev)+returndev;++returndev_from_new_bucket(seq);}voiddev_seq_stop(structseq_file*seq,void*v)
From: Eric Dumazet <hidden> Date: 2011-10-21 06:53:04
Le vendredi 21 octobre 2011 à 09:45 +0300, Mihai Maruseac a écrit :
Instead of using the dev->next chain and trying to resync at each call to
dev_seq_start, use the name hash, keeping the bucket and the offset in
seq->private field.
From: David Miller <hidden> Date: 2011-10-21 06:55:54
From: Mihai Maruseac <redacted>
Date: Fri, 21 Oct 2011 09:45:10 +0300
Instead of using the dev->next chain and trying to resync at each call to
dev_seq_start, use the name hash, keeping the bucket and the offset in
seq->private field.
Tests revealed the following results for ifconfig > /dev/null
From: Stephen Hemminger <hidden> Date: 2011-10-21 17:08:10
On Fri, 21 Oct 2011 09:45:10 +0300
Mihai Maruseac [off-list ref] wrote:
quoted hunk
Instead of using the dev->next chain and trying to resync at each call to
dev_seq_start, use the name hash, keeping the bucket and the offset in
seq->private field.
Tests revealed the following results for ifconfig > /dev/null
* 1000 interfaces:
* 0.114s without patch
* 0.089s with patch
* 3000 interfaces:
* 0.489s without patch
* 0.110s with patch
* 5000 interfaces:
* 1.363s without patch
* 0.250s with patch
* 128000 interfaces (other setup):
* ~100s without patch
* ~30s with patch
Signed-off-by: Mihai Maruseac <redacted>
---
net/core/dev.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 69 insertions(+), 15 deletions(-)