diff options
Diffstat (limited to 'drivers/net/ipvlan/ipvlan_main.c')
| -rw-r--r-- | drivers/net/ipvlan/ipvlan_main.c | 104 |
1 files changed, 55 insertions, 49 deletions
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index a266aa435d4d..30cb803e2fe5 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c | |||
| @@ -107,16 +107,6 @@ static int ipvlan_port_create(struct net_device *dev) | |||
| 107 | struct ipvl_port *port; | 107 | struct ipvl_port *port; |
| 108 | int err, idx; | 108 | int err, idx; |
| 109 | 109 | ||
| 110 | if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) { | ||
| 111 | netdev_err(dev, "Master is either lo or non-ether device\n"); | ||
| 112 | return -EINVAL; | ||
| 113 | } | ||
| 114 | |||
| 115 | if (netdev_is_rx_handler_busy(dev)) { | ||
| 116 | netdev_err(dev, "Device is already in use.\n"); | ||
| 117 | return -EBUSY; | ||
| 118 | } | ||
| 119 | |||
| 120 | port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL); | 110 | port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL); |
| 121 | if (!port) | 111 | if (!port) |
| 122 | return -ENOMEM; | 112 | return -ENOMEM; |
| @@ -179,8 +169,9 @@ static void ipvlan_port_destroy(struct net_device *dev) | |||
| 179 | static int ipvlan_init(struct net_device *dev) | 169 | static int ipvlan_init(struct net_device *dev) |
| 180 | { | 170 | { |
| 181 | struct ipvl_dev *ipvlan = netdev_priv(dev); | 171 | struct ipvl_dev *ipvlan = netdev_priv(dev); |
| 182 | const struct net_device *phy_dev = ipvlan->phy_dev; | 172 | struct net_device *phy_dev = ipvlan->phy_dev; |
| 183 | struct ipvl_port *port = ipvlan->port; | 173 | struct ipvl_port *port; |
| 174 | int err; | ||
| 184 | 175 | ||
| 185 | dev->state = (dev->state & ~IPVLAN_STATE_MASK) | | 176 | dev->state = (dev->state & ~IPVLAN_STATE_MASK) | |
| 186 | (phy_dev->state & IPVLAN_STATE_MASK); | 177 | (phy_dev->state & IPVLAN_STATE_MASK); |
| @@ -196,18 +187,27 @@ static int ipvlan_init(struct net_device *dev) | |||
| 196 | if (!ipvlan->pcpu_stats) | 187 | if (!ipvlan->pcpu_stats) |
| 197 | return -ENOMEM; | 188 | return -ENOMEM; |
| 198 | 189 | ||
| 190 | if (!netif_is_ipvlan_port(phy_dev)) { | ||
| 191 | err = ipvlan_port_create(phy_dev); | ||
| 192 | if (err < 0) { | ||
| 193 | free_percpu(ipvlan->pcpu_stats); | ||
| 194 | return err; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | port = ipvlan_port_get_rtnl(phy_dev); | ||
| 199 | port->count += 1; | 198 | port->count += 1; |
| 200 | |||
| 201 | return 0; | 199 | return 0; |
| 202 | } | 200 | } |
| 203 | 201 | ||
| 204 | static void ipvlan_uninit(struct net_device *dev) | 202 | static void ipvlan_uninit(struct net_device *dev) |
| 205 | { | 203 | { |
| 206 | struct ipvl_dev *ipvlan = netdev_priv(dev); | 204 | struct ipvl_dev *ipvlan = netdev_priv(dev); |
| 207 | struct ipvl_port *port = ipvlan->port; | 205 | struct net_device *phy_dev = ipvlan->phy_dev; |
| 206 | struct ipvl_port *port; | ||
| 208 | 207 | ||
| 209 | free_percpu(ipvlan->pcpu_stats); | 208 | free_percpu(ipvlan->pcpu_stats); |
| 210 | 209 | ||
| 210 | port = ipvlan_port_get_rtnl(phy_dev); | ||
| 211 | port->count -= 1; | 211 | port->count -= 1; |
| 212 | if (!port->count) | 212 | if (!port->count) |
| 213 | ipvlan_port_destroy(port->dev); | 213 | ipvlan_port_destroy(port->dev); |
| @@ -554,7 +554,6 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
| 554 | struct net_device *phy_dev; | 554 | struct net_device *phy_dev; |
| 555 | int err; | 555 | int err; |
| 556 | u16 mode = IPVLAN_MODE_L3; | 556 | u16 mode = IPVLAN_MODE_L3; |
| 557 | bool create = false; | ||
| 558 | 557 | ||
| 559 | if (!tb[IFLA_LINK]) | 558 | if (!tb[IFLA_LINK]) |
| 560 | return -EINVAL; | 559 | return -EINVAL; |
| @@ -568,28 +567,41 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
| 568 | 567 | ||
| 569 | phy_dev = tmp->phy_dev; | 568 | phy_dev = tmp->phy_dev; |
| 570 | } else if (!netif_is_ipvlan_port(phy_dev)) { | 569 | } else if (!netif_is_ipvlan_port(phy_dev)) { |
| 571 | err = ipvlan_port_create(phy_dev); | 570 | /* Exit early if the underlying link is invalid or busy */ |
| 572 | if (err < 0) | 571 | if (phy_dev->type != ARPHRD_ETHER || |
| 573 | return err; | 572 | phy_dev->flags & IFF_LOOPBACK) { |
| 574 | create = true; | 573 | netdev_err(phy_dev, |
| 575 | } | 574 | "Master is either lo or non-ether device\n"); |
| 575 | return -EINVAL; | ||
| 576 | } | ||
| 576 | 577 | ||
| 577 | if (data && data[IFLA_IPVLAN_MODE]) | 578 | if (netdev_is_rx_handler_busy(phy_dev)) { |
| 578 | mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); | 579 | netdev_err(phy_dev, "Device is already in use.\n"); |
| 580 | return -EBUSY; | ||
| 581 | } | ||
| 582 | } | ||
| 579 | 583 | ||
| 580 | port = ipvlan_port_get_rtnl(phy_dev); | ||
| 581 | ipvlan->phy_dev = phy_dev; | 584 | ipvlan->phy_dev = phy_dev; |
| 582 | ipvlan->dev = dev; | 585 | ipvlan->dev = dev; |
| 583 | ipvlan->port = port; | ||
| 584 | ipvlan->sfeatures = IPVLAN_FEATURES; | 586 | ipvlan->sfeatures = IPVLAN_FEATURES; |
| 585 | ipvlan_adjust_mtu(ipvlan, phy_dev); | 587 | ipvlan_adjust_mtu(ipvlan, phy_dev); |
| 586 | INIT_LIST_HEAD(&ipvlan->addrs); | 588 | INIT_LIST_HEAD(&ipvlan->addrs); |
| 587 | 589 | ||
| 588 | /* Flags are per port and latest update overrides. User has | 590 | /* TODO Probably put random address here to be presented to the |
| 589 | * to be consistent in setting it just like the mode attribute. | 591 | * world but keep using the physical-dev address for the outgoing |
| 592 | * packets. | ||
| 590 | */ | 593 | */ |
| 591 | if (data && data[IFLA_IPVLAN_FLAGS]) | 594 | memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); |
| 592 | ipvlan->port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); | 595 | |
| 596 | dev->priv_flags |= IFF_IPVLAN_SLAVE; | ||
| 597 | |||
| 598 | err = register_netdevice(dev); | ||
| 599 | if (err < 0) | ||
| 600 | return err; | ||
| 601 | |||
| 602 | /* ipvlan_init() would have created the port, if required */ | ||
| 603 | port = ipvlan_port_get_rtnl(phy_dev); | ||
| 604 | ipvlan->port = port; | ||
| 593 | 605 | ||
| 594 | /* If the port-id base is at the MAX value, then wrap it around and | 606 | /* If the port-id base is at the MAX value, then wrap it around and |
| 595 | * begin from 0x1 again. This may be due to a busy system where lots | 607 | * begin from 0x1 again. This may be due to a busy system where lots |
| @@ -609,31 +621,28 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
| 609 | err = ida_simple_get(&port->ida, 0x1, port->dev_id_start, | 621 | err = ida_simple_get(&port->ida, 0x1, port->dev_id_start, |
| 610 | GFP_KERNEL); | 622 | GFP_KERNEL); |
| 611 | if (err < 0) | 623 | if (err < 0) |
| 612 | goto destroy_ipvlan_port; | 624 | goto unregister_netdev; |
| 613 | dev->dev_id = err; | 625 | dev->dev_id = err; |
| 626 | |||
| 614 | /* Increment id-base to the next slot for the future assignment */ | 627 | /* Increment id-base to the next slot for the future assignment */ |
| 615 | port->dev_id_start = err + 1; | 628 | port->dev_id_start = err + 1; |
| 616 | 629 | ||
| 617 | /* TODO Probably put random address here to be presented to the | 630 | err = netdev_upper_dev_link(phy_dev, dev, extack); |
| 618 | * world but keep using the physical-dev address for the outgoing | 631 | if (err) |
| 619 | * packets. | 632 | goto remove_ida; |
| 620 | */ | ||
| 621 | memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); | ||
| 622 | 633 | ||
| 623 | dev->priv_flags |= IFF_IPVLAN_SLAVE; | 634 | /* Flags are per port and latest update overrides. User has |
| 635 | * to be consistent in setting it just like the mode attribute. | ||
| 636 | */ | ||
| 637 | if (data && data[IFLA_IPVLAN_FLAGS]) | ||
| 638 | port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); | ||
| 624 | 639 | ||
| 625 | err = register_netdevice(dev); | 640 | if (data && data[IFLA_IPVLAN_MODE]) |
| 626 | if (err < 0) | 641 | mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); |
| 627 | goto remove_ida; | ||
| 628 | 642 | ||
| 629 | err = netdev_upper_dev_link(phy_dev, dev, extack); | ||
| 630 | if (err) { | ||
| 631 | goto unregister_netdev; | ||
| 632 | } | ||
| 633 | err = ipvlan_set_port_mode(port, mode); | 643 | err = ipvlan_set_port_mode(port, mode); |
| 634 | if (err) { | 644 | if (err) |
| 635 | goto unlink_netdev; | 645 | goto unlink_netdev; |
| 636 | } | ||
| 637 | 646 | ||
| 638 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); | 647 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); |
| 639 | netif_stacked_transfer_operstate(phy_dev, dev); | 648 | netif_stacked_transfer_operstate(phy_dev, dev); |
| @@ -641,13 +650,10 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
| 641 | 650 | ||
| 642 | unlink_netdev: | 651 | unlink_netdev: |
| 643 | netdev_upper_dev_unlink(phy_dev, dev); | 652 | netdev_upper_dev_unlink(phy_dev, dev); |
| 644 | unregister_netdev: | ||
| 645 | unregister_netdevice(dev); | ||
| 646 | remove_ida: | 653 | remove_ida: |
| 647 | ida_simple_remove(&port->ida, dev->dev_id); | 654 | ida_simple_remove(&port->ida, dev->dev_id); |
| 648 | destroy_ipvlan_port: | 655 | unregister_netdev: |
| 649 | if (create) | 656 | unregister_netdevice(dev); |
| 650 | ipvlan_port_destroy(phy_dev); | ||
| 651 | return err; | 657 | return err; |
| 652 | } | 658 | } |
| 653 | EXPORT_SYMBOL_GPL(ipvlan_link_new); | 659 | EXPORT_SYMBOL_GPL(ipvlan_link_new); |
