From patchwork Sun Mar 24 03:23:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Oltean X-Patchwork-Id: 1062818 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="hfjfpmLu"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44RjT7757Dz9sSZ for ; Sun, 24 Mar 2019 14:25:31 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728239AbfCXDZW (ORCPT ); Sat, 23 Mar 2019 23:25:22 -0400 Received: from mail-wm1-f65.google.com ([209.85.128.65]:32809 "EHLO mail-wm1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727706AbfCXDZQ (ORCPT ); Sat, 23 Mar 2019 23:25:16 -0400 Received: by mail-wm1-f65.google.com with SMTP id z6so7033320wmi.0 for ; Sat, 23 Mar 2019 20:25:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=n66jCMFrfgg26kHTc2XqEHaRJBtIHU/xcZFihfo/FRs=; b=hfjfpmLuVH3wEmlQ3IdnUR5Sgo6RiEcU1H8w5OwYYMZ1bWrSiTN3b8ogl/D4ngPtcA OiFElvMyUT8Ia5dEee45gLla5j3VDv5uPwbFjaAfJOhz1BPjfiTr1Plc6UE3WWx5cZk0 XMP50NZ+OrezHZTpd5RJhgv+jLkGuwUHugYnpfCowiqYTN3dHt7VkXrgZAX9LpGsRkBi KSbhgx6zk6Kw2LsMFHHl06s+SdiM47Kegx5Kc5yrOA0SGV5mcF4nLWU/PGOgHyOxii7N Dr/CvdMruM3al+ozsgT/cu4bv1u2a3SkggxLsBCSJMCmGbe/8fALyRnDvzDt+e1YEmwS /aBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=n66jCMFrfgg26kHTc2XqEHaRJBtIHU/xcZFihfo/FRs=; b=HI3ECy/CHxEgKcF3IdV/YaxTgpmkcq8rXqKoH3hqDduL+fVE3lnKSd8EmWiOBZho3B SQZIC/i09Oo3V5j0y1M3mMp07D1QdZpaWCrnJ12Tsmto63FMoBzIQAR9v6dOr7nlSOLy luuzxoilYp9qfYcx8zZtuGl9iTYj903+AuMUj6j8XyxZWajvcr+nAhxrAXiRE5I8qQZm HktJFiKjtTLWe8vxjcauY3xKdkoHdBrW54QnI04lX3lKRt1+AGaZu/9U0wPdlQRuSdfs i4bT0TPvP3+89KlIQTX9EATpEpF0UY71HeHjG4bAdXHSN6vnAHIZAegoDfI/Yn9xKvcu e2sw== X-Gm-Message-State: APjAAAU5Lsfr49t4eSyTmBfOgDSnh9Rpfjw3kK7B3NoK4845NIVQGTmz 5xWytBUX+pUhb4ZXgm01fP4= X-Google-Smtp-Source: APXvYqzj5yJxn8cDMM5c3JlZ+FRFt92t+pJDh19qgz0ShA+hg5Jcp5/B8eS56OHH4Q1DAYPB+8NzHw== X-Received: by 2002:a1c:6c17:: with SMTP id h23mr1366703wmc.122.1553397914155; Sat, 23 Mar 2019 20:25:14 -0700 (PDT) Received: from localhost.localdomain ([188.26.228.227]) by smtp.gmail.com with ESMTPSA id c20sm12243049wre.28.2019.03.23.20.25.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 23 Mar 2019 20:25:13 -0700 (PDT) From: Vladimir Oltean To: davem@davemloft.net, netdev@vger.kernel.org Cc: f.fainelli@gmail.com, andrew@lunn.ch, vivien.didelot@gmail.com, linus.walleij@linaro.org, Vladimir Oltean Subject: [RFC PATCH net-next 07/13] net: dsa: sja1105: Add support for FDB and MDB management Date: Sun, 24 Mar 2019 05:23:40 +0200 Message-Id: <20190324032346.32394-8-olteanv@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190324032346.32394-1-olteanv@gmail.com> References: <20190324032346.32394-1-olteanv@gmail.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Currently only the (more difficult) first generation E/T series is supported. Here the TCAM is only 4-way associative, and to know where the hardware will search for a FDB entry, we need to perform the same hash algorithm in order to install the entry in the correct bin. On P/Q/R/S, the TCAM should be fully associative. However the SPI command interface is different, and because I don't have access to a new-generation device at the moment, support for it is TODO. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli --- drivers/net/dsa/sja1105/sja1105_main.c | 193 +++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 78bdb577c16b..afcca9926497 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -190,6 +190,9 @@ static int sja1105_init_static_fdb(struct sja1105_private *priv) table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; + /* We only populate the FDB table through dynamic + * L2 Address Lookup entries + */ if (table->entry_count) { kfree(table->entries); table->entry_count = 0; @@ -703,6 +706,190 @@ static void sja1105_adjust_link(struct dsa_switch *ds, int port, sja1105_adjust_port_config(priv, port, phydev->speed, true); } +#define fdb(bin, index) \ + ((bin) * SJA1105ET_FDB_BIN_SIZE + (index)) +#define is_bin_index_valid(i) \ + ((i) >= 0 && (i) < SJA1105ET_FDB_BIN_SIZE) + +static int +sja1105_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, + const u8 *addr, u16 vid, + struct sja1105_l2_lookup_entry *fdb_match, + int *last_unused) +{ + int index_in_bin; + + for (index_in_bin = 0; index_in_bin < SJA1105ET_FDB_BIN_SIZE; + index_in_bin++) { + struct sja1105_l2_lookup_entry l2_lookup = { 0 }; + + /* Skip unused entries, optionally marking them + * into the return value + */ + if (sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + fdb(bin, index_in_bin), + &l2_lookup)) { + if (last_unused) + *last_unused = index_in_bin; + continue; + } + + if (l2_lookup.macaddr == ether_addr_to_u64(addr) && + l2_lookup.vlanid == vid) { + if (fdb_match) + *fdb_match = l2_lookup; + return index_in_bin; + } + } + /* Return an invalid entry index if not found */ + return SJA1105ET_FDB_BIN_SIZE; +} + +static int sja1105_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct sja1105_l2_lookup_entry l2_lookup = { 0 }; + struct sja1105_private *priv = ds->priv; + struct device *dev = ds->dev; + int bin, index_in_bin; + int last_unused; + + bin = sja1105_fdb_hash(priv, addr, vid); + + index_in_bin = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid, + &l2_lookup, &last_unused); + if (is_bin_index_valid(index_in_bin)) { + /* We have an FDB entry. Is our port in the destination + * mask? If yes, we need to do nothing. If not, we need + * to rewrite the entry by adding this port to it. + */ + if (l2_lookup.destports & BIT(port)) + return 0; + l2_lookup.destports |= BIT(port); + } else { + /* We don't have an FDB entry. We construct a new one and + * try to find a place for it within the FDB table. + */ + l2_lookup.macaddr = ether_addr_to_u64(addr); + l2_lookup.destports = BIT(port); + l2_lookup.vlanid = vid; + + if (is_bin_index_valid(last_unused)) { + index_in_bin = last_unused; + } else { + /* Bin is full, need to evict somebody. + * Choose victim at random. If you get these messages + * often, you may need to consider changing the + * distribution function: + * static_config[BLK_IDX_L2_LOOKUP_PARAMS].entries->poly + */ + get_random_bytes(&index_in_bin, sizeof(u8)); + index_in_bin %= SJA1105ET_FDB_BIN_SIZE; + dev_warn(dev, "Warning, FDB bin %d full while adding entry for %pM. Evicting entry %u.\n", + bin, addr, index_in_bin); + /* Evict entry */ + sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + fdb(bin, index_in_bin), + NULL, false); + } + } + l2_lookup.index = fdb(bin, index_in_bin); + + return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup.index, &l2_lookup, true); +} + +static int sja1105_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct sja1105_l2_lookup_entry l2_lookup = { 0 }; + struct sja1105_private *priv = ds->priv; + u8 bin, index_in_bin; + bool keep; + + bin = sja1105_fdb_hash(priv, addr, vid); + + index_in_bin = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid, + &l2_lookup, NULL); + if (!is_bin_index_valid(index_in_bin)) + return 0; + + /* We have an FDB entry. Is our port in the destination mask? If yes, + * we need to remove it. If the resulting port mask becomes empty, we + * need to completely evict the FDB entry. + * Otherwise we just write it back. + */ + if (l2_lookup.destports & BIT(port)) + l2_lookup.destports &= ~BIT(port); + if (l2_lookup.destports) + keep = true; + else + keep = false; + + return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + fdb(bin, index_in_bin), + &l2_lookup, keep); +} + +static int sja1105_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct sja1105_private *priv = ds->priv; + struct device *dev = ds->dev; + int i; + + for (i = 0; i < MAX_L2_LOOKUP_COUNT; i++) { + struct sja1105_l2_lookup_entry l2_lookup; + u8 macaddr[ETH_ALEN]; + int rc; + + memset(&l2_lookup, 0, sizeof(l2_lookup)); + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + i, &l2_lookup); + /* No fdb entry at i, not an issue */ + if (rc == -EINVAL) + continue; + if (rc) { + dev_err(dev, "Failed to dump FDB: %d\n", rc); + return rc; + } + + /* FDB dump callback is per port. This means we have to + * disregard a valid entry if it's not for this port, even if + * only to revisit it later. This is inefficient because the + * 1024-sized FDB table needs to be traversed 4 times through + * SPI during a 'bridge fdb show' command. + */ + if (!(l2_lookup.destports & BIT(port))) + continue; + u64_to_ether_addr(l2_lookup.macaddr, macaddr); + cb(macaddr, l2_lookup.vlanid, false, data); + } + return 0; +} + +#undef fdb +#undef is_bin_index_valid + +/* This callback needs to be present */ +static int sja1105_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + return 0; +} + +static void sja1105_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + sja1105_fdb_add(ds, port, mdb->addr, mdb->vid); +} + +static int sja1105_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid); +} + static int sja1105_bridge_member(struct dsa_switch *ds, int port, struct net_device *br, bool member) { @@ -796,8 +983,14 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .get_tag_protocol = sja1105_get_tag_protocol, .setup = sja1105_setup, .adjust_link = sja1105_adjust_link, + .port_fdb_dump = sja1105_fdb_dump, + .port_fdb_add = sja1105_fdb_add, + .port_fdb_del = sja1105_fdb_del, .port_bridge_join = sja1105_bridge_join, .port_bridge_leave = sja1105_bridge_leave, + .port_mdb_prepare = sja1105_mdb_prepare, + .port_mdb_add = sja1105_mdb_add, + .port_mdb_del = sja1105_mdb_del, }; static int sja1105_probe(struct spi_device *spi)