From patchwork Fri Jul 1 14:05:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiri Pirko X-Patchwork-Id: 643029 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3rgzC35Vctz9sR8 for ; Sat, 2 Jul 2016 00:22:39 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=resnulli-us.20150623.gappssmtp.com header.i=@resnulli-us.20150623.gappssmtp.com header.b=wUWfA0WO; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932122AbcGAOWh (ORCPT ); Fri, 1 Jul 2016 10:22:37 -0400 Received: from mail-wm0-f66.google.com ([74.125.82.66]:35082 "EHLO mail-wm0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752445AbcGAOWb (ORCPT ); Fri, 1 Jul 2016 10:22:31 -0400 Received: by mail-wm0-f66.google.com with SMTP id a66so5672963wme.2 for ; Fri, 01 Jul 2016 07:22:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=resnulli-us.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=zEnjNPSMR+kTAvfNdiOe1jphGdvrSKQkdSq1NBW16Vg=; b=wUWfA0WO1gWi2wNHyV2UiMgtR3H+Q+fsLIzWVTFSEBvQKNK8MAd+WUnO3RNm0R463J 4RwkoM5HC90rFEd/CCuFpANdAXwFPQ7SeQdPQBRzZJruvsQiTI0x4nEWU3kX+0ggTLTn IbOfsz8tCNwDSnVSB6U1ppT7f7dGkBUeR+tlVuMRS/rAfKnU3IYurm3H5pi3AQFPjFn1 vymlhSNOM11dRZdFXisO9PXcirVS0NzClNudg5Iai8dLBweaWM8vm5GVJmFm35KmS2W9 ol5Roz+QhXlgVN4yLr7er6zbEFR+aGEHnNTpmwQruCSS9J4yz9EXmVOjvzIkBVVLe0eW Qlgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=zEnjNPSMR+kTAvfNdiOe1jphGdvrSKQkdSq1NBW16Vg=; b=KTOi+KyN8Ek+9xd6sBOelW3DeEwiwhTGFz9Dd37xTb0L66CDhr8muGoIkJj/B3C6On 9Nyzx+Y/9yDa65hZw4EM6G2riu34el61qv6MRipzR55ALshE0Vs12xUX80W7ZuuMhybR DKVmj6RLb6KWlDyWA/97MBXBGKvkH5e2hdyef58NricixtBHm60dlGHVbDf998kNruRk xZcvtsyONn2u0dA8LBYoAoyLxTU1dVWwYsDWCYq8ZKTWQLlRhi6ECcXWUyv/b7TeHpsB OWZaMoxUHzLyATlncMs3JlBWcCmQqU2Hhv1PhWSFwgBT1+Yd6tnLLnJ8wOsW1ff+3B2b bQqw== X-Gm-Message-State: ALyK8tKaPZa5Lpb1vN7W7gNHWCY4e69YGFR8rDpVzkclSpARFklDf7507zwhbPIlYVYXqw== X-Received: by 10.28.109.137 with SMTP id b9mr32132663wmi.68.1467381961925; Fri, 01 Jul 2016 07:06:01 -0700 (PDT) Received: from localhost (jirka.pirko.cz. [84.16.102.26]) by smtp.gmail.com with ESMTPSA id i74sm2598558wmg.21.2016.07.01.07.06.01 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 01 Jul 2016 07:06:01 -0700 (PDT) From: Jiri Pirko To: netdev@vger.kernel.org Cc: davem@davemloft.net, idosch@mellanox.com, yotamg@mellanox.com, eladr@mellanox.com, nogahf@mellanox.com, ogerlitz@mellanox.com, sfeldma@gmail.com, roopa@cumulusnetworks.com, andy@greyhouse.net, dsa@cumulusnetworks.com, tgraf@suug.ch, jhs@mojatatu.com, linville@tuxdriver.com, ivecera@redhat.com Subject: [patch net-next 33/42] mlxsw: spectrum_router: Periodically update the kernel's neigh table Date: Fri, 1 Jul 2016 16:05:01 +0200 Message-Id: <1467381910-3445-34-git-send-email-jiri@resnulli.us> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1467381910-3445-1-git-send-email-jiri@resnulli.us> References: <1467381910-3445-1-git-send-email-jiri@resnulli.us> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Yotam Gigi 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 Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 192 ++++++++++++++++++++- 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 734c5ba..9c2a60f 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 { struct mlxsw_sp_lpm_tree lpm_trees[MLXSW_SP_LPM_TREE_COUNT]; struct mlxsw_sp_vr vrs[MLXSW_SP_VIRTUAL_ROUTER_MAX]; struct rhashtable neigh_ht; + struct { + struct delayed_work dw; + unsigned long interval; /* ms */ + } neighs_update; }; struct mlxsw_sp { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 90d382a..db1c2c4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3,6 +3,7 @@ * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko * Copyright (c) 2016 Ido Schimmel + * Copyright (c) 2016 Yotam Gigi * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -676,14 +679,199 @@ void mlxsw_sp_router_neigh_destroy(struct net_device *dev, mlxsw_sp_neigh_entry_destroy(neigh_entry); } +static void +mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp) +{ + unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME); + + mlxsw_sp->router.neighs_update.interval = jiffies_to_msecs(interval); +} + +static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int ent_index) +{ + struct net_device *dev; + struct neighbour *n; + __be32 dipn; + u32 dip; + u16 rif; + + mlxsw_reg_rauhtd_ent_ipv4_unpack(rauhtd_pl, ent_index, &rif, &dip); + + if (!mlxsw_sp->rifs[rif]) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n"); + return; + } + + dipn = htonl(dip); + dev = mlxsw_sp->rifs[rif]->dev; + n = neigh_lookup(&arp_tbl, &dipn, dev); + if (!n) { + netdev_err(dev, "Failed to find matching neighbour for IP=%pI4h\n", + &dip); + return; + } + + netdev_dbg(dev, "Updating neighbour with IP=%pI4h\n", &dip); + neigh_event_send(n, NULL); + neigh_release(n); +} + +static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ + u8 num_entries; + int i; + + num_entries = mlxsw_reg_rauhtd_ipv4_rec_num_entries_get(rauhtd_pl, + rec_index); + /* Hardware starts counting at 0, so add 1. */ + num_entries++; + + /* Each record consists of several neighbour entries. */ + for (i = 0; i < num_entries; i++) { + int ent_index; + + ent_index = rec_index * MLXSW_REG_RAUHTD_IPV4_ENT_PER_REC + i; + mlxsw_sp_router_neigh_ent_ipv4_process(mlxsw_sp, rauhtd_pl, + ent_index); + } + +} + +static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, int rec_index) +{ + switch (mlxsw_reg_rauhtd_rec_type_get(rauhtd_pl, rec_index)) { + case MLXSW_REG_RAUHTD_TYPE_IPV4: + mlxsw_sp_router_neigh_rec_ipv4_process(mlxsw_sp, rauhtd_pl, + rec_index); + break; + case MLXSW_REG_RAUHTD_TYPE_IPV6: + WARN_ON_ONCE(1); + break; + } +} + +static void +mlxsw_sp_router_neighs_update_work_schedule(struct mlxsw_sp *mlxsw_sp) +{ + unsigned long interval = mlxsw_sp->router.neighs_update.interval; + + mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw, + msecs_to_jiffies(interval)); +} + +static void mlxsw_sp_router_neighs_update_work(struct work_struct *work) +{ + struct mlxsw_sp *mlxsw_sp; + char *rauhtd_pl; + u8 num_rec; + int i, err; + + rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL); + if (!rauhtd_pl) + return; + + mlxsw_sp = container_of(work, struct mlxsw_sp, + router.neighs_update.dw.work); + + /* Make sure the neighbour's netdev isn't removed in the + * process. + */ + rtnl_lock(); + do { + mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd), + rauhtd_pl); + if (err) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to dump neighbour talbe\n"); + break; + } + num_rec = mlxsw_reg_rauhtd_num_rec_get(rauhtd_pl); + for (i = 0; i < num_rec; i++) + mlxsw_sp_router_neigh_rec_process(mlxsw_sp, rauhtd_pl, + i); + } while (num_rec); + rtnl_unlock(); + + kfree(rauhtd_pl); + mlxsw_sp_router_neighs_update_work_schedule(mlxsw_sp); +} + +static int mlxsw_sp_router_netevent_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + struct mlxsw_sp *mlxsw_sp; + unsigned long interval; + struct neigh_parms *p; + + switch (event) { + case NETEVENT_DELAY_PROBE_TIME_UPDATE: + p = ptr; + + /* We don't care about changes in the default table. */ + if (!p->dev || p->tbl != &arp_tbl) + return NOTIFY_DONE; + + /* We are in atomic context and can't take RTNL mutex, + * so use RCU variant to walk the device chain. + */ + mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(p->dev); + if (!mlxsw_sp_port) + return NOTIFY_DONE; + + mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + interval = jiffies_to_msecs(NEIGH_VAR(p, DELAY_PROBE_TIME)); + mlxsw_sp->router.neighs_update.interval = interval; + + mlxsw_sp_port_dev_put(mlxsw_sp_port); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block mlxsw_sp_router_netevent_nb __read_mostly = { + .notifier_call = mlxsw_sp_router_netevent_event, +}; + static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp) { - return rhashtable_init(&mlxsw_sp->router.neigh_ht, - &mlxsw_sp_neigh_ht_params); + int err; + + err = rhashtable_init(&mlxsw_sp->router.neigh_ht, + &mlxsw_sp_neigh_ht_params); + if (err) + return err; + + /* Initialize the polling interval according to the default + * table. + */ + mlxsw_sp_router_neighs_update_interval_init(mlxsw_sp); + + err = register_netevent_notifier(&mlxsw_sp_router_netevent_nb); + if (err) + goto err_register_netevent_notifier; + + INIT_DELAYED_WORK(&mlxsw_sp->router.neighs_update.dw, + mlxsw_sp_router_neighs_update_work); + mlxsw_core_schedule_dw(&mlxsw_sp->router.neighs_update.dw, 0); + + return 0; + +err_register_netevent_notifier: + rhashtable_destroy(&mlxsw_sp->router.neigh_ht); + return err; } static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp) { + cancel_delayed_work_sync(&mlxsw_sp->router.neighs_update.dw); + unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); rhashtable_destroy(&mlxsw_sp->router.neigh_ht); }