Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dev: use name hash for dev_seq_ops

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 <mmaruseac@ixiacom.com>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Mihai Maruseac and committed by
David S. Miller
f04565dd e09eff7f

+70 -16
+70 -16
net/core/dev.c
··· 4093 4093 } 4094 4094 4095 4095 #ifdef CONFIG_PROC_FS 4096 + 4097 + #define BUCKET_SPACE (32 - NETDEV_HASHBITS) 4098 + 4099 + struct dev_iter_state { 4100 + struct seq_net_private p; 4101 + unsigned int pos; /* bucket << BUCKET_SPACE + offset */ 4102 + }; 4103 + 4104 + #define get_bucket(x) ((x) >> BUCKET_SPACE) 4105 + #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) 4106 + #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) 4107 + 4108 + static inline struct net_device *dev_from_same_bucket(struct seq_file *seq) 4109 + { 4110 + struct dev_iter_state *state = seq->private; 4111 + struct net *net = seq_file_net(seq); 4112 + struct net_device *dev; 4113 + struct hlist_node *p; 4114 + struct hlist_head *h; 4115 + unsigned int count, bucket, offset; 4116 + 4117 + bucket = get_bucket(state->pos); 4118 + offset = get_offset(state->pos); 4119 + h = &net->dev_name_head[bucket]; 4120 + count = 0; 4121 + hlist_for_each_entry_rcu(dev, p, h, name_hlist) { 4122 + if (count++ == offset) { 4123 + state->pos = set_bucket_offset(bucket, count); 4124 + return dev; 4125 + } 4126 + } 4127 + 4128 + return NULL; 4129 + } 4130 + 4131 + static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) 4132 + { 4133 + struct dev_iter_state *state = seq->private; 4134 + struct net_device *dev; 4135 + unsigned int bucket; 4136 + 4137 + bucket = get_bucket(state->pos); 4138 + do { 4139 + dev = dev_from_same_bucket(seq); 4140 + if (dev) 4141 + return dev; 4142 + 4143 + bucket++; 4144 + state->pos = set_bucket_offset(bucket, 0); 4145 + } while (bucket < NETDEV_HASHENTRIES); 4146 + 4147 + return NULL; 4148 + } 4149 + 4096 4150 /* 4097 4151 * This is invoked by the /proc filesystem handler to display a device 4098 4152 * in detail. ··· 4154 4100 void *dev_seq_start(struct seq_file *seq, loff_t *pos) 4155 4101 __acquires(RCU) 4156 4102 { 4157 - struct net *net = seq_file_net(seq); 4158 - loff_t off; 4159 - struct net_device *dev; 4103 + struct dev_iter_state *state = seq->private; 4160 4104 4161 4105 rcu_read_lock(); 4162 4106 if (!*pos) 4163 4107 return SEQ_START_TOKEN; 4164 4108 4165 - off = 1; 4166 - for_each_netdev_rcu(net, dev) 4167 - if (off++ == *pos) 4168 - return dev; 4109 + /* check for end of the hash */ 4110 + if (state->pos == 0 && *pos > 1) 4111 + return NULL; 4169 4112 4170 - return NULL; 4113 + return dev_from_new_bucket(seq); 4171 4114 } 4172 4115 4173 4116 void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) 4174 4117 { 4175 - struct net_device *dev = v; 4176 - 4177 - if (v == SEQ_START_TOKEN) 4178 - dev = first_net_device_rcu(seq_file_net(seq)); 4179 - else 4180 - dev = next_net_device_rcu(dev); 4118 + struct net_device *dev; 4181 4119 4182 4120 ++*pos; 4183 - return dev; 4121 + 4122 + if (v == SEQ_START_TOKEN) 4123 + return dev_from_new_bucket(seq); 4124 + 4125 + dev = dev_from_same_bucket(seq); 4126 + if (dev) 4127 + return dev; 4128 + 4129 + return dev_from_new_bucket(seq); 4184 4130 } 4185 4131 4186 4132 void dev_seq_stop(struct seq_file *seq, void *v) ··· 4279 4225 static int dev_seq_open(struct inode *inode, struct file *file) 4280 4226 { 4281 4227 return seq_open_net(inode, file, &dev_seq_ops, 4282 - sizeof(struct seq_net_private)); 4228 + sizeof(struct dev_iter_state)); 4283 4229 } 4284 4230 4285 4231 static const struct file_operations dev_seq_fops = {