diff options
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index db1c2c42cd3b..ed0e6c09dcc8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | |||
| @@ -559,6 +559,10 @@ struct mlxsw_sp_neigh_entry { | |||
| 559 | struct mlxsw_sp_neigh_key key; | 559 | struct mlxsw_sp_neigh_key key; |
| 560 | u16 rif; | 560 | u16 rif; |
| 561 | struct neighbour *n; | 561 | struct neighbour *n; |
| 562 | bool offloaded; | ||
| 563 | struct delayed_work dw; | ||
| 564 | struct mlxsw_sp_port *mlxsw_sp_port; | ||
| 565 | unsigned char ha[ETH_ALEN]; | ||
| 562 | }; | 566 | }; |
| 563 | 567 | ||
| 564 | static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { | 568 | static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { |
| @@ -585,6 +589,8 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp, | |||
| 585 | mlxsw_sp_neigh_ht_params); | 589 | mlxsw_sp_neigh_ht_params); |
| 586 | } | 590 | } |
| 587 | 591 | ||
| 592 | static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work); | ||
| 593 | |||
| 588 | static struct mlxsw_sp_neigh_entry * | 594 | static struct mlxsw_sp_neigh_entry * |
| 589 | mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len, | 595 | mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len, |
| 590 | struct net_device *dev, u16 rif, | 596 | struct net_device *dev, u16 rif, |
| @@ -599,6 +605,7 @@ mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len, | |||
| 599 | neigh_entry->key.dev = dev; | 605 | neigh_entry->key.dev = dev; |
| 600 | neigh_entry->rif = rif; | 606 | neigh_entry->rif = rif; |
| 601 | neigh_entry->n = n; | 607 | neigh_entry->n = n; |
| 608 | INIT_DELAYED_WORK(&neigh_entry->dw, mlxsw_sp_router_neigh_update_hw); | ||
| 602 | return neigh_entry; | 609 | return neigh_entry; |
| 603 | } | 610 | } |
| 604 | 611 | ||
| @@ -801,13 +808,76 @@ static void mlxsw_sp_router_neighs_update_work(struct work_struct *work) | |||
| 801 | mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp); | 808 | mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp); |
| 802 | } | 809 | } |
| 803 | 810 | ||
| 811 | static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work) | ||
| 812 | { | ||
| 813 | struct mlxsw_sp_neigh_entry *neigh_entry = | ||
| 814 | container_of(work, struct mlxsw_sp_neigh_entry, dw.work); | ||
| 815 | struct neighbour *n = neigh_entry->n; | ||
| 816 | struct mlxsw_sp_port *mlxsw_sp_port = neigh_entry->mlxsw_sp_port; | ||
| 817 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | ||
| 818 | char rauht_pl[MLXSW_REG_RAUHT_LEN]; | ||
| 819 | struct net_device *dev; | ||
| 820 | bool entry_connected; | ||
| 821 | u8 nud_state; | ||
| 822 | bool updating; | ||
| 823 | bool removing; | ||
| 824 | bool adding; | ||
| 825 | u32 dip; | ||
| 826 | int err; | ||
| 827 | |||
| 828 | read_lock_bh(&n->lock); | ||
| 829 | dip = ntohl(*((__be32 *) n->primary_key)); | ||
| 830 | memcpy(neigh_entry->ha, n->ha, sizeof(neigh_entry->ha)); | ||
| 831 | nud_state = n->nud_state; | ||
| 832 | dev = n->dev; | ||
| 833 | read_unlock_bh(&n->lock); | ||
| 834 | |||
| 835 | entry_connected = nud_state & NUD_VALID; | ||
| 836 | adding = (!neigh_entry->offloaded) && entry_connected; | ||
| 837 | updating = neigh_entry->offloaded && entry_connected; | ||
| 838 | removing = neigh_entry->offloaded && !entry_connected; | ||
| 839 | |||
| 840 | if (adding || updating) { | ||
| 841 | mlxsw_reg_rauht_pack4(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_ADD, | ||
| 842 | neigh_entry->rif, | ||
| 843 | neigh_entry->ha, dip); | ||
| 844 | err = mlxsw_reg_write(mlxsw_sp->core, | ||
| 845 | MLXSW_REG(rauht), rauht_pl); | ||
| 846 | if (err) { | ||
| 847 | netdev_err(dev, "Could not add neigh %pI4h\n", &dip); | ||
| 848 | neigh_entry->offloaded = false; | ||
| 849 | } else { | ||
| 850 | neigh_entry->offloaded = true; | ||
| 851 | } | ||
| 852 | } else if (removing) { | ||
| 853 | mlxsw_reg_rauht_pack4(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE, | ||
| 854 | neigh_entry->rif, | ||
| 855 | neigh_entry->ha, dip); | ||
| 856 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), | ||
| 857 | rauht_pl); | ||
| 858 | if (err) { | ||
| 859 | netdev_err(dev, "Could not delete neigh %pI4h\n", &dip); | ||
| 860 | neigh_entry->offloaded = true; | ||
| 861 | } else { | ||
| 862 | neigh_entry->offloaded = false; | ||
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | neigh_release(n); | ||
| 867 | mlxsw_sp_port_dev_put(mlxsw_sp_port); | ||
| 868 | } | ||
| 869 | |||
| 804 | static int mlxsw_sp_router_netevent_event(struct notifier_block *unused, | 870 | static int mlxsw_sp_router_netevent_event(struct notifier_block *unused, |
| 805 | unsigned long event, void *ptr) | 871 | unsigned long event, void *ptr) |
| 806 | { | 872 | { |
| 873 | struct mlxsw_sp_neigh_entry *neigh_entry; | ||
| 807 | struct mlxsw_sp_port *mlxsw_sp_port; | 874 | struct mlxsw_sp_port *mlxsw_sp_port; |
| 808 | struct mlxsw_sp *mlxsw_sp; | 875 | struct mlxsw_sp *mlxsw_sp; |
| 809 | unsigned long interval; | 876 | unsigned long interval; |
| 877 | struct net_device *dev; | ||
| 810 | struct neigh_parms *p; | 878 | struct neigh_parms *p; |
| 879 | struct neighbour *n; | ||
| 880 | u32 dip; | ||
| 811 | 881 | ||
| 812 | switch (event) { | 882 | switch (event) { |
| 813 | case NETEVENT_DELAY_PROBE_TIME_UPDATE: | 883 | case NETEVENT_DELAY_PROBE_TIME_UPDATE: |
| @@ -830,6 +900,39 @@ static int mlxsw_sp_router_netevent_event(struct notifier_block *unused, | |||
| 830 | 900 | ||
| 831 | mlxsw_sp_port_dev_put(mlxsw_sp_port); | 901 | mlxsw_sp_port_dev_put(mlxsw_sp_port); |
| 832 | break; | 902 | break; |
| 903 | case NETEVENT_NEIGH_UPDATE: | ||
| 904 | n = ptr; | ||
| 905 | dev = n->dev; | ||
| 906 | |||
| 907 | if (n->tbl != &arp_tbl) | ||
| 908 | return NOTIFY_DONE; | ||
| 909 | |||
| 910 | mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(dev); | ||
| 911 | if (!mlxsw_sp_port) | ||
| 912 | return NOTIFY_DONE; | ||
| 913 | |||
| 914 | mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | ||
| 915 | dip = ntohl(*((__be32 *) n->primary_key)); | ||
| 916 | neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, | ||
| 917 | &dip, | ||
| 918 | sizeof(__be32), | ||
| 919 | dev); | ||
| 920 | if (WARN_ON(!neigh_entry) || WARN_ON(neigh_entry->n != n)) { | ||
| 921 | mlxsw_sp_port_dev_put(mlxsw_sp_port); | ||
| 922 | return NOTIFY_DONE; | ||
| 923 | } | ||
| 924 | neigh_entry->mlxsw_sp_port = mlxsw_sp_port; | ||
| 925 | |||
| 926 | /* Take a reference to ensure the neighbour won't be | ||
| 927 | * destructed until we drop the reference in delayed | ||
| 928 | * work. | ||
| 929 | */ | ||
| 930 | neigh_clone(n); | ||
| 931 | if (!mlxsw_core_schedule_dw(&neigh_entry->dw, 0)) { | ||
| 932 | neigh_release(n); | ||
| 933 | mlxsw_sp_port_dev_put(mlxsw_sp_port); | ||
| 934 | } | ||
| 935 | break; | ||
| 833 | } | 936 | } |
| 834 | 937 | ||
| 835 | return NOTIFY_DONE; | 938 | return NOTIFY_DONE; |
