aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2012-04-02 18:33:02 -0400
committerDavid S. Miller <davem@davemloft.net>2012-04-03 17:23:23 -0400
commit2def16ae6b0c77571200f18ba4be049b03d75579 (patch)
tree8b5bf927cba74c94e2a155b64052df07621917b8
parent01627d968c8b5e2810fe8c417b406b968297c236 (diff)
net: fix /proc/net/dev regression
Commit f04565ddf52 (dev: use name hash for dev_seq_ops) added a second regression, as some devices are missing from /proc/net/dev if many devices are defined. When seq_file buffer is filled, the last ->next/show() method is canceled (pos value is reverted to value prior ->next() call) Problem is after above commit, we dont restart the lookup at right position in ->start() method. Fix this by removing the internal 'pos' pointer added in commit, since we need to use the 'loff_t *pos' provided by seq_file layer. This also reverts commit 5cac98dd0 (net: Fix corruption in /proc/*/net/dev_mcast), since its not needed anymore. Reported-by: Ben Greear <greearb@candelatech.com> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Cc: Mihai Maruseac <mmaruseac@ixiacom.com> Tested-by: Ben Greear <greearb@candelatech.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/netdevice.h2
-rw-r--r--net/core/dev.c58
-rw-r--r--net/core/dev_addr_lists.c3
3 files changed, 15 insertions, 48 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 1f77540bdc95..5cbaa20f1659 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2604,8 +2604,6 @@ extern void net_disable_timestamp(void);
2604extern void *dev_seq_start(struct seq_file *seq, loff_t *pos); 2604extern void *dev_seq_start(struct seq_file *seq, loff_t *pos);
2605extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos); 2605extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos);
2606extern void dev_seq_stop(struct seq_file *seq, void *v); 2606extern void dev_seq_stop(struct seq_file *seq, void *v);
2607extern int dev_seq_open_ops(struct inode *inode, struct file *file,
2608 const struct seq_operations *ops);
2609#endif 2607#endif
2610 2608
2611extern int netdev_class_create_file(struct class_attribute *class_attr); 2609extern int netdev_class_create_file(struct class_attribute *class_attr);
diff --git a/net/core/dev.c b/net/core/dev.c
index 6c7dc9d78e10..c25d453b2803 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4028,54 +4028,41 @@ static int dev_ifconf(struct net *net, char __user *arg)
4028 4028
4029#ifdef CONFIG_PROC_FS 4029#ifdef CONFIG_PROC_FS
4030 4030
4031#define BUCKET_SPACE (32 - NETDEV_HASHBITS) 4031#define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1)
4032
4033struct dev_iter_state {
4034 struct seq_net_private p;
4035 unsigned int pos; /* bucket << BUCKET_SPACE + offset */
4036};
4037 4032
4038#define get_bucket(x) ((x) >> BUCKET_SPACE) 4033#define get_bucket(x) ((x) >> BUCKET_SPACE)
4039#define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) 4034#define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1))
4040#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) 4035#define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o))
4041 4036
4042static inline struct net_device *dev_from_same_bucket(struct seq_file *seq) 4037static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos)
4043{ 4038{
4044 struct dev_iter_state *state = seq->private;
4045 struct net *net = seq_file_net(seq); 4039 struct net *net = seq_file_net(seq);
4046 struct net_device *dev; 4040 struct net_device *dev;
4047 struct hlist_node *p; 4041 struct hlist_node *p;
4048 struct hlist_head *h; 4042 struct hlist_head *h;
4049 unsigned int count, bucket, offset; 4043 unsigned int count = 0, offset = get_offset(*pos);
4050 4044
4051 bucket = get_bucket(state->pos); 4045 h = &net->dev_name_head[get_bucket(*pos)];
4052 offset = get_offset(state->pos);
4053 h = &net->dev_name_head[bucket];
4054 count = 0;
4055 hlist_for_each_entry_rcu(dev, p, h, name_hlist) { 4046 hlist_for_each_entry_rcu(dev, p, h, name_hlist) {
4056 if (count++ == offset) { 4047 if (++count == offset)
4057 state->pos = set_bucket_offset(bucket, count);
4058 return dev; 4048 return dev;
4059 }
4060 } 4049 }
4061 4050
4062 return NULL; 4051 return NULL;
4063} 4052}
4064 4053
4065static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) 4054static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos)
4066{ 4055{
4067 struct dev_iter_state *state = seq->private;
4068 struct net_device *dev; 4056 struct net_device *dev;
4069 unsigned int bucket; 4057 unsigned int bucket;
4070 4058
4071 bucket = get_bucket(state->pos);
4072 do { 4059 do {
4073 dev = dev_from_same_bucket(seq); 4060 dev = dev_from_same_bucket(seq, pos);
4074 if (dev) 4061 if (dev)
4075 return dev; 4062 return dev;
4076 4063
4077 bucket++; 4064 bucket = get_bucket(*pos) + 1;
4078 state->pos = set_bucket_offset(bucket, 0); 4065 *pos = set_bucket_offset(bucket, 1);
4079 } while (bucket < NETDEV_HASHENTRIES); 4066 } while (bucket < NETDEV_HASHENTRIES);
4080 4067
4081 return NULL; 4068 return NULL;
@@ -4088,33 +4075,20 @@ static inline struct net_device *dev_from_new_bucket(struct seq_file *seq)
4088void *dev_seq_start(struct seq_file *seq, loff_t *pos) 4075void *dev_seq_start(struct seq_file *seq, loff_t *pos)
4089 __acquires(RCU) 4076 __acquires(RCU)
4090{ 4077{
4091 struct dev_iter_state *state = seq->private;
4092
4093 rcu_read_lock(); 4078 rcu_read_lock();
4094 if (!*pos) 4079 if (!*pos)
4095 return SEQ_START_TOKEN; 4080 return SEQ_START_TOKEN;
4096 4081
4097 /* check for end of the hash */ 4082 if (get_bucket(*pos) >= NETDEV_HASHENTRIES)
4098 if (state->pos == 0 && *pos > 1)
4099 return NULL; 4083 return NULL;
4100 4084
4101 return dev_from_new_bucket(seq); 4085 return dev_from_bucket(seq, pos);
4102} 4086}
4103 4087
4104void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) 4088void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
4105{ 4089{
4106 struct net_device *dev;
4107
4108 ++*pos; 4090 ++*pos;
4109 4091 return dev_from_bucket(seq, pos);
4110 if (v == SEQ_START_TOKEN)
4111 return dev_from_new_bucket(seq);
4112
4113 dev = dev_from_same_bucket(seq);
4114 if (dev)
4115 return dev;
4116
4117 return dev_from_new_bucket(seq);
4118} 4092}
4119 4093
4120void dev_seq_stop(struct seq_file *seq, void *v) 4094void dev_seq_stop(struct seq_file *seq, void *v)
@@ -4213,13 +4187,7 @@ static const struct seq_operations dev_seq_ops = {
4213static int dev_seq_open(struct inode *inode, struct file *file) 4187static int dev_seq_open(struct inode *inode, struct file *file)
4214{ 4188{
4215 return seq_open_net(inode, file, &dev_seq_ops, 4189 return seq_open_net(inode, file, &dev_seq_ops,
4216 sizeof(struct dev_iter_state)); 4190 sizeof(struct seq_net_private));
4217}
4218
4219int dev_seq_open_ops(struct inode *inode, struct file *file,
4220 const struct seq_operations *ops)
4221{
4222 return seq_open_net(inode, file, ops, sizeof(struct dev_iter_state));
4223} 4191}
4224 4192
4225static const struct file_operations dev_seq_fops = { 4193static const struct file_operations dev_seq_fops = {
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 29c07fef9228..626698f0db8b 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -696,7 +696,8 @@ static const struct seq_operations dev_mc_seq_ops = {
696 696
697static int dev_mc_seq_open(struct inode *inode, struct file *file) 697static int dev_mc_seq_open(struct inode *inode, struct file *file)
698{ 698{
699 return dev_seq_open_ops(inode, file, &dev_mc_seq_ops); 699 return seq_open_net(inode, file, &dev_mc_seq_ops,
700 sizeof(struct seq_net_private));
700} 701}
701 702
702static const struct file_operations dev_mc_seq_fops = { 703static const struct file_operations dev_mc_seq_fops = {