aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c103
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
564static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { 568static 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
592static void mlxsw_sp_router_neigh_update_hw(struct work_struct *work);
593
588static struct mlxsw_sp_neigh_entry * 594static struct mlxsw_sp_neigh_entry *
589mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len, 595mlxsw_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
811static 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
804static int mlxsw_sp_router_netevent_event(struct notifier_block *unused, 870static 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;