aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYotam Gigi <yotamg@mellanox.com>2016-07-05 05:27:43 -0400
committerDavid S. Miller <davem@davemloft.net>2016-07-05 12:06:29 -0400
commitc723c735fa6bacfdb01c4697c2cfeba142990d18 (patch)
tree52ca53d6e985d9c6853683d5fddab37941f32654
parent2a4501ae18b52fcdf553404286e6cefabd1d17ec (diff)
mlxsw: spectrum_router: Periodically update the kernel's neigh table
As previously explained, the driver should periodically poll the device for neighbours activity according to the configured DELAY_PROBE_TIME. This will prevent active neighbours from staying in STALE state for long periods of time. During init configure the polling interval according to the DELAY_PROBE_TIME used in the default table. In addition, register a netevent notification block, so that the interval is updated whenever DELAY_PROBE_TIME changes. Using the computed interval schedule a delayed work, which will update the kernel via neigh_event_send() on any active neighbour since the last delayed work. Signed-off-by: Yotam Gigi <yotamg@mellanox.com> Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c192
2 files changed, 194 insertions, 2 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 734c5baffaf1..9c2a60f01517 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -214,6 +214,10 @@ struct mlxsw_sp_router {
214 struct mlxsw_sp_lpm_tree lpm_trees[MLXSW_SP_LPM_TREE_COUNT]; 214 struct mlxsw_sp_lpm_tree lpm_trees[MLXSW_SP_LPM_TREE_COUNT];
215 struct mlxsw_sp_vr vrs[MLXSW_SP_VIRTUAL_ROUTER_MAX]; 215 struct mlxsw_sp_vr vrs[MLXSW_SP_VIRTUAL_ROUTER_MAX];
216 struct rhashtable neigh_ht; 216 struct rhashtable neigh_ht;
217 struct {
218 struct delayed_work dw;
219 unsigned long interval; /* ms */
220 } neighs_update;
217}; 221};
218 222
219struct mlxsw_sp { 223struct mlxsw_sp {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 90d382a5d6a7..db1c2c42cd3b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -3,6 +3,7 @@
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> 4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com> 5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
6 * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com>
6 * 7 *
7 * Redistribution and use in source and binary forms, with or without 8 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met: 9 * modification, are permitted provided that the following conditions are met:
@@ -38,6 +39,8 @@
38#include <linux/rhashtable.h> 39#include <linux/rhashtable.h>
39#include <linux/bitops.h> 40#include <linux/bitops.h>
40#include <linux/in6.h> 41#include <linux/in6.h>
42#include <linux/notifier.h>
43#include <net/netevent.h>
41#include <net/neighbour.h> 44#include <net/neighbour.h>
42#include <net/arp.h> 45#include <net/arp.h>
43 46
@@ -676,14 +679,199 @@ void mlxsw_sp_router_neigh_destroy(struct net_device *dev,
676 mlxsw_sp_neigh_entry_destroy(neigh_entry); 679 mlxsw_sp_neigh_entry_destroy(neigh_entry);
677} 680}
678 681
682static void
683mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
684{
685 unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
686
687 mlxsw_sp->router.neighs_update.interval = jiffies_to_msecs(interval);
688}
689
690static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
691 char *rauhtd_pl,
692 int ent_index)
693{
694 struct net_device *dev;
695 struct neighbour *n;
696 __be32 dipn;
697 u32 dip;
698 u16 rif;
699
700 mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip);
701
702 if (!mlxsw_sp->rifs[rif]) {
703 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n");
704 return;
705 }
706
707 dipn = htonl(dip);
708 dev = mlxsw_sp->rifs[rif]->dev;
709 n = neigh_lookup(&arp_tbl, &dipn, dev);
710 if (!n) {
711 netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n",
712 &dip);
713 return;
714 }
715
716 netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip);
717 neigh_event_send(n, NULL);
718 neigh_release(n);
719}
720
721static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp,
722 char *rauhtd_pl,
723 int rec_index)
724{
725 u8 num_entries;
726 int i;
727
728 num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl,
729 rec_index);
730 /* Hardware starts counting at 0, so add 1. */
731 num_entries++;
732
733 /* Each record consists of several neighbour entries. */
734 for (i = 0; i < num_entries; i++) {
735 int ent_index;
736
737 ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i;
738 mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl,
739 ent_index);
740 }
741
742}
743
744static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp,
745 char *rauhtd_pl, int rec_index)
746{
747 switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) {
748 case MLXSW_REG_RAUHTD_TYPE_IPV4:
749 mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl,
750 rec_index);
751 break;
752 case MLXSW_REG_RAUHTD_TYPE_IPV6:
753 WARN_ON_ONCE(1);
754 break;
755 }
756}
757
758static void
759mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp)
760{
761 unsigned long interval = mlxsw_sp->router.neighs_update.interval;
762
763 mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw,
764 msecs_to_jiffies(interval));
765}
766
767static void mlxsw_sp_router_neighs_update_work(struct work_struct *work)
768{
769 struct mlxsw_sp *mlxsw_sp;
770 char *rauhtd_pl;
771 u8 num_rec;
772 int i, err;
773
774 rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL);
775 if (!rauhtd_pl)
776 return;
777
778 mlxsw_sp = container_of(work, struct mlxsw_sp,
779 router.neighs_update.dw.work);
780
781 /* Make sure the neighbour's netdev isn't removed in the
782 * process.
783 */
784 rtnl_lock();
785 do {
786 mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4);
787 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd),
788 rauhtd_pl);
789 if (err) {
790 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n");
791 break;
792 }
793 num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl);
794 for (i = 0; i < num_rec; i++)
795 mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl,
796 i);
797 } while (num_rec);
798 rtnl_unlock();
799
800 kfree(rauhtd_pl);
801 mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp);
802}
803
804static int mlxsw_sp_router_netevent_event(struct notifier_block *unused,
805 unsigned long event, void *ptr)
806{
807 struct mlxsw_sp_port *mlxsw_sp_port;
808 struct mlxsw_sp *mlxsw_sp;
809 unsigned long interval;
810 struct neigh_parms *p;
811
812 switch (event) {
813 case NETEVENT_DELAY_PROBE_TIME_UPDATE:
814 p = ptr;
815
816 /* We don't care about changes in the default table. */
817 if (!p->dev || p->tbl != &arp_tbl)
818 return NOTIFY_DONE;
819
820 /* We are in atomic context and can't take RTNL mutex,
821 * so use RCU variant to walk the device chain.
822 */
823 mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev);
824 if (!mlxsw_sp_port)
825 return NOTIFY_DONE;
826
827 mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
828 interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME));
829 mlxsw_sp->router.neighs_update.interval = interval;
830
831 mlxsw_sp_port_dev_put(mlxsw_sp_port);
832 break;
833 }
834
835 return NOTIFY_DONE;
836}
837
838static struct notifier_block mlxsw_sp_router_netevent_nb __read_mostly = {
839 .notifier_call = mlxsw_sp_router_netevent_event,
840};
841
679static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp) 842static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
680{ 843{
681 return rhashtable_init(&mlxsw_sp->router.neigh_ht, 844 int err;
682 &mlxsw_sp_neigh_ht_params); 845
846 err = rhashtable_init(&mlxsw_sp->router.neigh_ht,
847 &mlxsw_sp_neigh_ht_params);
848 if (err)
849 return err;
850
851 /* Initialize the polling interval according to the default
852 * table.
853 */
854 mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp);
855
856 err = register_netevent_notifier(&mlxsw_sp_router_netevent_nb);
857 if (err)
858 goto err_register_netevent_notifier;
859
860 INIT_DELAYED_WORK(&mlxsw_sp->router.neighs_update.dw,
861 mlxsw_sp_router_neighs_update_work);
862 mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw, 0);
863
864 return 0;
865
866err_register_netevent_notifier:
867 rhashtable_destroy(&mlxsw_sp->router.neigh_ht);
868 return err;
683} 869}
684 870
685static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp) 871static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
686{ 872{
873 cancel_delayed_work_sync(&mlxsw_sp->router.neighs_update.dw);
874 unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb);
687 rhashtable_destroy(&mlxsw_sp->router.neigh_ht); 875 rhashtable_destroy(&mlxsw_sp->router.neigh_ht);
688} 876}
689 877