diff options
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 79 |
1 files changed, 34 insertions, 45 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 5d59155adf2a..9bb8f87c4cda 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1409,14 +1409,34 @@ EXPORT_SYMBOL(register_netdevice_notifier); | |||
1409 | * register_netdevice_notifier(). The notifier is unlinked into the | 1409 | * register_netdevice_notifier(). The notifier is unlinked into the |
1410 | * kernel structures and may then be reused. A negative errno code | 1410 | * kernel structures and may then be reused. A negative errno code |
1411 | * is returned on a failure. | 1411 | * is returned on a failure. |
1412 | * | ||
1413 | * After unregistering unregister and down device events are synthesized | ||
1414 | * for all devices on the device list to the removed notifier to remove | ||
1415 | * the need for special case cleanup code. | ||
1412 | */ | 1416 | */ |
1413 | 1417 | ||
1414 | int unregister_netdevice_notifier(struct notifier_block *nb) | 1418 | int unregister_netdevice_notifier(struct notifier_block *nb) |
1415 | { | 1419 | { |
1420 | struct net_device *dev; | ||
1421 | struct net *net; | ||
1416 | int err; | 1422 | int err; |
1417 | 1423 | ||
1418 | rtnl_lock(); | 1424 | rtnl_lock(); |
1419 | err = raw_notifier_chain_unregister(&netdev_chain, nb); | 1425 | err = raw_notifier_chain_unregister(&netdev_chain, nb); |
1426 | if (err) | ||
1427 | goto unlock; | ||
1428 | |||
1429 | for_each_net(net) { | ||
1430 | for_each_netdev(net, dev) { | ||
1431 | if (dev->flags & IFF_UP) { | ||
1432 | nb->notifier_call(nb, NETDEV_GOING_DOWN, dev); | ||
1433 | nb->notifier_call(nb, NETDEV_DOWN, dev); | ||
1434 | } | ||
1435 | nb->notifier_call(nb, NETDEV_UNREGISTER, dev); | ||
1436 | nb->notifier_call(nb, NETDEV_UNREGISTER_BATCH, dev); | ||
1437 | } | ||
1438 | } | ||
1439 | unlock: | ||
1420 | rtnl_unlock(); | 1440 | rtnl_unlock(); |
1421 | return err; | 1441 | return err; |
1422 | } | 1442 | } |
@@ -1596,6 +1616,7 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) | |||
1596 | kfree_skb(skb); | 1616 | kfree_skb(skb); |
1597 | return NET_RX_DROP; | 1617 | return NET_RX_DROP; |
1598 | } | 1618 | } |
1619 | skb->skb_iif = 0; | ||
1599 | skb_set_dev(skb, dev); | 1620 | skb_set_dev(skb, dev); |
1600 | skb->tstamp.tv64 = 0; | 1621 | skb->tstamp.tv64 = 0; |
1601 | skb->pkt_type = PACKET_HOST; | 1622 | skb->pkt_type = PACKET_HOST; |
@@ -4027,54 +4048,41 @@ static int dev_ifconf(struct net *net, char __user *arg) | |||
4027 | 4048 | ||
4028 | #ifdef CONFIG_PROC_FS | 4049 | #ifdef CONFIG_PROC_FS |
4029 | 4050 | ||
4030 | #define BUCKET_SPACE (32 - NETDEV_HASHBITS) | 4051 | #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) |
4031 | |||
4032 | struct dev_iter_state { | ||
4033 | struct seq_net_private p; | ||
4034 | unsigned int pos; /* bucket << BUCKET_SPACE + offset */ | ||
4035 | }; | ||
4036 | 4052 | ||
4037 | #define get_bucket(x) ((x) >> BUCKET_SPACE) | 4053 | #define get_bucket(x) ((x) >> BUCKET_SPACE) |
4038 | #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) | 4054 | #define get_offset(x) ((x) & ((1 << BUCKET_SPACE) - 1)) |
4039 | #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) | 4055 | #define set_bucket_offset(b, o) ((b) << BUCKET_SPACE | (o)) |
4040 | 4056 | ||
4041 | static inline struct net_device *dev_from_same_bucket(struct seq_file *seq) | 4057 | static inline struct net_device *dev_from_same_bucket(struct seq_file *seq, loff_t *pos) |
4042 | { | 4058 | { |
4043 | struct dev_iter_state *state = seq->private; | ||
4044 | struct net *net = seq_file_net(seq); | 4059 | struct net *net = seq_file_net(seq); |
4045 | struct net_device *dev; | 4060 | struct net_device *dev; |
4046 | struct hlist_node *p; | 4061 | struct hlist_node *p; |
4047 | struct hlist_head *h; | 4062 | struct hlist_head *h; |
4048 | unsigned int count, bucket, offset; | 4063 | unsigned int count = 0, offset = get_offset(*pos); |
4049 | 4064 | ||
4050 | bucket = get_bucket(state->pos); | 4065 | h = &net->dev_name_head[get_bucket(*pos)]; |
4051 | offset = get_offset(state->pos); | ||
4052 | h = &net->dev_name_head[bucket]; | ||
4053 | count = 0; | ||
4054 | hlist_for_each_entry_rcu(dev, p, h, name_hlist) { | 4066 | hlist_for_each_entry_rcu(dev, p, h, name_hlist) { |
4055 | if (count++ == offset) { | 4067 | if (++count == offset) |
4056 | state->pos = set_bucket_offset(bucket, count); | ||
4057 | return dev; | 4068 | return dev; |
4058 | } | ||
4059 | } | 4069 | } |
4060 | 4070 | ||
4061 | return NULL; | 4071 | return NULL; |
4062 | } | 4072 | } |
4063 | 4073 | ||
4064 | static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) | 4074 | static inline struct net_device *dev_from_bucket(struct seq_file *seq, loff_t *pos) |
4065 | { | 4075 | { |
4066 | struct dev_iter_state *state = seq->private; | ||
4067 | struct net_device *dev; | 4076 | struct net_device *dev; |
4068 | unsigned int bucket; | 4077 | unsigned int bucket; |
4069 | 4078 | ||
4070 | bucket = get_bucket(state->pos); | ||
4071 | do { | 4079 | do { |
4072 | dev = dev_from_same_bucket(seq); | 4080 | dev = dev_from_same_bucket(seq, pos); |
4073 | if (dev) | 4081 | if (dev) |
4074 | return dev; | 4082 | return dev; |
4075 | 4083 | ||
4076 | bucket++; | 4084 | bucket = get_bucket(*pos) + 1; |
4077 | state->pos = set_bucket_offset(bucket, 0); | 4085 | *pos = set_bucket_offset(bucket, 1); |
4078 | } while (bucket < NETDEV_HASHENTRIES); | 4086 | } while (bucket < NETDEV_HASHENTRIES); |
4079 | 4087 | ||
4080 | return NULL; | 4088 | return NULL; |
@@ -4087,33 +4095,20 @@ static inline struct net_device *dev_from_new_bucket(struct seq_file *seq) | |||
4087 | void *dev_seq_start(struct seq_file *seq, loff_t *pos) | 4095 | void *dev_seq_start(struct seq_file *seq, loff_t *pos) |
4088 | __acquires(RCU) | 4096 | __acquires(RCU) |
4089 | { | 4097 | { |
4090 | struct dev_iter_state *state = seq->private; | ||
4091 | |||
4092 | rcu_read_lock(); | 4098 | rcu_read_lock(); |
4093 | if (!*pos) | 4099 | if (!*pos) |
4094 | return SEQ_START_TOKEN; | 4100 | return SEQ_START_TOKEN; |
4095 | 4101 | ||
4096 | /* check for end of the hash */ | 4102 | if (get_bucket(*pos) >= NETDEV_HASHENTRIES) |
4097 | if (state->pos == 0 && *pos > 1) | ||
4098 | return NULL; | 4103 | return NULL; |
4099 | 4104 | ||
4100 | return dev_from_new_bucket(seq); | 4105 | return dev_from_bucket(seq, pos); |
4101 | } | 4106 | } |
4102 | 4107 | ||
4103 | void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 4108 | void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) |
4104 | { | 4109 | { |
4105 | struct net_device *dev; | ||
4106 | |||
4107 | ++*pos; | 4110 | ++*pos; |
4108 | 4111 | return dev_from_bucket(seq, pos); | |
4109 | if (v == SEQ_START_TOKEN) | ||
4110 | return dev_from_new_bucket(seq); | ||
4111 | |||
4112 | dev = dev_from_same_bucket(seq); | ||
4113 | if (dev) | ||
4114 | return dev; | ||
4115 | |||
4116 | return dev_from_new_bucket(seq); | ||
4117 | } | 4112 | } |
4118 | 4113 | ||
4119 | void dev_seq_stop(struct seq_file *seq, void *v) | 4114 | void dev_seq_stop(struct seq_file *seq, void *v) |
@@ -4212,13 +4207,7 @@ static const struct seq_operations dev_seq_ops = { | |||
4212 | static int dev_seq_open(struct inode *inode, struct file *file) | 4207 | static int dev_seq_open(struct inode *inode, struct file *file) |
4213 | { | 4208 | { |
4214 | return seq_open_net(inode, file, &dev_seq_ops, | 4209 | return seq_open_net(inode, file, &dev_seq_ops, |
4215 | sizeof(struct dev_iter_state)); | 4210 | sizeof(struct seq_net_private)); |
4216 | } | ||
4217 | |||
4218 | int dev_seq_open_ops(struct inode *inode, struct file *file, | ||
4219 | const struct seq_operations *ops) | ||
4220 | { | ||
4221 | return seq_open_net(inode, file, ops, sizeof(struct dev_iter_state)); | ||
4222 | } | 4211 | } |
4223 | 4212 | ||
4224 | static const struct file_operations dev_seq_fops = { | 4213 | static const struct file_operations dev_seq_fops = { |