diff options
Diffstat (limited to 'drivers/net/macvlan.c')
| -rw-r--r-- | drivers/net/macvlan.c | 105 |
1 files changed, 71 insertions, 34 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 87e8d4cb4057..0ef0eb0db945 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c | |||
| @@ -37,8 +37,14 @@ struct macvlan_port { | |||
| 37 | struct net_device *dev; | 37 | struct net_device *dev; |
| 38 | struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; | 38 | struct hlist_head vlan_hash[MACVLAN_HASH_SIZE]; |
| 39 | struct list_head vlans; | 39 | struct list_head vlans; |
| 40 | struct rcu_head rcu; | ||
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 43 | #define macvlan_port_get_rcu(dev) \ | ||
| 44 | ((struct macvlan_port *) rcu_dereference(dev->rx_handler_data)) | ||
| 45 | #define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data) | ||
| 46 | #define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT) | ||
| 47 | |||
| 42 | static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, | 48 | static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port, |
| 43 | const unsigned char *addr) | 49 | const unsigned char *addr) |
| 44 | { | 50 | { |
| @@ -145,15 +151,17 @@ static void macvlan_broadcast(struct sk_buff *skb, | |||
| 145 | } | 151 | } |
| 146 | 152 | ||
| 147 | /* called under rcu_read_lock() from netif_receive_skb */ | 153 | /* called under rcu_read_lock() from netif_receive_skb */ |
| 148 | static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port, | 154 | static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb) |
| 149 | struct sk_buff *skb) | ||
| 150 | { | 155 | { |
| 156 | struct macvlan_port *port; | ||
| 151 | const struct ethhdr *eth = eth_hdr(skb); | 157 | const struct ethhdr *eth = eth_hdr(skb); |
| 152 | const struct macvlan_dev *vlan; | 158 | const struct macvlan_dev *vlan; |
| 153 | const struct macvlan_dev *src; | 159 | const struct macvlan_dev *src; |
| 154 | struct net_device *dev; | 160 | struct net_device *dev; |
| 155 | unsigned int len; | 161 | unsigned int len = 0; |
| 162 | int ret = NET_RX_DROP; | ||
| 156 | 163 | ||
| 164 | port = macvlan_port_get_rcu(skb->dev); | ||
| 157 | if (is_multicast_ether_addr(eth->h_dest)) { | 165 | if (is_multicast_ether_addr(eth->h_dest)) { |
| 158 | src = macvlan_hash_lookup(port, eth->h_source); | 166 | src = macvlan_hash_lookup(port, eth->h_source); |
| 159 | if (!src) | 167 | if (!src) |
| @@ -188,14 +196,16 @@ static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port, | |||
| 188 | } | 196 | } |
| 189 | len = skb->len + ETH_HLEN; | 197 | len = skb->len + ETH_HLEN; |
| 190 | skb = skb_share_check(skb, GFP_ATOMIC); | 198 | skb = skb_share_check(skb, GFP_ATOMIC); |
| 191 | macvlan_count_rx(vlan, len, skb != NULL, 0); | ||
| 192 | if (!skb) | 199 | if (!skb) |
| 193 | return NULL; | 200 | goto out; |
| 194 | 201 | ||
| 195 | skb->dev = dev; | 202 | skb->dev = dev; |
| 196 | skb->pkt_type = PACKET_HOST; | 203 | skb->pkt_type = PACKET_HOST; |
| 197 | 204 | ||
| 198 | vlan->receive(skb); | 205 | ret = vlan->receive(skb); |
| 206 | |||
| 207 | out: | ||
| 208 | macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, 0); | ||
| 199 | return NULL; | 209 | return NULL; |
| 200 | } | 210 | } |
| 201 | 211 | ||
| @@ -424,29 +434,38 @@ static void macvlan_uninit(struct net_device *dev) | |||
| 424 | free_percpu(vlan->rx_stats); | 434 | free_percpu(vlan->rx_stats); |
| 425 | } | 435 | } |
| 426 | 436 | ||
| 427 | static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev) | 437 | static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, |
| 438 | struct rtnl_link_stats64 *stats) | ||
| 428 | { | 439 | { |
| 429 | struct net_device_stats *stats = &dev->stats; | ||
| 430 | struct macvlan_dev *vlan = netdev_priv(dev); | 440 | struct macvlan_dev *vlan = netdev_priv(dev); |
| 431 | 441 | ||
| 432 | dev_txq_stats_fold(dev, stats); | 442 | dev_txq_stats_fold(dev, stats); |
| 433 | 443 | ||
| 434 | if (vlan->rx_stats) { | 444 | if (vlan->rx_stats) { |
| 435 | struct macvlan_rx_stats *p, rx = {0}; | 445 | struct macvlan_rx_stats *p, accum = {0}; |
| 446 | u64 rx_packets, rx_bytes, rx_multicast; | ||
| 447 | unsigned int start; | ||
| 436 | int i; | 448 | int i; |
| 437 | 449 | ||
| 438 | for_each_possible_cpu(i) { | 450 | for_each_possible_cpu(i) { |
| 439 | p = per_cpu_ptr(vlan->rx_stats, i); | 451 | p = per_cpu_ptr(vlan->rx_stats, i); |
| 440 | rx.rx_packets += p->rx_packets; | 452 | do { |
| 441 | rx.rx_bytes += p->rx_bytes; | 453 | start = u64_stats_fetch_begin_bh(&p->syncp); |
| 442 | rx.rx_errors += p->rx_errors; | 454 | rx_packets = p->rx_packets; |
| 443 | rx.multicast += p->multicast; | 455 | rx_bytes = p->rx_bytes; |
| 456 | rx_multicast = p->rx_multicast; | ||
| 457 | } while (u64_stats_fetch_retry_bh(&p->syncp, start)); | ||
| 458 | accum.rx_packets += rx_packets; | ||
| 459 | accum.rx_bytes += rx_bytes; | ||
| 460 | accum.rx_multicast += rx_multicast; | ||
| 461 | /* rx_errors is an ulong, updated without syncp protection */ | ||
| 462 | accum.rx_errors += p->rx_errors; | ||
| 444 | } | 463 | } |
| 445 | stats->rx_packets = rx.rx_packets; | 464 | stats->rx_packets = accum.rx_packets; |
| 446 | stats->rx_bytes = rx.rx_bytes; | 465 | stats->rx_bytes = accum.rx_bytes; |
| 447 | stats->rx_errors = rx.rx_errors; | 466 | stats->rx_errors = accum.rx_errors; |
| 448 | stats->rx_dropped = rx.rx_errors; | 467 | stats->rx_dropped = accum.rx_errors; |
| 449 | stats->multicast = rx.multicast; | 468 | stats->multicast = accum.rx_multicast; |
| 450 | } | 469 | } |
| 451 | return stats; | 470 | return stats; |
| 452 | } | 471 | } |
| @@ -495,11 +514,11 @@ static const struct net_device_ops macvlan_netdev_ops = { | |||
| 495 | .ndo_change_rx_flags = macvlan_change_rx_flags, | 514 | .ndo_change_rx_flags = macvlan_change_rx_flags, |
| 496 | .ndo_set_mac_address = macvlan_set_mac_address, | 515 | .ndo_set_mac_address = macvlan_set_mac_address, |
| 497 | .ndo_set_multicast_list = macvlan_set_multicast_list, | 516 | .ndo_set_multicast_list = macvlan_set_multicast_list, |
| 498 | .ndo_get_stats = macvlan_dev_get_stats, | 517 | .ndo_get_stats64 = macvlan_dev_get_stats64, |
| 499 | .ndo_validate_addr = eth_validate_addr, | 518 | .ndo_validate_addr = eth_validate_addr, |
| 500 | }; | 519 | }; |
| 501 | 520 | ||
| 502 | static void macvlan_setup(struct net_device *dev) | 521 | void macvlan_common_setup(struct net_device *dev) |
| 503 | { | 522 | { |
| 504 | ether_setup(dev); | 523 | ether_setup(dev); |
| 505 | 524 | ||
| @@ -508,6 +527,12 @@ static void macvlan_setup(struct net_device *dev) | |||
| 508 | dev->destructor = free_netdev; | 527 | dev->destructor = free_netdev; |
| 509 | dev->header_ops = &macvlan_hard_header_ops, | 528 | dev->header_ops = &macvlan_hard_header_ops, |
| 510 | dev->ethtool_ops = &macvlan_ethtool_ops; | 529 | dev->ethtool_ops = &macvlan_ethtool_ops; |
| 530 | } | ||
| 531 | EXPORT_SYMBOL_GPL(macvlan_common_setup); | ||
| 532 | |||
| 533 | static void macvlan_setup(struct net_device *dev) | ||
| 534 | { | ||
| 535 | macvlan_common_setup(dev); | ||
| 511 | dev->tx_queue_len = 0; | 536 | dev->tx_queue_len = 0; |
| 512 | } | 537 | } |
| 513 | 538 | ||
| @@ -515,6 +540,7 @@ static int macvlan_port_create(struct net_device *dev) | |||
| 515 | { | 540 | { |
| 516 | struct macvlan_port *port; | 541 | struct macvlan_port *port; |
| 517 | unsigned int i; | 542 | unsigned int i; |
| 543 | int err; | ||
| 518 | 544 | ||
| 519 | if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) | 545 | if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) |
| 520 | return -EINVAL; | 546 | return -EINVAL; |
| @@ -527,19 +553,32 @@ static int macvlan_port_create(struct net_device *dev) | |||
| 527 | INIT_LIST_HEAD(&port->vlans); | 553 | INIT_LIST_HEAD(&port->vlans); |
| 528 | for (i = 0; i < MACVLAN_HASH_SIZE; i++) | 554 | for (i = 0; i < MACVLAN_HASH_SIZE; i++) |
| 529 | INIT_HLIST_HEAD(&port->vlan_hash[i]); | 555 | INIT_HLIST_HEAD(&port->vlan_hash[i]); |
| 530 | rcu_assign_pointer(dev->macvlan_port, port); | 556 | |
| 531 | return 0; | 557 | err = netdev_rx_handler_register(dev, macvlan_handle_frame, port); |
| 558 | if (err) | ||
| 559 | kfree(port); | ||
| 560 | |||
| 561 | dev->priv_flags |= IFF_MACVLAN_PORT; | ||
| 562 | return err; | ||
| 532 | } | 563 | } |
| 533 | 564 | ||
| 534 | static void macvlan_port_destroy(struct net_device *dev) | 565 | static void macvlan_port_rcu_free(struct rcu_head *head) |
| 535 | { | 566 | { |
| 536 | struct macvlan_port *port = dev->macvlan_port; | 567 | struct macvlan_port *port; |
| 537 | 568 | ||
| 538 | rcu_assign_pointer(dev->macvlan_port, NULL); | 569 | port = container_of(head, struct macvlan_port, rcu); |
| 539 | synchronize_rcu(); | ||
| 540 | kfree(port); | 570 | kfree(port); |
| 541 | } | 571 | } |
| 542 | 572 | ||
| 573 | static void macvlan_port_destroy(struct net_device *dev) | ||
| 574 | { | ||
| 575 | struct macvlan_port *port = macvlan_port_get(dev); | ||
| 576 | |||
| 577 | dev->priv_flags &= ~IFF_MACVLAN_PORT; | ||
| 578 | netdev_rx_handler_unregister(dev); | ||
| 579 | call_rcu(&port->rcu, macvlan_port_rcu_free); | ||
| 580 | } | ||
| 581 | |||
| 543 | static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) | 582 | static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) |
| 544 | { | 583 | { |
| 545 | if (tb[IFLA_ADDRESS]) { | 584 | if (tb[IFLA_ADDRESS]) { |
| @@ -615,12 +654,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, | |||
| 615 | if (!tb[IFLA_ADDRESS]) | 654 | if (!tb[IFLA_ADDRESS]) |
| 616 | random_ether_addr(dev->dev_addr); | 655 | random_ether_addr(dev->dev_addr); |
| 617 | 656 | ||
| 618 | if (lowerdev->macvlan_port == NULL) { | 657 | if (!macvlan_port_exists(lowerdev)) { |
| 619 | err = macvlan_port_create(lowerdev); | 658 | err = macvlan_port_create(lowerdev); |
| 620 | if (err < 0) | 659 | if (err < 0) |
| 621 | return err; | 660 | return err; |
| 622 | } | 661 | } |
| 623 | port = lowerdev->macvlan_port; | 662 | port = macvlan_port_get(lowerdev); |
| 624 | 663 | ||
| 625 | vlan->lowerdev = lowerdev; | 664 | vlan->lowerdev = lowerdev; |
| 626 | vlan->dev = dev; | 665 | vlan->dev = dev; |
| @@ -705,7 +744,6 @@ int macvlan_link_register(struct rtnl_link_ops *ops) | |||
| 705 | /* common fields */ | 744 | /* common fields */ |
| 706 | ops->priv_size = sizeof(struct macvlan_dev); | 745 | ops->priv_size = sizeof(struct macvlan_dev); |
| 707 | ops->get_tx_queues = macvlan_get_tx_queues; | 746 | ops->get_tx_queues = macvlan_get_tx_queues; |
| 708 | ops->setup = macvlan_setup; | ||
| 709 | ops->validate = macvlan_validate; | 747 | ops->validate = macvlan_validate; |
| 710 | ops->maxtype = IFLA_MACVLAN_MAX; | 748 | ops->maxtype = IFLA_MACVLAN_MAX; |
| 711 | ops->policy = macvlan_policy; | 749 | ops->policy = macvlan_policy; |
| @@ -719,6 +757,7 @@ EXPORT_SYMBOL_GPL(macvlan_link_register); | |||
| 719 | 757 | ||
| 720 | static struct rtnl_link_ops macvlan_link_ops = { | 758 | static struct rtnl_link_ops macvlan_link_ops = { |
| 721 | .kind = "macvlan", | 759 | .kind = "macvlan", |
| 760 | .setup = macvlan_setup, | ||
| 722 | .newlink = macvlan_newlink, | 761 | .newlink = macvlan_newlink, |
| 723 | .dellink = macvlan_dellink, | 762 | .dellink = macvlan_dellink, |
| 724 | }; | 763 | }; |
| @@ -730,10 +769,11 @@ static int macvlan_device_event(struct notifier_block *unused, | |||
| 730 | struct macvlan_dev *vlan, *next; | 769 | struct macvlan_dev *vlan, *next; |
| 731 | struct macvlan_port *port; | 770 | struct macvlan_port *port; |
| 732 | 771 | ||
| 733 | port = dev->macvlan_port; | 772 | if (!macvlan_port_exists(dev)) |
| 734 | if (port == NULL) | ||
| 735 | return NOTIFY_DONE; | 773 | return NOTIFY_DONE; |
| 736 | 774 | ||
| 775 | port = macvlan_port_get(dev); | ||
| 776 | |||
| 737 | switch (event) { | 777 | switch (event) { |
| 738 | case NETDEV_CHANGE: | 778 | case NETDEV_CHANGE: |
| 739 | list_for_each_entry(vlan, &port->vlans, list) | 779 | list_for_each_entry(vlan, &port->vlans, list) |
| @@ -767,14 +807,12 @@ static int __init macvlan_init_module(void) | |||
| 767 | int err; | 807 | int err; |
| 768 | 808 | ||
| 769 | register_netdevice_notifier(&macvlan_notifier_block); | 809 | register_netdevice_notifier(&macvlan_notifier_block); |
| 770 | macvlan_handle_frame_hook = macvlan_handle_frame; | ||
| 771 | 810 | ||
| 772 | err = macvlan_link_register(&macvlan_link_ops); | 811 | err = macvlan_link_register(&macvlan_link_ops); |
| 773 | if (err < 0) | 812 | if (err < 0) |
| 774 | goto err1; | 813 | goto err1; |
| 775 | return 0; | 814 | return 0; |
| 776 | err1: | 815 | err1: |
| 777 | macvlan_handle_frame_hook = NULL; | ||
| 778 | unregister_netdevice_notifier(&macvlan_notifier_block); | 816 | unregister_netdevice_notifier(&macvlan_notifier_block); |
| 779 | return err; | 817 | return err; |
| 780 | } | 818 | } |
| @@ -782,7 +820,6 @@ err1: | |||
| 782 | static void __exit macvlan_cleanup_module(void) | 820 | static void __exit macvlan_cleanup_module(void) |
| 783 | { | 821 | { |
| 784 | rtnl_link_unregister(&macvlan_link_ops); | 822 | rtnl_link_unregister(&macvlan_link_ops); |
| 785 | macvlan_handle_frame_hook = NULL; | ||
| 786 | unregister_netdevice_notifier(&macvlan_notifier_block); | 823 | unregister_netdevice_notifier(&macvlan_notifier_block); |
| 787 | } | 824 | } |
| 788 | 825 | ||
