diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum.c')
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1072 |
1 files changed, 799 insertions, 273 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d23948b88962..c812513e079d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c | |||
| @@ -51,6 +51,7 @@ | |||
| 51 | #include <linux/list.h> | 51 | #include <linux/list.h> |
| 52 | #include <linux/notifier.h> | 52 | #include <linux/notifier.h> |
| 53 | #include <linux/dcbnl.h> | 53 | #include <linux/dcbnl.h> |
| 54 | #include <linux/inetdevice.h> | ||
| 54 | #include <net/switchdev.h> | 55 | #include <net/switchdev.h> |
| 55 | #include <generated/utsrelease.h> | 56 | #include <generated/utsrelease.h> |
| 56 | 57 | ||
| @@ -210,23 +211,6 @@ static int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port) | |||
| 210 | return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr); | 211 | return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr); |
| 211 | } | 212 | } |
| 212 | 213 | ||
| 213 | static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, | ||
| 214 | u16 vid, enum mlxsw_reg_spms_state state) | ||
| 215 | { | ||
| 216 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | ||
| 217 | char *spms_pl; | ||
| 218 | int err; | ||
| 219 | |||
| 220 | spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); | ||
| 221 | if (!spms_pl) | ||
| 222 | return -ENOMEM; | ||
| 223 | mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port); | ||
| 224 | mlxsw_reg_spms_vid_pack(spms_pl, vid, state); | ||
| 225 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl); | ||
| 226 | kfree(spms_pl); | ||
| 227 | return err; | ||
| 228 | } | ||
| 229 | |||
| 230 | static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) | 214 | static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) |
| 231 | { | 215 | { |
| 232 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | 216 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
| @@ -409,7 +393,11 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, | |||
| 409 | } | 393 | } |
| 410 | 394 | ||
| 411 | mlxsw_sp_txhdr_construct(skb, &tx_info); | 395 | mlxsw_sp_txhdr_construct(skb, &tx_info); |
| 412 | len = skb->len; | 396 | /* TX header is consumed by HW on the way so we shouldn't count its |
| 397 | * bytes as being sent. | ||
| 398 | */ | ||
| 399 | len = skb->len - MLXSW_TXHDR_LEN; | ||
| 400 | |||
| 413 | /* Due to a race we might fail here because of a full queue. In that | 401 | /* Due to a race we might fail here because of a full queue. In that |
| 414 | * unlikely case we simply drop the packet. | 402 | * unlikely case we simply drop the packet. |
| 415 | */ | 403 | */ |
| @@ -633,87 +621,6 @@ static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) | |||
| 633 | return 0; | 621 | return 0; |
| 634 | } | 622 | } |
| 635 | 623 | ||
| 636 | static struct mlxsw_sp_fid * | ||
| 637 | mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, u16 vid) | ||
| 638 | { | ||
| 639 | struct mlxsw_sp_fid *f; | ||
| 640 | |||
| 641 | list_for_each_entry(f, &mlxsw_sp->port_vfids.list, list) { | ||
| 642 | if (f->vid == vid) | ||
| 643 | return f; | ||
| 644 | } | ||
| 645 | |||
| 646 | return NULL; | ||
| 647 | } | ||
| 648 | |||
| 649 | static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp) | ||
| 650 | { | ||
| 651 | return find_first_zero_bit(mlxsw_sp->port_vfids.mapped, | ||
| 652 | MLXSW_SP_VFID_PORT_MAX); | ||
| 653 | } | ||
| 654 | |||
| 655 | static int mlxsw_sp_vfid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create) | ||
| 656 | { | ||
| 657 | char sfmr_pl[MLXSW_REG_SFMR_LEN]; | ||
| 658 | |||
| 659 | mlxsw_reg_sfmr_pack(sfmr_pl, !create, fid, 0); | ||
| 660 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); | ||
| 661 | } | ||
| 662 | |||
| 663 | static void mlxsw_sp_vport_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport); | ||
| 664 | |||
| 665 | static struct mlxsw_sp_fid *mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, | ||
| 666 | u16 vid) | ||
| 667 | { | ||
| 668 | struct device *dev = mlxsw_sp->bus_info->dev; | ||
| 669 | struct mlxsw_sp_fid *f; | ||
| 670 | u16 vfid, fid; | ||
| 671 | int err; | ||
| 672 | |||
| 673 | vfid = mlxsw_sp_avail_vfid_get(mlxsw_sp); | ||
| 674 | if (vfid == MLXSW_SP_VFID_PORT_MAX) { | ||
| 675 | dev_err(dev, "No available vFIDs\n"); | ||
| 676 | return ERR_PTR(-ERANGE); | ||
| 677 | } | ||
| 678 | |||
| 679 | fid = mlxsw_sp_vfid_to_fid(vfid); | ||
| 680 | err = mlxsw_sp_vfid_op(mlxsw_sp, fid, true); | ||
| 681 | if (err) { | ||
| 682 | dev_err(dev, "Failed to create FID=%d\n", fid); | ||
| 683 | return ERR_PTR(err); | ||
| 684 | } | ||
| 685 | |||
| 686 | f = kzalloc(sizeof(*f), GFP_KERNEL); | ||
| 687 | if (!f) | ||
| 688 | goto err_allocate_vfid; | ||
| 689 | |||
| 690 | f->leave = mlxsw_sp_vport_vfid_leave; | ||
| 691 | f->fid = fid; | ||
| 692 | f->vid = vid; | ||
| 693 | |||
| 694 | list_add(&f->list, &mlxsw_sp->port_vfids.list); | ||
| 695 | set_bit(vfid, mlxsw_sp->port_vfids.mapped); | ||
| 696 | |||
| 697 | return f; | ||
| 698 | |||
| 699 | err_allocate_vfid: | ||
| 700 | mlxsw_sp_vfid_op(mlxsw_sp, fid, false); | ||
| 701 | return ERR_PTR(-ENOMEM); | ||
| 702 | } | ||
| 703 | |||
| 704 | static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, | ||
| 705 | struct mlxsw_sp_fid *f) | ||
| 706 | { | ||
| 707 | u16 vfid = mlxsw_sp_fid_to_vfid(f->fid); | ||
| 708 | |||
| 709 | clear_bit(vfid, mlxsw_sp->port_vfids.mapped); | ||
| 710 | list_del(&f->list); | ||
| 711 | |||
| 712 | mlxsw_sp_vfid_op(mlxsw_sp, f->fid, false); | ||
| 713 | |||
| 714 | kfree(f); | ||
| 715 | } | ||
| 716 | |||
| 717 | static struct mlxsw_sp_port * | 624 | static struct mlxsw_sp_port * |
| 718 | mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) | 625 | mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) |
| 719 | { | 626 | { |
| @@ -746,72 +653,12 @@ static void mlxsw_sp_port_vport_destroy(struct mlxsw_sp_port *mlxsw_sp_vport) | |||
| 746 | kfree(mlxsw_sp_vport); | 653 | kfree(mlxsw_sp_vport); |
| 747 | } | 654 | } |
| 748 | 655 | ||
| 749 | static int mlxsw_sp_vport_fid_map(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid, | ||
| 750 | bool valid) | ||
| 751 | { | ||
| 752 | enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; | ||
| 753 | u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); | ||
| 754 | |||
| 755 | return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, mt, valid, fid, | ||
| 756 | vid); | ||
| 757 | } | ||
| 758 | |||
| 759 | static int mlxsw_sp_vport_vfid_join(struct mlxsw_sp_port *mlxsw_sp_vport) | ||
| 760 | { | ||
| 761 | u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); | ||
| 762 | struct mlxsw_sp_fid *f; | ||
| 763 | int err; | ||
| 764 | |||
| 765 | f = mlxsw_sp_vfid_find(mlxsw_sp_vport->mlxsw_sp, vid); | ||
| 766 | if (!f) { | ||
| 767 | f = mlxsw_sp_vfid_create(mlxsw_sp_vport->mlxsw_sp, vid); | ||
| 768 | if (IS_ERR(f)) | ||
| 769 | return PTR_ERR(f); | ||
| 770 | } | ||
| 771 | |||
| 772 | if (!f->ref_count) { | ||
| 773 | err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, true); | ||
| 774 | if (err) | ||
| 775 | goto err_vport_flood_set; | ||
| 776 | } | ||
| 777 | |||
| 778 | err = mlxsw_sp_vport_fid_map(mlxsw_sp_vport, f->fid, true); | ||
| 779 | if (err) | ||
| 780 | goto err_vport_fid_map; | ||
| 781 | |||
| 782 | mlxsw_sp_vport_fid_set(mlxsw_sp_vport, f); | ||
| 783 | f->ref_count++; | ||
| 784 | |||
| 785 | return 0; | ||
| 786 | |||
| 787 | err_vport_fid_map: | ||
| 788 | if (!f->ref_count) | ||
| 789 | mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, false); | ||
| 790 | err_vport_flood_set: | ||
| 791 | if (!f->ref_count) | ||
| 792 | mlxsw_sp_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f); | ||
| 793 | return err; | ||
| 794 | } | ||
| 795 | |||
| 796 | static void mlxsw_sp_vport_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport) | ||
| 797 | { | ||
| 798 | struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); | ||
| 799 | |||
| 800 | mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); | ||
| 801 | |||
| 802 | mlxsw_sp_vport_fid_map(mlxsw_sp_vport, f->fid, false); | ||
| 803 | |||
| 804 | if (--f->ref_count == 0) { | ||
| 805 | mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, false); | ||
| 806 | mlxsw_sp_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f); | ||
| 807 | } | ||
| 808 | } | ||
| 809 | |||
| 810 | int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, | 656 | int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, |
| 811 | u16 vid) | 657 | u16 vid) |
| 812 | { | 658 | { |
| 813 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); | 659 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| 814 | struct mlxsw_sp_port *mlxsw_sp_vport; | 660 | struct mlxsw_sp_port *mlxsw_sp_vport; |
| 661 | bool untagged = vid == 1; | ||
| 815 | int err; | 662 | int err; |
| 816 | 663 | ||
| 817 | /* VLAN 0 is added to HW filter when device goes up, but it is | 664 | /* VLAN 0 is added to HW filter when device goes up, but it is |
| @@ -843,41 +690,24 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, | |||
| 843 | } | 690 | } |
| 844 | } | 691 | } |
| 845 | 692 | ||
| 846 | err = mlxsw_sp_vport_vfid_join(mlxsw_sp_vport); | ||
| 847 | if (err) { | ||
| 848 | netdev_err(dev, "Failed to join vFID\n"); | ||
| 849 | goto err_vport_vfid_join; | ||
| 850 | } | ||
| 851 | |||
| 852 | err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); | 693 | err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); |
| 853 | if (err) { | 694 | if (err) { |
| 854 | netdev_err(dev, "Failed to disable learning for VID=%d\n", vid); | 695 | netdev_err(dev, "Failed to disable learning for VID=%d\n", vid); |
| 855 | goto err_port_vid_learning_set; | 696 | goto err_port_vid_learning_set; |
| 856 | } | 697 | } |
| 857 | 698 | ||
| 858 | err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, false); | 699 | err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, untagged); |
| 859 | if (err) { | 700 | if (err) { |
| 860 | netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", | 701 | netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", |
| 861 | vid); | 702 | vid); |
| 862 | goto err_port_add_vid; | 703 | goto err_port_add_vid; |
| 863 | } | 704 | } |
| 864 | 705 | ||
| 865 | err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid, | ||
| 866 | MLXSW_REG_SPMS_STATE_FORWARDING); | ||
| 867 | if (err) { | ||
| 868 | netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); | ||
| 869 | goto err_port_stp_state_set; | ||
| 870 | } | ||
| 871 | |||
| 872 | return 0; | 706 | return 0; |
| 873 | 707 | ||
| 874 | err_port_stp_state_set: | ||
| 875 | mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false); | ||
| 876 | err_port_add_vid: | 708 | err_port_add_vid: |
| 877 | mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); | 709 | mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); |
| 878 | err_port_vid_learning_set: | 710 | err_port_vid_learning_set: |
| 879 | mlxsw_sp_vport_vfid_leave(mlxsw_sp_vport); | ||
| 880 | err_vport_vfid_join: | ||
| 881 | if (list_is_singular(&mlxsw_sp_port->vports_list)) | 711 | if (list_is_singular(&mlxsw_sp_port->vports_list)) |
| 882 | mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); | 712 | mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); |
| 883 | err_port_vp_mode_trans: | 713 | err_port_vp_mode_trans: |
| @@ -885,8 +715,8 @@ err_port_vp_mode_trans: | |||
| 885 | return err; | 715 | return err; |
| 886 | } | 716 | } |
| 887 | 717 | ||
| 888 | int mlxsw_sp_port_kill_vid(struct net_device *dev, | 718 | static int mlxsw_sp_port_kill_vid(struct net_device *dev, |
| 889 | __be16 __always_unused proto, u16 vid) | 719 | __be16 __always_unused proto, u16 vid) |
| 890 | { | 720 | { |
| 891 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); | 721 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
| 892 | struct mlxsw_sp_port *mlxsw_sp_vport; | 722 | struct mlxsw_sp_port *mlxsw_sp_vport; |
| @@ -905,13 +735,6 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, | |||
| 905 | return 0; | 735 | return 0; |
| 906 | } | 736 | } |
| 907 | 737 | ||
| 908 | err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid, | ||
| 909 | MLXSW_REG_SPMS_STATE_DISCARDING); | ||
| 910 | if (err) { | ||
| 911 | netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); | ||
| 912 | return err; | ||
| 913 | } | ||
| 914 | |||
| 915 | err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false); | 738 | err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false); |
| 916 | if (err) { | 739 | if (err) { |
| 917 | netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", | 740 | netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", |
| @@ -980,6 +803,8 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { | |||
| 980 | .ndo_get_stats64 = mlxsw_sp_port_get_stats64, | 803 | .ndo_get_stats64 = mlxsw_sp_port_get_stats64, |
| 981 | .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, | 804 | .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, |
| 982 | .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, | 805 | .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, |
| 806 | .ndo_neigh_construct = mlxsw_sp_router_neigh_construct, | ||
| 807 | .ndo_neigh_destroy = mlxsw_sp_router_neigh_destroy, | ||
| 983 | .ndo_fdb_add = switchdev_port_fdb_add, | 808 | .ndo_fdb_add = switchdev_port_fdb_add, |
| 984 | .ndo_fdb_del = switchdev_port_fdb_del, | 809 | .ndo_fdb_del = switchdev_port_fdb_del, |
| 985 | .ndo_fdb_dump = switchdev_port_fdb_dump, | 810 | .ndo_fdb_dump = switchdev_port_fdb_dump, |
| @@ -1840,23 +1665,6 @@ err_port_active_vlans_alloc: | |||
| 1840 | return err; | 1665 | return err; |
| 1841 | } | 1666 | } |
| 1842 | 1667 | ||
| 1843 | static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port) | ||
| 1844 | { | ||
| 1845 | struct net_device *dev = mlxsw_sp_port->dev; | ||
| 1846 | struct mlxsw_sp_port *mlxsw_sp_vport, *tmp; | ||
| 1847 | |||
| 1848 | list_for_each_entry_safe(mlxsw_sp_vport, tmp, | ||
| 1849 | &mlxsw_sp_port->vports_list, vport.list) { | ||
| 1850 | u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); | ||
| 1851 | |||
| 1852 | /* vPorts created for VLAN devices should already be gone | ||
| 1853 | * by now, since we unregistered the port netdev. | ||
| 1854 | */ | ||
| 1855 | WARN_ON(is_vlan_dev(mlxsw_sp_vport->dev)); | ||
| 1856 | mlxsw_sp_port_kill_vid(dev, 0, vid); | ||
| 1857 | } | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) | 1668 | static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) |
| 1861 | { | 1669 | { |
| 1862 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; | 1670 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; |
| @@ -1867,13 +1675,14 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) | |||
| 1867 | mlxsw_core_port_fini(&mlxsw_sp_port->core_port); | 1675 | mlxsw_core_port_fini(&mlxsw_sp_port->core_port); |
| 1868 | unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ | 1676 | unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ |
| 1869 | mlxsw_sp_port_dcb_fini(mlxsw_sp_port); | 1677 | mlxsw_sp_port_dcb_fini(mlxsw_sp_port); |
| 1870 | mlxsw_sp_port_vports_fini(mlxsw_sp_port); | 1678 | mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1); |
| 1871 | mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); | 1679 | mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); |
| 1872 | mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); | 1680 | mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT); |
| 1873 | mlxsw_sp_port_module_unmap(mlxsw_sp, mlxsw_sp_port->local_port); | 1681 | mlxsw_sp_port_module_unmap(mlxsw_sp, mlxsw_sp_port->local_port); |
| 1874 | free_percpu(mlxsw_sp_port->pcpu_stats); | 1682 | free_percpu(mlxsw_sp_port->pcpu_stats); |
| 1875 | kfree(mlxsw_sp_port->untagged_vlans); | 1683 | kfree(mlxsw_sp_port->untagged_vlans); |
| 1876 | kfree(mlxsw_sp_port->active_vlans); | 1684 | kfree(mlxsw_sp_port->active_vlans); |
| 1685 | WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vports_list)); | ||
| 1877 | free_netdev(mlxsw_sp_port->dev); | 1686 | free_netdev(mlxsw_sp_port->dev); |
| 1878 | } | 1687 | } |
| 1879 | 1688 | ||
| @@ -2110,11 +1919,8 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, | |||
| 2110 | 1919 | ||
| 2111 | local_port = mlxsw_reg_pude_local_port_get(pude_pl); | 1920 | local_port = mlxsw_reg_pude_local_port_get(pude_pl); |
| 2112 | mlxsw_sp_port = mlxsw_sp->ports[local_port]; | 1921 | mlxsw_sp_port = mlxsw_sp->ports[local_port]; |
| 2113 | if (!mlxsw_sp_port) { | 1922 | if (!mlxsw_sp_port) |
| 2114 | dev_warn(mlxsw_sp->bus_info->dev, "Port %d: Link event received for non-existent port\n", | ||
| 2115 | local_port); | ||
| 2116 | return; | 1923 | return; |
| 2117 | } | ||
| 2118 | 1924 | ||
| 2119 | status = mlxsw_reg_pude_oper_status_get(pude_pl); | 1925 | status = mlxsw_reg_pude_oper_status_get(pude_pl); |
| 2120 | if (status == MLXSW_PORT_OPER_STATUS_UP) { | 1926 | if (status == MLXSW_PORT_OPER_STATUS_UP) { |
| @@ -2269,6 +2075,31 @@ static const struct mlxsw_rx_listener mlxsw_sp_rx_listener[] = { | |||
| 2269 | .local_port = MLXSW_PORT_DONT_CARE, | 2075 | .local_port = MLXSW_PORT_DONT_CARE, |
| 2270 | .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT, | 2076 | .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT, |
| 2271 | }, | 2077 | }, |
| 2078 | { | ||
| 2079 | .func = mlxsw_sp_rx_listener_func, | ||
| 2080 | .local_port = MLXSW_PORT_DONT_CARE, | ||
| 2081 | .trap_id = MLXSW_TRAP_ID_ARPBC, | ||
| 2082 | }, | ||
| 2083 | { | ||
| 2084 | .func = mlxsw_sp_rx_listener_func, | ||
| 2085 | .local_port = MLXSW_PORT_DONT_CARE, | ||
| 2086 | .trap_id = MLXSW_TRAP_ID_ARPUC, | ||
| 2087 | }, | ||
| 2088 | { | ||
| 2089 | .func = mlxsw_sp_rx_listener_func, | ||
| 2090 | .local_port = MLXSW_PORT_DONT_CARE, | ||
| 2091 | .trap_id = MLXSW_TRAP_ID_IP2ME, | ||
| 2092 | }, | ||
| 2093 | { | ||
| 2094 | .func = mlxsw_sp_rx_listener_func, | ||
| 2095 | .local_port = MLXSW_PORT_DONT_CARE, | ||
| 2096 | .trap_id = MLXSW_TRAP_ID_RTR_INGRESS0, | ||
| 2097 | }, | ||
| 2098 | { | ||
| 2099 | .func = mlxsw_sp_rx_listener_func, | ||
| 2100 | .local_port = MLXSW_PORT_DONT_CARE, | ||
| 2101 | .trap_id = MLXSW_TRAP_ID_HOST_MISS_IPV4, | ||
| 2102 | }, | ||
| 2272 | }; | 2103 | }; |
| 2273 | 2104 | ||
| 2274 | static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) | 2105 | static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) |
| @@ -2309,7 +2140,7 @@ err_rx_trap_set: | |||
| 2309 | mlxsw_sp); | 2140 | mlxsw_sp); |
| 2310 | err_rx_listener_register: | 2141 | err_rx_listener_register: |
| 2311 | for (i--; i >= 0; i--) { | 2142 | for (i--; i >= 0; i--) { |
| 2312 | mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, | 2143 | mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, |
| 2313 | mlxsw_sp_rx_listener[i].trap_id); | 2144 | mlxsw_sp_rx_listener[i].trap_id); |
| 2314 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); | 2145 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); |
| 2315 | 2146 | ||
| @@ -2326,7 +2157,7 @@ static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) | |||
| 2326 | int i; | 2157 | int i; |
| 2327 | 2158 | ||
| 2328 | for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { | 2159 | for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { |
| 2329 | mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, | 2160 | mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, |
| 2330 | mlxsw_sp_rx_listener[i].trap_id); | 2161 | mlxsw_sp_rx_listener[i].trap_id); |
| 2331 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); | 2162 | mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); |
| 2332 | 2163 | ||
| @@ -2406,8 +2237,7 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, | |||
| 2406 | mlxsw_sp->core = mlxsw_core; | 2237 | mlxsw_sp->core = mlxsw_core; |
| 2407 | mlxsw_sp->bus_info = mlxsw_bus_info; | 2238 | mlxsw_sp->bus_info = mlxsw_bus_info; |
| 2408 | INIT_LIST_HEAD(&mlxsw_sp->fids); | 2239 | INIT_LIST_HEAD(&mlxsw_sp->fids); |
| 2409 | INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list); | 2240 | INIT_LIST_HEAD(&mlxsw_sp->vfids.list); |
| 2410 | INIT_LIST_HEAD(&mlxsw_sp->br_vfids.list); | ||
| 2411 | INIT_LIST_HEAD(&mlxsw_sp->br_mids.list); | 2241 | INIT_LIST_HEAD(&mlxsw_sp->br_mids.list); |
| 2412 | 2242 | ||
| 2413 | err = mlxsw_sp_base_mac_get(mlxsw_sp); | 2243 | err = mlxsw_sp_base_mac_get(mlxsw_sp); |
| @@ -2416,16 +2246,10 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, | |||
| 2416 | return err; | 2246 | return err; |
| 2417 | } | 2247 | } |
| 2418 | 2248 | ||
| 2419 | err = mlxsw_sp_ports_create(mlxsw_sp); | ||
| 2420 | if (err) { | ||
| 2421 | dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); | ||
| 2422 | return err; | ||
| 2423 | } | ||
| 2424 | |||
| 2425 | err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE); | 2249 | err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE); |
| 2426 | if (err) { | 2250 | if (err) { |
| 2427 | dev_err(mlxsw_sp->bus_info->dev, "Failed to register for PUDE events\n"); | 2251 | dev_err(mlxsw_sp->bus_info->dev, "Failed to register for PUDE events\n"); |
| 2428 | goto err_event_register; | 2252 | return err; |
| 2429 | } | 2253 | } |
| 2430 | 2254 | ||
| 2431 | err = mlxsw_sp_traps_init(mlxsw_sp); | 2255 | err = mlxsw_sp_traps_init(mlxsw_sp); |
| @@ -2458,8 +2282,24 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, | |||
| 2458 | goto err_switchdev_init; | 2282 | goto err_switchdev_init; |
| 2459 | } | 2283 | } |
| 2460 | 2284 | ||
| 2285 | err = mlxsw_sp_router_init(mlxsw_sp); | ||
| 2286 | if (err) { | ||
| 2287 | dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize router\n"); | ||
| 2288 | goto err_router_init; | ||
| 2289 | } | ||
| 2290 | |||
| 2291 | err = mlxsw_sp_ports_create(mlxsw_sp); | ||
| 2292 | if (err) { | ||
| 2293 | dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); | ||
| 2294 | goto err_ports_create; | ||
| 2295 | } | ||
| 2296 | |||
| 2461 | return 0; | 2297 | return 0; |
| 2462 | 2298 | ||
| 2299 | err_ports_create: | ||
| 2300 | mlxsw_sp_router_fini(mlxsw_sp); | ||
| 2301 | err_router_init: | ||
| 2302 | mlxsw_sp_switchdev_fini(mlxsw_sp); | ||
| 2463 | err_switchdev_init: | 2303 | err_switchdev_init: |
| 2464 | err_lag_init: | 2304 | err_lag_init: |
| 2465 | mlxsw_sp_buffers_fini(mlxsw_sp); | 2305 | mlxsw_sp_buffers_fini(mlxsw_sp); |
| @@ -2468,21 +2308,24 @@ err_flood_init: | |||
| 2468 | mlxsw_sp_traps_fini(mlxsw_sp); | 2308 | mlxsw_sp_traps_fini(mlxsw_sp); |
| 2469 | err_rx_listener_register: | 2309 | err_rx_listener_register: |
| 2470 | mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); | 2310 | mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); |
| 2471 | err_event_register: | ||
| 2472 | mlxsw_sp_ports_remove(mlxsw_sp); | ||
| 2473 | return err; | 2311 | return err; |
| 2474 | } | 2312 | } |
| 2475 | 2313 | ||
| 2476 | static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) | 2314 | static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) |
| 2477 | { | 2315 | { |
| 2478 | struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); | 2316 | struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); |
| 2317 | int i; | ||
| 2479 | 2318 | ||
| 2319 | mlxsw_sp_ports_remove(mlxsw_sp); | ||
| 2320 | mlxsw_sp_router_fini(mlxsw_sp); | ||
| 2480 | mlxsw_sp_switchdev_fini(mlxsw_sp); | 2321 | mlxsw_sp_switchdev_fini(mlxsw_sp); |
| 2481 | mlxsw_sp_buffers_fini(mlxsw_sp); | 2322 | mlxsw_sp_buffers_fini(mlxsw_sp); |
| 2482 | mlxsw_sp_traps_fini(mlxsw_sp); | 2323 | mlxsw_sp_traps_fini(mlxsw_sp); |
| 2483 | mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); | 2324 | mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); |
| 2484 | mlxsw_sp_ports_remove(mlxsw_sp); | 2325 | WARN_ON(!list_empty(&mlxsw_sp->vfids.list)); |
| 2485 | WARN_ON(!list_empty(&mlxsw_sp->fids)); | 2326 | WARN_ON(!list_empty(&mlxsw_sp->fids)); |
| 2327 | for (i = 0; i < MLXSW_SP_RIF_MAX; i++) | ||
| 2328 | WARN_ON_ONCE(mlxsw_sp->rifs[i]); | ||
| 2486 | } | 2329 | } |
| 2487 | 2330 | ||
| 2488 | static struct mlxsw_config_profile mlxsw_sp_config_profile = { | 2331 | static struct mlxsw_config_profile mlxsw_sp_config_profile = { |
| @@ -2513,6 +2356,10 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = { | |||
| 2513 | .max_ib_mc = 0, | 2356 | .max_ib_mc = 0, |
| 2514 | .used_max_pkey = 1, | 2357 | .used_max_pkey = 1, |
| 2515 | .max_pkey = 0, | 2358 | .max_pkey = 0, |
| 2359 | .used_kvd_sizes = 1, | ||
| 2360 | .kvd_linear_size = MLXSW_SP_KVD_LINEAR_SIZE, | ||
| 2361 | .kvd_hash_single_size = MLXSW_SP_KVD_HASH_SINGLE_SIZE, | ||
| 2362 | .kvd_hash_double_size = MLXSW_SP_KVD_HASH_DOUBLE_SIZE, | ||
| 2516 | .swid_config = { | 2363 | .swid_config = { |
| 2517 | { | 2364 | { |
| 2518 | .used_type = 1, | 2365 | .used_type = 1, |
| @@ -2544,6 +2391,559 @@ static struct mlxsw_driver mlxsw_sp_driver = { | |||
| 2544 | .profile = &mlxsw_sp_config_profile, | 2391 | .profile = &mlxsw_sp_config_profile, |
| 2545 | }; | 2392 | }; |
| 2546 | 2393 | ||
| 2394 | static bool mlxsw_sp_port_dev_check(const struct net_device *dev) | ||
| 2395 | { | ||
| 2396 | return dev->netdev_ops == &mlxsw_sp_port_netdev_ops; | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev) | ||
| 2400 | { | ||
| 2401 | struct net_device *lower_dev; | ||
| 2402 | struct list_head *iter; | ||
| 2403 | |||
| 2404 | if (mlxsw_sp_port_dev_check(dev)) | ||
| 2405 | return netdev_priv(dev); | ||
| 2406 | |||
| 2407 | netdev_for_each_all_lower_dev(dev, lower_dev, iter) { | ||
| 2408 | if (mlxsw_sp_port_dev_check(lower_dev)) | ||
| 2409 | return netdev_priv(lower_dev); | ||
| 2410 | } | ||
| 2411 | return NULL; | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | static struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) | ||
| 2415 | { | ||
| 2416 | struct mlxsw_sp_port *mlxsw_sp_port; | ||
| 2417 | |||
| 2418 | mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev); | ||
| 2419 | return mlxsw_sp_port ? mlxsw_sp_port->mlxsw_sp : NULL; | ||
| 2420 | } | ||
| 2421 | |||
| 2422 | static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev) | ||
| 2423 | { | ||
| 2424 | struct net_device *lower_dev; | ||
| 2425 | struct list_head *iter; | ||
| 2426 | |||
| 2427 | if (mlxsw_sp_port_dev_check(dev)) | ||
| 2428 | return netdev_priv(dev); | ||
| 2429 | |||
| 2430 | netdev_for_each_all_lower_dev_rcu(dev, lower_dev, iter) { | ||
| 2431 | if (mlxsw_sp_port_dev_check(lower_dev)) | ||
| 2432 | return netdev_priv(lower_dev); | ||
| 2433 | } | ||
| 2434 | return NULL; | ||
| 2435 | } | ||
| 2436 | |||
| 2437 | struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev) | ||
| 2438 | { | ||
| 2439 | struct mlxsw_sp_port *mlxsw_sp_port; | ||
| 2440 | |||
| 2441 | rcu_read_lock(); | ||
| 2442 | mlxsw_sp_port = mlxsw_sp_port_dev_lower_find_rcu(dev); | ||
| 2443 | if (mlxsw_sp_port) | ||
| 2444 | dev_hold(mlxsw_sp_port->dev); | ||
| 2445 | rcu_read_unlock(); | ||
| 2446 | return mlxsw_sp_port; | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port) | ||
| 2450 | { | ||
| 2451 | dev_put(mlxsw_sp_port->dev); | ||
| 2452 | } | ||
| 2453 | |||
| 2454 | static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, | ||
| 2455 | unsigned long event) | ||
| 2456 | { | ||
| 2457 | switch (event) { | ||
| 2458 | case NETDEV_UP: | ||
| 2459 | if (!r) | ||
| 2460 | return true; | ||
| 2461 | r->ref_count++; | ||
| 2462 | return false; | ||
| 2463 | case NETDEV_DOWN: | ||
| 2464 | if (r && --r->ref_count == 0) | ||
| 2465 | return true; | ||
| 2466 | /* It is possible we already removed the RIF ourselves | ||
| 2467 | * if it was assigned to a netdev that is now a bridge | ||
| 2468 | * or LAG slave. | ||
| 2469 | */ | ||
| 2470 | return false; | ||
| 2471 | } | ||
| 2472 | |||
| 2473 | return false; | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp) | ||
| 2477 | { | ||
| 2478 | int i; | ||
| 2479 | |||
| 2480 | for (i = 0; i < MLXSW_SP_RIF_MAX; i++) | ||
| 2481 | if (!mlxsw_sp->rifs[i]) | ||
| 2482 | return i; | ||
| 2483 | |||
| 2484 | return MLXSW_SP_RIF_MAX; | ||
| 2485 | } | ||
| 2486 | |||
| 2487 | static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport, | ||
| 2488 | bool *p_lagged, u16 *p_system_port) | ||
| 2489 | { | ||
| 2490 | u8 local_port = mlxsw_sp_vport->local_port; | ||
| 2491 | |||
| 2492 | *p_lagged = mlxsw_sp_vport->lagged; | ||
| 2493 | *p_system_port = *p_lagged ? mlxsw_sp_vport->lag_id : local_port; | ||
| 2494 | } | ||
| 2495 | |||
| 2496 | static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport, | ||
| 2497 | struct net_device *l3_dev, u16 rif, | ||
| 2498 | bool create) | ||
| 2499 | { | ||
| 2500 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; | ||
| 2501 | bool lagged = mlxsw_sp_vport->lagged; | ||
| 2502 | char ritr_pl[MLXSW_REG_RITR_LEN]; | ||
| 2503 | u16 system_port; | ||
| 2504 | |||
| 2505 | mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif, | ||
| 2506 | l3_dev->mtu, l3_dev->dev_addr); | ||
| 2507 | |||
| 2508 | mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port); | ||
| 2509 | mlxsw_reg_ritr_sp_if_pack(ritr_pl, lagged, system_port, | ||
| 2510 | mlxsw_sp_vport_vid_get(mlxsw_sp_vport)); | ||
| 2511 | |||
| 2512 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | ||
| 2513 | } | ||
| 2514 | |||
| 2515 | static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport); | ||
| 2516 | |||
| 2517 | static struct mlxsw_sp_fid * | ||
| 2518 | mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev) | ||
| 2519 | { | ||
| 2520 | struct mlxsw_sp_fid *f; | ||
| 2521 | |||
| 2522 | f = kzalloc(sizeof(*f), GFP_KERNEL); | ||
| 2523 | if (!f) | ||
| 2524 | return NULL; | ||
| 2525 | |||
| 2526 | f->leave = mlxsw_sp_vport_rif_sp_leave; | ||
| 2527 | f->ref_count = 0; | ||
| 2528 | f->dev = l3_dev; | ||
| 2529 | f->fid = fid; | ||
| 2530 | |||
| 2531 | return f; | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | static struct mlxsw_sp_rif * | ||
| 2535 | mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f) | ||
| 2536 | { | ||
| 2537 | struct mlxsw_sp_rif *r; | ||
| 2538 | |||
| 2539 | r = kzalloc(sizeof(*r), GFP_KERNEL); | ||
| 2540 | if (!r) | ||
| 2541 | return NULL; | ||
| 2542 | |||
| 2543 | ether_addr_copy(r->addr, l3_dev->dev_addr); | ||
| 2544 | r->mtu = l3_dev->mtu; | ||
| 2545 | r->ref_count = 1; | ||
| 2546 | r->dev = l3_dev; | ||
| 2547 | r->rif = rif; | ||
| 2548 | r->f = f; | ||
| 2549 | |||
| 2550 | return r; | ||
| 2551 | } | ||
| 2552 | |||
| 2553 | static struct mlxsw_sp_rif * | ||
| 2554 | mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, | ||
| 2555 | struct net_device *l3_dev) | ||
| 2556 | { | ||
| 2557 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; | ||
| 2558 | struct mlxsw_sp_fid *f; | ||
| 2559 | struct mlxsw_sp_rif *r; | ||
| 2560 | u16 fid, rif; | ||
| 2561 | int err; | ||
| 2562 | |||
| 2563 | rif = mlxsw_sp_avail_rif_get(mlxsw_sp); | ||
| 2564 | if (rif == MLXSW_SP_RIF_MAX) | ||
| 2565 | return ERR_PTR(-ERANGE); | ||
| 2566 | |||
| 2567 | err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, true); | ||
| 2568 | if (err) | ||
| 2569 | return ERR_PTR(err); | ||
| 2570 | |||
| 2571 | fid = mlxsw_sp_rif_sp_to_fid(rif); | ||
| 2572 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true); | ||
| 2573 | if (err) | ||
| 2574 | goto err_rif_fdb_op; | ||
| 2575 | |||
| 2576 | f = mlxsw_sp_rfid_alloc(fid, l3_dev); | ||
| 2577 | if (!f) { | ||
| 2578 | err = -ENOMEM; | ||
| 2579 | goto err_rfid_alloc; | ||
| 2580 | } | ||
| 2581 | |||
| 2582 | r = mlxsw_sp_rif_alloc(rif, l3_dev, f); | ||
| 2583 | if (!r) { | ||
| 2584 | err = -ENOMEM; | ||
| 2585 | goto err_rif_alloc; | ||
| 2586 | } | ||
| 2587 | |||
| 2588 | f->r = r; | ||
| 2589 | mlxsw_sp->rifs[rif] = r; | ||
| 2590 | |||
| 2591 | return r; | ||
| 2592 | |||
| 2593 | err_rif_alloc: | ||
| 2594 | kfree(f); | ||
| 2595 | err_rfid_alloc: | ||
| 2596 | mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); | ||
| 2597 | err_rif_fdb_op: | ||
| 2598 | mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); | ||
| 2599 | return ERR_PTR(err); | ||
| 2600 | } | ||
| 2601 | |||
| 2602 | static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, | ||
| 2603 | struct mlxsw_sp_rif *r) | ||
| 2604 | { | ||
| 2605 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; | ||
| 2606 | struct net_device *l3_dev = r->dev; | ||
| 2607 | struct mlxsw_sp_fid *f = r->f; | ||
| 2608 | u16 fid = f->fid; | ||
| 2609 | u16 rif = r->rif; | ||
| 2610 | |||
| 2611 | mlxsw_sp->rifs[rif] = NULL; | ||
| 2612 | f->r = NULL; | ||
| 2613 | |||
| 2614 | kfree(r); | ||
| 2615 | |||
| 2616 | kfree(f); | ||
| 2617 | |||
| 2618 | mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); | ||
| 2619 | |||
| 2620 | mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); | ||
| 2621 | } | ||
| 2622 | |||
| 2623 | static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport, | ||
| 2624 | struct net_device *l3_dev) | ||
| 2625 | { | ||
| 2626 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; | ||
| 2627 | struct mlxsw_sp_rif *r; | ||
| 2628 | |||
| 2629 | r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); | ||
| 2630 | if (!r) { | ||
| 2631 | r = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev); | ||
| 2632 | if (IS_ERR(r)) | ||
| 2633 | return PTR_ERR(r); | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | mlxsw_sp_vport_fid_set(mlxsw_sp_vport, r->f); | ||
| 2637 | r->f->ref_count++; | ||
| 2638 | |||
| 2639 | netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", r->f->fid); | ||
| 2640 | |||
| 2641 | return 0; | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport) | ||
| 2645 | { | ||
| 2646 | struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); | ||
| 2647 | |||
| 2648 | netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid); | ||
| 2649 | |||
| 2650 | mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); | ||
| 2651 | if (--f->ref_count == 0) | ||
| 2652 | mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->r); | ||
| 2653 | } | ||
| 2654 | |||
| 2655 | static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev, | ||
| 2656 | struct net_device *port_dev, | ||
| 2657 | unsigned long event, u16 vid) | ||
| 2658 | { | ||
| 2659 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev); | ||
| 2660 | struct mlxsw_sp_port *mlxsw_sp_vport; | ||
| 2661 | |||
| 2662 | mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); | ||
| 2663 | if (WARN_ON(!mlxsw_sp_vport)) | ||
| 2664 | return -EINVAL; | ||
| 2665 | |||
| 2666 | switch (event) { | ||
| 2667 | case NETDEV_UP: | ||
| 2668 | return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, l3_dev); | ||
| 2669 | case NETDEV_DOWN: | ||
| 2670 | mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); | ||
| 2671 | break; | ||
| 2672 | } | ||
| 2673 | |||
| 2674 | return 0; | ||
| 2675 | } | ||
| 2676 | |||
| 2677 | static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev, | ||
| 2678 | unsigned long event) | ||
| 2679 | { | ||
| 2680 | if (netif_is_bridge_port(port_dev) || netif_is_lag_port(port_dev)) | ||
| 2681 | return 0; | ||
| 2682 | |||
| 2683 | return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1); | ||
| 2684 | } | ||
| 2685 | |||
| 2686 | static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev, | ||
| 2687 | struct net_device *lag_dev, | ||
| 2688 | unsigned long event, u16 vid) | ||
| 2689 | { | ||
| 2690 | struct net_device *port_dev; | ||
| 2691 | struct list_head *iter; | ||
| 2692 | int err; | ||
| 2693 | |||
| 2694 | netdev_for_each_lower_dev(lag_dev, port_dev, iter) { | ||
| 2695 | if (mlxsw_sp_port_dev_check(port_dev)) { | ||
| 2696 | err = mlxsw_sp_inetaddr_vport_event(l3_dev, port_dev, | ||
| 2697 | event, vid); | ||
| 2698 | if (err) | ||
| 2699 | return err; | ||
| 2700 | } | ||
| 2701 | } | ||
| 2702 | |||
| 2703 | return 0; | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev, | ||
| 2707 | unsigned long event) | ||
| 2708 | { | ||
| 2709 | if (netif_is_bridge_port(lag_dev)) | ||
| 2710 | return 0; | ||
| 2711 | |||
| 2712 | return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1); | ||
| 2713 | } | ||
| 2714 | |||
| 2715 | static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp, | ||
| 2716 | struct net_device *l3_dev) | ||
| 2717 | { | ||
| 2718 | u16 fid; | ||
| 2719 | |||
| 2720 | if (is_vlan_dev(l3_dev)) | ||
| 2721 | fid = vlan_dev_vlan_id(l3_dev); | ||
| 2722 | else if (mlxsw_sp->master_bridge.dev == l3_dev) | ||
| 2723 | fid = 1; | ||
| 2724 | else | ||
| 2725 | return mlxsw_sp_vfid_find(mlxsw_sp, l3_dev); | ||
| 2726 | |||
| 2727 | return mlxsw_sp_fid_find(mlxsw_sp, fid); | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid) | ||
| 2731 | { | ||
| 2732 | if (mlxsw_sp_fid_is_vfid(fid)) | ||
| 2733 | return MLXSW_REG_RITR_FID_IF; | ||
| 2734 | else | ||
| 2735 | return MLXSW_REG_RITR_VLAN_IF; | ||
| 2736 | } | ||
| 2737 | |||
| 2738 | static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, | ||
| 2739 | struct net_device *l3_dev, | ||
| 2740 | u16 fid, u16 rif, | ||
| 2741 | bool create) | ||
| 2742 | { | ||
| 2743 | enum mlxsw_reg_ritr_if_type rif_type; | ||
| 2744 | char ritr_pl[MLXSW_REG_RITR_LEN]; | ||
| 2745 | |||
| 2746 | rif_type = mlxsw_sp_rif_type_get(fid); | ||
| 2747 | mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, l3_dev->mtu, | ||
| 2748 | l3_dev->dev_addr); | ||
| 2749 | mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid); | ||
| 2750 | |||
| 2751 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | ||
| 2752 | } | ||
| 2753 | |||
| 2754 | static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, | ||
| 2755 | struct net_device *l3_dev, | ||
| 2756 | struct mlxsw_sp_fid *f) | ||
| 2757 | { | ||
| 2758 | struct mlxsw_sp_rif *r; | ||
| 2759 | u16 rif; | ||
| 2760 | int err; | ||
| 2761 | |||
| 2762 | rif = mlxsw_sp_avail_rif_get(mlxsw_sp); | ||
| 2763 | if (rif == MLXSW_SP_RIF_MAX) | ||
| 2764 | return -ERANGE; | ||
| 2765 | |||
| 2766 | err = mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, true); | ||
| 2767 | if (err) | ||
| 2768 | return err; | ||
| 2769 | |||
| 2770 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, true); | ||
| 2771 | if (err) | ||
| 2772 | goto err_rif_fdb_op; | ||
| 2773 | |||
| 2774 | r = mlxsw_sp_rif_alloc(rif, l3_dev, f); | ||
| 2775 | if (!r) { | ||
| 2776 | err = -ENOMEM; | ||
| 2777 | goto err_rif_alloc; | ||
| 2778 | } | ||
| 2779 | |||
| 2780 | f->r = r; | ||
| 2781 | mlxsw_sp->rifs[rif] = r; | ||
| 2782 | |||
| 2783 | netdev_dbg(l3_dev, "RIF=%d created\n", rif); | ||
| 2784 | |||
| 2785 | return 0; | ||
| 2786 | |||
| 2787 | err_rif_alloc: | ||
| 2788 | mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); | ||
| 2789 | err_rif_fdb_op: | ||
| 2790 | mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); | ||
| 2791 | return err; | ||
| 2792 | } | ||
| 2793 | |||
| 2794 | void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, | ||
| 2795 | struct mlxsw_sp_rif *r) | ||
| 2796 | { | ||
| 2797 | struct net_device *l3_dev = r->dev; | ||
| 2798 | struct mlxsw_sp_fid *f = r->f; | ||
| 2799 | u16 rif = r->rif; | ||
| 2800 | |||
| 2801 | mlxsw_sp->rifs[rif] = NULL; | ||
| 2802 | f->r = NULL; | ||
| 2803 | |||
| 2804 | kfree(r); | ||
| 2805 | |||
| 2806 | mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); | ||
| 2807 | |||
| 2808 | mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); | ||
| 2809 | |||
| 2810 | netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif); | ||
| 2811 | } | ||
| 2812 | |||
| 2813 | static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev, | ||
| 2814 | struct net_device *br_dev, | ||
| 2815 | unsigned long event) | ||
| 2816 | { | ||
| 2817 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev); | ||
| 2818 | struct mlxsw_sp_fid *f; | ||
| 2819 | |||
| 2820 | /* FID can either be an actual FID if the L3 device is the | ||
| 2821 | * VLAN-aware bridge or a VLAN device on top. Otherwise, the | ||
| 2822 | * L3 device is a VLAN-unaware bridge and we get a vFID. | ||
| 2823 | */ | ||
| 2824 | f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); | ||
| 2825 | if (WARN_ON(!f)) | ||
| 2826 | return -EINVAL; | ||
| 2827 | |||
| 2828 | switch (event) { | ||
| 2829 | case NETDEV_UP: | ||
| 2830 | return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); | ||
| 2831 | case NETDEV_DOWN: | ||
| 2832 | mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); | ||
| 2833 | break; | ||
| 2834 | } | ||
| 2835 | |||
| 2836 | return 0; | ||
| 2837 | } | ||
| 2838 | |||
| 2839 | static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev, | ||
| 2840 | unsigned long event) | ||
| 2841 | { | ||
| 2842 | struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); | ||
| 2843 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev); | ||
| 2844 | u16 vid = vlan_dev_vlan_id(vlan_dev); | ||
| 2845 | |||
| 2846 | if (mlxsw_sp_port_dev_check(real_dev)) | ||
| 2847 | return mlxsw_sp_inetaddr_vport_event(vlan_dev, real_dev, event, | ||
| 2848 | vid); | ||
| 2849 | else if (netif_is_lag_master(real_dev)) | ||
| 2850 | return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event, | ||
| 2851 | vid); | ||
| 2852 | else if (netif_is_bridge_master(real_dev) && | ||
| 2853 | mlxsw_sp->master_bridge.dev == real_dev) | ||
| 2854 | return mlxsw_sp_inetaddr_bridge_event(vlan_dev, real_dev, | ||
| 2855 | event); | ||
| 2856 | |||
| 2857 | return 0; | ||
| 2858 | } | ||
| 2859 | |||
| 2860 | static int mlxsw_sp_inetaddr_event(struct notifier_block *unused, | ||
| 2861 | unsigned long event, void *ptr) | ||
| 2862 | { | ||
| 2863 | struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; | ||
| 2864 | struct net_device *dev = ifa->ifa_dev->dev; | ||
| 2865 | struct mlxsw_sp *mlxsw_sp; | ||
| 2866 | struct mlxsw_sp_rif *r; | ||
| 2867 | int err = 0; | ||
| 2868 | |||
| 2869 | mlxsw_sp = mlxsw_sp_lower_get(dev); | ||
| 2870 | if (!mlxsw_sp) | ||
| 2871 | goto out; | ||
| 2872 | |||
| 2873 | r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | ||
| 2874 | if (!mlxsw_sp_rif_should_config(r, event)) | ||
| 2875 | goto out; | ||
| 2876 | |||
| 2877 | if (mlxsw_sp_port_dev_check(dev)) | ||
| 2878 | err = mlxsw_sp_inetaddr_port_event(dev, event); | ||
| 2879 | else if (netif_is_lag_master(dev)) | ||
| 2880 | err = mlxsw_sp_inetaddr_lag_event(dev, event); | ||
| 2881 | else if (netif_is_bridge_master(dev)) | ||
| 2882 | err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event); | ||
| 2883 | else if (is_vlan_dev(dev)) | ||
| 2884 | err = mlxsw_sp_inetaddr_vlan_event(dev, event); | ||
| 2885 | |||
| 2886 | out: | ||
| 2887 | return notifier_from_errno(err); | ||
| 2888 | } | ||
| 2889 | |||
| 2890 | static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif, | ||
| 2891 | const char *mac, int mtu) | ||
| 2892 | { | ||
| 2893 | char ritr_pl[MLXSW_REG_RITR_LEN]; | ||
| 2894 | int err; | ||
| 2895 | |||
| 2896 | mlxsw_reg_ritr_rif_pack(ritr_pl, rif); | ||
| 2897 | err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | ||
| 2898 | if (err) | ||
| 2899 | return err; | ||
| 2900 | |||
| 2901 | mlxsw_reg_ritr_mtu_set(ritr_pl, mtu); | ||
| 2902 | mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac); | ||
| 2903 | mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE); | ||
| 2904 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); | ||
| 2905 | } | ||
| 2906 | |||
| 2907 | static int mlxsw_sp_netdevice_router_port_event(struct net_device *dev) | ||
| 2908 | { | ||
| 2909 | struct mlxsw_sp *mlxsw_sp; | ||
| 2910 | struct mlxsw_sp_rif *r; | ||
| 2911 | int err; | ||
| 2912 | |||
| 2913 | mlxsw_sp = mlxsw_sp_lower_get(dev); | ||
| 2914 | if (!mlxsw_sp) | ||
| 2915 | return 0; | ||
| 2916 | |||
| 2917 | r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); | ||
| 2918 | if (!r) | ||
| 2919 | return 0; | ||
| 2920 | |||
| 2921 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, false); | ||
| 2922 | if (err) | ||
| 2923 | return err; | ||
| 2924 | |||
| 2925 | err = mlxsw_sp_rif_edit(mlxsw_sp, r->rif, dev->dev_addr, dev->mtu); | ||
| 2926 | if (err) | ||
| 2927 | goto err_rif_edit; | ||
| 2928 | |||
| 2929 | err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, r->f->fid, true); | ||
| 2930 | if (err) | ||
| 2931 | goto err_rif_fdb_op; | ||
| 2932 | |||
| 2933 | ether_addr_copy(r->addr, dev->dev_addr); | ||
| 2934 | r->mtu = dev->mtu; | ||
| 2935 | |||
| 2936 | netdev_dbg(dev, "Updated RIF=%d\n", r->rif); | ||
| 2937 | |||
| 2938 | return 0; | ||
| 2939 | |||
| 2940 | err_rif_fdb_op: | ||
| 2941 | mlxsw_sp_rif_edit(mlxsw_sp, r->rif, r->addr, r->mtu); | ||
| 2942 | err_rif_edit: | ||
| 2943 | mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, true); | ||
| 2944 | return err; | ||
| 2945 | } | ||
| 2946 | |||
| 2547 | static bool mlxsw_sp_lag_port_fid_member(struct mlxsw_sp_port *lag_port, | 2947 | static bool mlxsw_sp_lag_port_fid_member(struct mlxsw_sp_port *lag_port, |
| 2548 | u16 fid) | 2948 | u16 fid) |
| 2549 | { | 2949 | { |
| @@ -2624,9 +3024,15 @@ int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) | |||
| 2624 | return mlxsw_sp_port_fdb_flush_by_port_fid(mlxsw_sp_port, fid); | 3024 | return mlxsw_sp_port_fdb_flush_by_port_fid(mlxsw_sp_port, fid); |
| 2625 | } | 3025 | } |
| 2626 | 3026 | ||
| 2627 | static bool mlxsw_sp_port_dev_check(const struct net_device *dev) | 3027 | static void mlxsw_sp_master_bridge_gone_sync(struct mlxsw_sp *mlxsw_sp) |
| 2628 | { | 3028 | { |
| 2629 | return dev->netdev_ops == &mlxsw_sp_port_netdev_ops; | 3029 | struct mlxsw_sp_fid *f, *tmp; |
| 3030 | |||
| 3031 | list_for_each_entry_safe(f, tmp, &mlxsw_sp->fids, list) | ||
| 3032 | if (--f->ref_count == 0) | ||
| 3033 | mlxsw_sp_fid_destroy(mlxsw_sp, f); | ||
| 3034 | else | ||
| 3035 | WARN_ON_ONCE(1); | ||
| 2630 | } | 3036 | } |
| 2631 | 3037 | ||
| 2632 | static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, | 3038 | static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, |
| @@ -2645,8 +3051,15 @@ static void mlxsw_sp_master_bridge_inc(struct mlxsw_sp *mlxsw_sp, | |||
| 2645 | 3051 | ||
| 2646 | static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp) | 3052 | static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp) |
| 2647 | { | 3053 | { |
| 2648 | if (--mlxsw_sp->master_bridge.ref_count == 0) | 3054 | if (--mlxsw_sp->master_bridge.ref_count == 0) { |
| 2649 | mlxsw_sp->master_bridge.dev = NULL; | 3055 | mlxsw_sp->master_bridge.dev = NULL; |
| 3056 | /* It's possible upper VLAN devices are still holding | ||
| 3057 | * references to underlying FIDs. Drop the reference | ||
| 3058 | * and release the resources if it was the last one. | ||
| 3059 | * If it wasn't, then something bad happened. | ||
| 3060 | */ | ||
| 3061 | mlxsw_sp_master_bridge_gone_sync(mlxsw_sp); | ||
| 3062 | } | ||
| 2650 | } | 3063 | } |
| 2651 | 3064 | ||
| 2652 | static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, | 3065 | static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, |
| @@ -2806,6 +3219,45 @@ static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp, | |||
| 2806 | return -EBUSY; | 3219 | return -EBUSY; |
| 2807 | } | 3220 | } |
| 2808 | 3221 | ||
| 3222 | static void | ||
| 3223 | mlxsw_sp_port_pvid_vport_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, | ||
| 3224 | u16 lag_id) | ||
| 3225 | { | ||
| 3226 | struct mlxsw_sp_port *mlxsw_sp_vport; | ||
| 3227 | struct mlxsw_sp_fid *f; | ||
| 3228 | |||
| 3229 | mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1); | ||
| 3230 | if (WARN_ON(!mlxsw_sp_vport)) | ||
| 3231 | return; | ||
| 3232 | |||
| 3233 | /* If vPort is assigned a RIF, then leave it since it's no | ||
| 3234 | * longer valid. | ||
| 3235 | */ | ||
| 3236 | f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); | ||
| 3237 | if (f) | ||
| 3238 | f->leave(mlxsw_sp_vport); | ||
| 3239 | |||
| 3240 | mlxsw_sp_vport->lag_id = lag_id; | ||
| 3241 | mlxsw_sp_vport->lagged = 1; | ||
| 3242 | } | ||
| 3243 | |||
| 3244 | static void | ||
| 3245 | mlxsw_sp_port_pvid_vport_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port) | ||
| 3246 | { | ||
| 3247 | struct mlxsw_sp_port *mlxsw_sp_vport; | ||
| 3248 | struct mlxsw_sp_fid *f; | ||
| 3249 | |||
| 3250 | mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1); | ||
| 3251 | if (WARN_ON(!mlxsw_sp_vport)) | ||
| 3252 | return; | ||
| 3253 | |||
| 3254 | f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); | ||
| 3255 | if (f) | ||
| 3256 | f->leave(mlxsw_sp_vport); | ||
| 3257 | |||
| 3258 | mlxsw_sp_vport->lagged = 0; | ||
| 3259 | } | ||
| 3260 | |||
| 2809 | static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, | 3261 | static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, |
| 2810 | struct net_device *lag_dev) | 3262 | struct net_device *lag_dev) |
| 2811 | { | 3263 | { |
| @@ -2841,6 +3293,9 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, | |||
| 2841 | mlxsw_sp_port->lag_id = lag_id; | 3293 | mlxsw_sp_port->lag_id = lag_id; |
| 2842 | mlxsw_sp_port->lagged = 1; | 3294 | mlxsw_sp_port->lagged = 1; |
| 2843 | lag->ref_count++; | 3295 | lag->ref_count++; |
| 3296 | |||
| 3297 | mlxsw_sp_port_pvid_vport_lag_join(mlxsw_sp_port, lag_id); | ||
| 3298 | |||
| 2844 | return 0; | 3299 | return 0; |
| 2845 | 3300 | ||
| 2846 | err_col_port_enable: | 3301 | err_col_port_enable: |
| @@ -2878,6 +3333,8 @@ static void mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port, | |||
| 2878 | mlxsw_sp_port->local_port); | 3333 | mlxsw_sp_port->local_port); |
| 2879 | mlxsw_sp_port->lagged = 0; | 3334 | mlxsw_sp_port->lagged = 0; |
| 2880 | lag->ref_count--; | 3335 | lag->ref_count--; |
| 3336 | |||
| 3337 | mlxsw_sp_port_pvid_vport_lag_leave(mlxsw_sp_port); | ||
| 2881 | } | 3338 | } |
| 2882 | 3339 | ||
| 2883 | static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port, | 3340 | static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port, |
| @@ -3071,47 +3528,97 @@ static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev, | |||
| 3071 | return 0; | 3528 | return 0; |
| 3072 | } | 3529 | } |
| 3073 | 3530 | ||
| 3074 | static struct mlxsw_sp_fid * | 3531 | static int mlxsw_sp_master_bridge_vlan_link(struct mlxsw_sp *mlxsw_sp, |
| 3075 | mlxsw_sp_br_vfid_find(const struct mlxsw_sp *mlxsw_sp, | 3532 | struct net_device *vlan_dev) |
| 3076 | const struct net_device *br_dev) | ||
| 3077 | { | 3533 | { |
| 3534 | u16 fid = vlan_dev_vlan_id(vlan_dev); | ||
| 3078 | struct mlxsw_sp_fid *f; | 3535 | struct mlxsw_sp_fid *f; |
| 3079 | 3536 | ||
| 3080 | list_for_each_entry(f, &mlxsw_sp->br_vfids.list, list) { | 3537 | f = mlxsw_sp_fid_find(mlxsw_sp, fid); |
| 3081 | if (f->dev == br_dev) | 3538 | if (!f) { |
| 3082 | return f; | 3539 | f = mlxsw_sp_fid_create(mlxsw_sp, fid); |
| 3540 | if (IS_ERR(f)) | ||
| 3541 | return PTR_ERR(f); | ||
| 3083 | } | 3542 | } |
| 3084 | 3543 | ||
| 3085 | return NULL; | 3544 | f->ref_count++; |
| 3545 | |||
| 3546 | return 0; | ||
| 3547 | } | ||
| 3548 | |||
| 3549 | static void mlxsw_sp_master_bridge_vlan_unlink(struct mlxsw_sp *mlxsw_sp, | ||
| 3550 | struct net_device *vlan_dev) | ||
| 3551 | { | ||
| 3552 | u16 fid = vlan_dev_vlan_id(vlan_dev); | ||
| 3553 | struct mlxsw_sp_fid *f; | ||
| 3554 | |||
| 3555 | f = mlxsw_sp_fid_find(mlxsw_sp, fid); | ||
| 3556 | if (f && f->r) | ||
| 3557 | mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); | ||
| 3558 | if (f && --f->ref_count == 0) | ||
| 3559 | mlxsw_sp_fid_destroy(mlxsw_sp, f); | ||
| 3086 | } | 3560 | } |
| 3087 | 3561 | ||
| 3088 | static u16 mlxsw_sp_vfid_to_br_vfid(u16 vfid) | 3562 | static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, |
| 3563 | unsigned long event, void *ptr) | ||
| 3089 | { | 3564 | { |
| 3090 | return vfid - MLXSW_SP_VFID_PORT_MAX; | 3565 | struct netdev_notifier_changeupper_info *info; |
| 3566 | struct net_device *upper_dev; | ||
| 3567 | struct mlxsw_sp *mlxsw_sp; | ||
| 3568 | int err; | ||
| 3569 | |||
| 3570 | mlxsw_sp = mlxsw_sp_lower_get(br_dev); | ||
| 3571 | if (!mlxsw_sp) | ||
| 3572 | return 0; | ||
| 3573 | if (br_dev != mlxsw_sp->master_bridge.dev) | ||
| 3574 | return 0; | ||
| 3575 | |||
| 3576 | info = ptr; | ||
| 3577 | |||
| 3578 | switch (event) { | ||
| 3579 | case NETDEV_CHANGEUPPER: | ||
| 3580 | upper_dev = info->upper_dev; | ||
| 3581 | if (!is_vlan_dev(upper_dev)) | ||
| 3582 | break; | ||
| 3583 | if (info->linking) { | ||
| 3584 | err = mlxsw_sp_master_bridge_vlan_link(mlxsw_sp, | ||
| 3585 | upper_dev); | ||
| 3586 | if (err) | ||
| 3587 | return err; | ||
| 3588 | } else { | ||
| 3589 | mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev); | ||
| 3590 | } | ||
| 3591 | break; | ||
| 3592 | } | ||
| 3593 | |||
| 3594 | return 0; | ||
| 3091 | } | 3595 | } |
| 3092 | 3596 | ||
| 3093 | static u16 mlxsw_sp_br_vfid_to_vfid(u16 br_vfid) | 3597 | static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp) |
| 3094 | { | 3598 | { |
| 3095 | return MLXSW_SP_VFID_PORT_MAX + br_vfid; | 3599 | return find_first_zero_bit(mlxsw_sp->vfids.mapped, |
| 3600 | MLXSW_SP_VFID_MAX); | ||
| 3096 | } | 3601 | } |
| 3097 | 3602 | ||
| 3098 | static u16 mlxsw_sp_avail_br_vfid_get(const struct mlxsw_sp *mlxsw_sp) | 3603 | static int mlxsw_sp_vfid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create) |
| 3099 | { | 3604 | { |
| 3100 | return find_first_zero_bit(mlxsw_sp->br_vfids.mapped, | 3605 | char sfmr_pl[MLXSW_REG_SFMR_LEN]; |
| 3101 | MLXSW_SP_VFID_BR_MAX); | 3606 | |
| 3607 | mlxsw_reg_sfmr_pack(sfmr_pl, !create, fid, 0); | ||
| 3608 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); | ||
| 3102 | } | 3609 | } |
| 3103 | 3610 | ||
| 3104 | static void mlxsw_sp_vport_br_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport); | 3611 | static void mlxsw_sp_vport_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport); |
| 3105 | 3612 | ||
| 3106 | static struct mlxsw_sp_fid *mlxsw_sp_br_vfid_create(struct mlxsw_sp *mlxsw_sp, | 3613 | static struct mlxsw_sp_fid *mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, |
| 3107 | struct net_device *br_dev) | 3614 | struct net_device *br_dev) |
| 3108 | { | 3615 | { |
| 3109 | struct device *dev = mlxsw_sp->bus_info->dev; | 3616 | struct device *dev = mlxsw_sp->bus_info->dev; |
| 3110 | struct mlxsw_sp_fid *f; | 3617 | struct mlxsw_sp_fid *f; |
| 3111 | u16 vfid, fid; | 3618 | u16 vfid, fid; |
| 3112 | int err; | 3619 | int err; |
| 3113 | 3620 | ||
| 3114 | vfid = mlxsw_sp_br_vfid_to_vfid(mlxsw_sp_avail_br_vfid_get(mlxsw_sp)); | 3621 | vfid = mlxsw_sp_avail_vfid_get(mlxsw_sp); |
| 3115 | if (vfid == MLXSW_SP_VFID_MAX) { | 3622 | if (vfid == MLXSW_SP_VFID_MAX) { |
| 3116 | dev_err(dev, "No available vFIDs\n"); | 3623 | dev_err(dev, "No available vFIDs\n"); |
| 3117 | return ERR_PTR(-ERANGE); | 3624 | return ERR_PTR(-ERANGE); |
| @@ -3128,12 +3635,12 @@ static struct mlxsw_sp_fid *mlxsw_sp_br_vfid_create(struct mlxsw_sp *mlxsw_sp, | |||
| 3128 | if (!f) | 3635 | if (!f) |
| 3129 | goto err_allocate_vfid; | 3636 | goto err_allocate_vfid; |
| 3130 | 3637 | ||
| 3131 | f->leave = mlxsw_sp_vport_br_vfid_leave; | 3638 | f->leave = mlxsw_sp_vport_vfid_leave; |
| 3132 | f->fid = fid; | 3639 | f->fid = fid; |
| 3133 | f->dev = br_dev; | 3640 | f->dev = br_dev; |
| 3134 | 3641 | ||
| 3135 | list_add(&f->list, &mlxsw_sp->br_vfids.list); | 3642 | list_add(&f->list, &mlxsw_sp->vfids.list); |
| 3136 | set_bit(mlxsw_sp_vfid_to_br_vfid(vfid), mlxsw_sp->br_vfids.mapped); | 3643 | set_bit(vfid, mlxsw_sp->vfids.mapped); |
| 3137 | 3644 | ||
| 3138 | return f; | 3645 | return f; |
| 3139 | 3646 | ||
| @@ -3142,29 +3649,42 @@ err_allocate_vfid: | |||
| 3142 | return ERR_PTR(-ENOMEM); | 3649 | return ERR_PTR(-ENOMEM); |
| 3143 | } | 3650 | } |
| 3144 | 3651 | ||
| 3145 | static void mlxsw_sp_br_vfid_destroy(struct mlxsw_sp *mlxsw_sp, | 3652 | static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, |
| 3146 | struct mlxsw_sp_fid *f) | 3653 | struct mlxsw_sp_fid *f) |
| 3147 | { | 3654 | { |
| 3148 | u16 vfid = mlxsw_sp_fid_to_vfid(f->fid); | 3655 | u16 vfid = mlxsw_sp_fid_to_vfid(f->fid); |
| 3149 | u16 br_vfid = mlxsw_sp_vfid_to_br_vfid(vfid); | 3656 | u16 fid = f->fid; |
| 3150 | 3657 | ||
| 3151 | clear_bit(br_vfid, mlxsw_sp->br_vfids.mapped); | 3658 | clear_bit(vfid, mlxsw_sp->vfids.mapped); |
| 3152 | list_del(&f->list); | 3659 | list_del(&f->list); |
| 3153 | 3660 | ||
| 3154 | mlxsw_sp_vfid_op(mlxsw_sp, f->fid, false); | 3661 | if (f->r) |
| 3662 | mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); | ||
| 3155 | 3663 | ||
| 3156 | kfree(f); | 3664 | kfree(f); |
| 3665 | |||
| 3666 | mlxsw_sp_vfid_op(mlxsw_sp, fid, false); | ||
| 3157 | } | 3667 | } |
| 3158 | 3668 | ||
| 3159 | static int mlxsw_sp_vport_br_vfid_join(struct mlxsw_sp_port *mlxsw_sp_vport, | 3669 | static int mlxsw_sp_vport_fid_map(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid, |
| 3160 | struct net_device *br_dev) | 3670 | bool valid) |
| 3671 | { | ||
| 3672 | enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; | ||
| 3673 | u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); | ||
| 3674 | |||
| 3675 | return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, mt, valid, fid, | ||
| 3676 | vid); | ||
| 3677 | } | ||
| 3678 | |||
| 3679 | static int mlxsw_sp_vport_vfid_join(struct mlxsw_sp_port *mlxsw_sp_vport, | ||
| 3680 | struct net_device *br_dev) | ||
| 3161 | { | 3681 | { |
| 3162 | struct mlxsw_sp_fid *f; | 3682 | struct mlxsw_sp_fid *f; |
| 3163 | int err; | 3683 | int err; |
| 3164 | 3684 | ||
| 3165 | f = mlxsw_sp_br_vfid_find(mlxsw_sp_vport->mlxsw_sp, br_dev); | 3685 | f = mlxsw_sp_vfid_find(mlxsw_sp_vport->mlxsw_sp, br_dev); |
| 3166 | if (!f) { | 3686 | if (!f) { |
| 3167 | f = mlxsw_sp_br_vfid_create(mlxsw_sp_vport->mlxsw_sp, br_dev); | 3687 | f = mlxsw_sp_vfid_create(mlxsw_sp_vport->mlxsw_sp, br_dev); |
| 3168 | if (IS_ERR(f)) | 3688 | if (IS_ERR(f)) |
| 3169 | return PTR_ERR(f); | 3689 | return PTR_ERR(f); |
| 3170 | } | 3690 | } |
| @@ -3188,11 +3708,11 @@ err_vport_fid_map: | |||
| 3188 | mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, false); | 3708 | mlxsw_sp_vport_flood_set(mlxsw_sp_vport, f->fid, false); |
| 3189 | err_vport_flood_set: | 3709 | err_vport_flood_set: |
| 3190 | if (!f->ref_count) | 3710 | if (!f->ref_count) |
| 3191 | mlxsw_sp_br_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f); | 3711 | mlxsw_sp_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f); |
| 3192 | return err; | 3712 | return err; |
| 3193 | } | 3713 | } |
| 3194 | 3714 | ||
| 3195 | static void mlxsw_sp_vport_br_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport) | 3715 | static void mlxsw_sp_vport_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport) |
| 3196 | { | 3716 | { |
| 3197 | struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); | 3717 | struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); |
| 3198 | 3718 | ||
| @@ -3206,22 +3726,24 @@ static void mlxsw_sp_vport_br_vfid_leave(struct mlxsw_sp_port *mlxsw_sp_vport) | |||
| 3206 | 3726 | ||
| 3207 | mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); | 3727 | mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); |
| 3208 | if (--f->ref_count == 0) | 3728 | if (--f->ref_count == 0) |
| 3209 | mlxsw_sp_br_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f); | 3729 | mlxsw_sp_vfid_destroy(mlxsw_sp_vport->mlxsw_sp, f); |
| 3210 | } | 3730 | } |
| 3211 | 3731 | ||
| 3212 | static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport, | 3732 | static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport, |
| 3213 | struct net_device *br_dev) | 3733 | struct net_device *br_dev) |
| 3214 | { | 3734 | { |
| 3735 | struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); | ||
| 3215 | u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); | 3736 | u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); |
| 3216 | struct net_device *dev = mlxsw_sp_vport->dev; | 3737 | struct net_device *dev = mlxsw_sp_vport->dev; |
| 3217 | int err; | 3738 | int err; |
| 3218 | 3739 | ||
| 3219 | mlxsw_sp_vport_vfid_leave(mlxsw_sp_vport); | 3740 | if (f && !WARN_ON(!f->leave)) |
| 3741 | f->leave(mlxsw_sp_vport); | ||
| 3220 | 3742 | ||
| 3221 | err = mlxsw_sp_vport_br_vfid_join(mlxsw_sp_vport, br_dev); | 3743 | err = mlxsw_sp_vport_vfid_join(mlxsw_sp_vport, br_dev); |
| 3222 | if (err) { | 3744 | if (err) { |
| 3223 | netdev_err(dev, "Failed to join vFID\n"); | 3745 | netdev_err(dev, "Failed to join vFID\n"); |
| 3224 | goto err_vport_br_vfid_join; | 3746 | return err; |
| 3225 | } | 3747 | } |
| 3226 | 3748 | ||
| 3227 | err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); | 3749 | err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); |
| @@ -3238,9 +3760,7 @@ static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport, | |||
| 3238 | return 0; | 3760 | return 0; |
| 3239 | 3761 | ||
| 3240 | err_port_vid_learning_set: | 3762 | err_port_vid_learning_set: |
| 3241 | mlxsw_sp_vport_br_vfid_leave(mlxsw_sp_vport); | 3763 | mlxsw_sp_vport_vfid_leave(mlxsw_sp_vport); |
| 3242 | err_vport_br_vfid_join: | ||
| 3243 | mlxsw_sp_vport_vfid_join(mlxsw_sp_vport); | ||
| 3244 | return err; | 3764 | return err; |
| 3245 | } | 3765 | } |
| 3246 | 3766 | ||
| @@ -3250,12 +3770,7 @@ static void mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport) | |||
| 3250 | 3770 | ||
| 3251 | mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); | 3771 | mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); |
| 3252 | 3772 | ||
| 3253 | mlxsw_sp_vport_br_vfid_leave(mlxsw_sp_vport); | 3773 | mlxsw_sp_vport_vfid_leave(mlxsw_sp_vport); |
| 3254 | |||
| 3255 | mlxsw_sp_vport_vfid_join(mlxsw_sp_vport); | ||
| 3256 | |||
| 3257 | mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid, | ||
| 3258 | MLXSW_REG_SPMS_STATE_FORWARDING); | ||
| 3259 | 3774 | ||
| 3260 | mlxsw_sp_vport->learning = 0; | 3775 | mlxsw_sp_vport->learning = 0; |
| 3261 | mlxsw_sp_vport->learning_sync = 0; | 3776 | mlxsw_sp_vport->learning_sync = 0; |
| @@ -3271,7 +3786,7 @@ mlxsw_sp_port_master_bridge_check(const struct mlxsw_sp_port *mlxsw_sp_port, | |||
| 3271 | 3786 | ||
| 3272 | list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list, | 3787 | list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list, |
| 3273 | vport.list) { | 3788 | vport.list) { |
| 3274 | struct net_device *dev = mlxsw_sp_vport_br_get(mlxsw_sp_vport); | 3789 | struct net_device *dev = mlxsw_sp_vport_dev_get(mlxsw_sp_vport); |
| 3275 | 3790 | ||
| 3276 | if (dev && dev == br_dev) | 3791 | if (dev && dev == br_dev) |
| 3277 | return false; | 3792 | return false; |
| @@ -3365,10 +3880,14 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, | |||
| 3365 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 3880 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
| 3366 | int err = 0; | 3881 | int err = 0; |
| 3367 | 3882 | ||
| 3368 | if (mlxsw_sp_port_dev_check(dev)) | 3883 | if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU) |
| 3884 | err = mlxsw_sp_netdevice_router_port_event(dev); | ||
| 3885 | else if (mlxsw_sp_port_dev_check(dev)) | ||
| 3369 | err = mlxsw_sp_netdevice_port_event(dev, event, ptr); | 3886 | err = mlxsw_sp_netdevice_port_event(dev, event, ptr); |
| 3370 | else if (netif_is_lag_master(dev)) | 3887 | else if (netif_is_lag_master(dev)) |
| 3371 | err = mlxsw_sp_netdevice_lag_event(dev, event, ptr); | 3888 | err = mlxsw_sp_netdevice_lag_event(dev, event, ptr); |
| 3889 | else if (netif_is_bridge_master(dev)) | ||
| 3890 | err = mlxsw_sp_netdevice_bridge_event(dev, event, ptr); | ||
| 3372 | else if (is_vlan_dev(dev)) | 3891 | else if (is_vlan_dev(dev)) |
| 3373 | err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr); | 3892 | err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr); |
| 3374 | 3893 | ||
| @@ -3379,11 +3898,17 @@ static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = { | |||
| 3379 | .notifier_call = mlxsw_sp_netdevice_event, | 3898 | .notifier_call = mlxsw_sp_netdevice_event, |
| 3380 | }; | 3899 | }; |
| 3381 | 3900 | ||
| 3901 | static struct notifier_block mlxsw_sp_inetaddr_nb __read_mostly = { | ||
| 3902 | .notifier_call = mlxsw_sp_inetaddr_event, | ||
| 3903 | .priority = 10, /* Must be called before FIB notifier block */ | ||
| 3904 | }; | ||
| 3905 | |||
| 3382 | static int __init mlxsw_sp_module_init(void) | 3906 | static int __init mlxsw_sp_module_init(void) |
| 3383 | { | 3907 | { |
| 3384 | int err; | 3908 | int err; |
| 3385 | 3909 | ||
| 3386 | register_netdevice_notifier(&mlxsw_sp_netdevice_nb); | 3910 | register_netdevice_notifier(&mlxsw_sp_netdevice_nb); |
| 3911 | register_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); | ||
| 3387 | err = mlxsw_core_driver_register(&mlxsw_sp_driver); | 3912 | err = mlxsw_core_driver_register(&mlxsw_sp_driver); |
| 3388 | if (err) | 3913 | if (err) |
| 3389 | goto err_core_driver_register; | 3914 | goto err_core_driver_register; |
| @@ -3397,6 +3922,7 @@ err_core_driver_register: | |||
| 3397 | static void __exit mlxsw_sp_module_exit(void) | 3922 | static void __exit mlxsw_sp_module_exit(void) |
| 3398 | { | 3923 | { |
| 3399 | mlxsw_core_driver_unregister(&mlxsw_sp_driver); | 3924 | mlxsw_core_driver_unregister(&mlxsw_sp_driver); |
| 3925 | unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); | ||
| 3400 | unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); | 3926 | unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); |
| 3401 | } | 3927 | } |
| 3402 | 3928 | ||
