diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-04-02 18:33:02 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-04-03 17:23:23 -0400 |
commit | 2def16ae6b0c77571200f18ba4be049b03d75579 (patch) | |
tree | 8b5bf927cba74c94e2a155b64052df07621917b8 /net | |
parent | 01627d968c8b5e2810fe8c417b406b968297c236 (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>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/dev.c | 58 | ||||
-rw-r--r-- | net/core/dev_addr_lists.c | 3 |
2 files changed, 15 insertions, 46 deletions
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 | |||
4033 | struct 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 | ||
4042 | static inline struct net_device *dev_from_same_bucket(struct seq_file *seq) | 4037 | static 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 | ||
4065 | static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) | 4054 | static 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) | |||
4088 | void *dev_seq_start(struct seq_file *seq, loff_t *pos) | 4075 | void *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 | ||
4104 | void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 4088 | void *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 | ||
4120 | void dev_seq_stop(struct seq_file *seq, void *v) | 4094 | void dev_seq_stop(struct seq_file *seq, void *v) |
@@ -4213,13 +4187,7 @@ static const struct seq_operations dev_seq_ops = { | |||
4213 | static int dev_seq_open(struct inode *inode, struct file *file) | 4187 | static 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 | |||
4219 | int 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 | ||
4225 | static const struct file_operations dev_seq_fops = { | 4193 | static 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 | ||
697 | static int dev_mc_seq_open(struct inode *inode, struct file *file) | 697 | static 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 | ||
702 | static const struct file_operations dev_mc_seq_fops = { | 703 | static const struct file_operations dev_mc_seq_fops = { |