diff options
-rw-r--r-- | net/core/dev.c | 84 |
1 files changed, 69 insertions, 15 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 40d439524f49..ad5d7027c545 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -4093,6 +4093,60 @@ static int dev_ifconf(struct net *net, char __user *arg) | |||
4093 | } | 4093 | } |
4094 | 4094 | ||
4095 | #ifdef CONFIG_PROC_FS | 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 | * This is invoked by the /proc filesystem handler to display a device | 4151 | * This is invoked by the /proc filesystem handler to display a device |
4098 | * in detail. | 4152 | * in detail. |
@@ -4100,33 +4154,33 @@ static int dev_ifconf(struct net *net, char __user *arg) | |||
4100 | void *dev_seq_start(struct seq_file *seq, loff_t *pos) | 4154 | void *dev_seq_start(struct seq_file *seq, loff_t *pos) |
4101 | __acquires(RCU) | 4155 | __acquires(RCU) |
4102 | { | 4156 | { |
4103 | struct net *net = seq_file_net(seq); | 4157 | struct dev_iter_state *state = seq->private; |
4104 | loff_t off; | ||
4105 | struct net_device *dev; | ||
4106 | 4158 | ||
4107 | rcu_read_lock(); | 4159 | rcu_read_lock(); |
4108 | if (!*pos) | 4160 | if (!*pos) |
4109 | return SEQ_START_TOKEN; | 4161 | return SEQ_START_TOKEN; |
4110 | 4162 | ||
4111 | off = 1; | 4163 | /* check for end of the hash */ |
4112 | for_each_netdev_rcu(net, dev) | 4164 | if (state->pos == 0 && *pos > 1) |
4113 | if (off++ == *pos) | 4165 | return NULL; |
4114 | return dev; | ||
4115 | 4166 | ||
4116 | return NULL; | 4167 | return dev_from_new_bucket(seq); |
4117 | } | 4168 | } |
4118 | 4169 | ||
4119 | void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 4170 | void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
4120 | { | 4171 | { |
4121 | struct net_device *dev = v; | 4172 | struct net_device *dev; |
4173 | |||
4174 | ++*pos; | ||
4122 | 4175 | ||
4123 | if (v == SEQ_START_TOKEN) | 4176 | if (v == SEQ_START_TOKEN) |
4124 | dev = first_net_device_rcu(seq_file_net(seq)); | 4177 | return dev_from_new_bucket(seq); |
4125 | else | ||
4126 | dev = next_net_device_rcu(dev); | ||
4127 | 4178 | ||
4128 | ++*pos; | 4179 | dev = dev_from_same_bucket(seq); |
4129 | return dev; | 4180 | if (dev) |
4181 | return dev; | ||
4182 | |||
4183 | return dev_from_new_bucket(seq); | ||
4130 | } | 4184 | } |
4131 | 4185 | ||
4132 | void dev_seq_stop(struct seq_file *seq, void *v) | 4186 | void dev_seq_stop(struct seq_file *seq, void *v) |
@@ -4225,7 +4279,7 @@ static const struct seq_operations dev_seq_ops = { | |||
4225 | static int dev_seq_open(struct inode *inode, struct file *file) | 4279 | static int dev_seq_open(struct inode *inode, struct file *file) |
4226 | { | 4280 | { |
4227 | return seq_open_net(inode, file, &dev_seq_ops, | 4281 | return seq_open_net(inode, file, &dev_seq_ops, |
4228 | sizeof(struct seq_net_private)); | 4282 | sizeof(struct dev_iter_state)); |
4229 | } | 4283 | } |
4230 | 4284 | ||
4231 | static const struct file_operations dev_seq_fops = { | 4285 | static const struct file_operations dev_seq_fops = { |