From nobody Fri Apr 3 17:49:38 2026 Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E18393E716C; Tue, 24 Mar 2026 10:50:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=68.232.154.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774349465; cv=none; b=F+45Ubkc67JNh4UhRFS7d556JJXUVybJmRQeSKgtzV2uS28xoKDsHrZxkdWMYPlO1gzjDmXsm8kKJtTRlKFlTB/tGnwFaqBkMF312s/IG/0pS2urkLfX6vne5mYJdRo8kgRpV1/hqgjE6eg7aAjRs+CWJ6Jto5/GUywbjTHDmyo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774349465; c=relaxed/simple; bh=EsNzF3sc87MdfE8dBtV9lZybhab7jt4X5Ze/nLTZcmM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=bOJSjbzaszI3wt3DtaQ+9ObnfA5hYsVhqNX8KTciqxkRtWDhIFfaSCfNW+mfEqakVx6Zwef6jriSM1IL/VZmZZ3/04eU+DIbTnuHp82fvR+rMvYkxyv7+hT6XB3N3WJTX8qyfh+qYLvBZUS8SkayrE+WW9qP9nHHNtXZzZH5fj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=microchip.com; spf=pass smtp.mailfrom=microchip.com; dkim=pass (2048-bit key) header.d=microchip.com header.i=@microchip.com header.b=a7UD5pQk; arc=none smtp.client-ip=68.232.154.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=microchip.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=microchip.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=microchip.com header.i=@microchip.com header.b="a7UD5pQk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1774349459; x=1805885459; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=EsNzF3sc87MdfE8dBtV9lZybhab7jt4X5Ze/nLTZcmM=; b=a7UD5pQklnNlCvbpf42tZqVv4JW5pAPdZlvKR+3d8cmbfK10pALdUAiF LqnYvMMLox/6V0THdD3fsrLTW3MGUd0N0AfQL4L8ggdfI5z7NNbP21eY5 1jy+JRf0Vg18Z63Q/7JxhJcW7Eh8TucU+MzVxahHmodTyUfS068uAQrA/ b2ZXFrSxM8db56eY9tAk34cxk3DPDPWKqdXOTf21inUkOmsLPUc7NJY8z 8OENxgOkllxGIDui3VfnpQqBIBE2M9+sX9qn8nR/DrMhW5vMAd2w2JdSX Piq61vKoSQj9VGjwWUQ6DkAECbPFbRYR40HKpTJJw2RBkrSYXXVN9u9xb A==; X-CSE-ConnectionGUID: vgUXfekRRgebdU5TbZAl3Q== X-CSE-MsgGUID: MJr9pPFES4mlUQuKzUhVbA== X-IronPort-AV: E=Sophos;i="6.23,138,1770620400"; d="scan'208";a="54371283" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa4.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 24 Mar 2026 03:50:54 -0700 Received: from chn-vm-ex02.mchp-main.com (10.10.85.144) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.58; Tue, 24 Mar 2026 03:50:34 -0700 Received: from [127.0.0.1] (10.10.85.11) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server id 15.1.2507.58 via Frontend Transport; Tue, 24 Mar 2026 03:50:31 -0700 From: =?utf-8?q?Jens_Emil_Schulz_=C3=98stergaard?= Date: Tue, 24 Mar 2026 11:46:51 +0100 Subject: [PATCH net-next v2 8/9] net: dsa: lan9645x: add mdb management Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260324-dsa_lan9645x_switch_driver_base-v2-8-f7504e3b0681@microchip.com> References: <20260324-dsa_lan9645x_switch_driver_base-v2-0-f7504e3b0681@microchip.com> In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-0-f7504e3b0681@microchip.com> To: , Andrew Lunn , "Vladimir Oltean" , "David S. Miller" , "Eric Dumazet" , Jakub Kicinski , Paolo Abeni , Simon Horman , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Woojung Huh , Russell King , Steen Hegelund , Daniel Machon CC: , , , =?utf-8?q?Jens_Emil_Schulz_=C3=98stergaard?= X-Mailer: b4 0.15-dev Add support for dsa mdb callbacks. L2 multicast and IP multicast is handled differently. IP multicast stores the port group mask inline in the mac table. L2 multicast points to a PGID index, which encodes the port group mask. Reviewed-by: Steen Hegelund Signed-off-by: Jens Emil Schulz =C3=98stergaard --- Changes in v2: - New file: selftests required implementation of the mdb callbacks. --- drivers/net/dsa/microchip/lan9645x/Makefile | 1 + drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c | 14 + drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 61 ++++ drivers/net/dsa/microchip/lan9645x/lan9645x_main.h | 21 ++ drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c | 391 +++++++++++++++++= ++++ drivers/net/dsa/microchip/lan9645x/lan9645x_port.c | 8 + include/linux/dsa/lan9645x.h | 11 + net/dsa/tag_lan9645x.c | 19 +- 8 files changed, 524 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/= microchip/lan9645x/Makefile index 70815edca5b9..2413d11fe849 100644 --- a/drivers/net/dsa/microchip/lan9645x/Makefile +++ b/drivers/net/dsa/microchip/lan9645x/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_NET_DSA_MICROCHIP_LAN9645X) +=3D mchp-lan9645x= .o mchp-lan9645x-objs :=3D \ lan9645x_mac.o \ lan9645x_main.o \ + lan9645x_mdb.o \ lan9645x_npi.o \ lan9645x_phylink.o \ lan9645x_port.o \ diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c b/drivers/ne= t/dsa/microchip/lan9645x/lan9645x_mac.c index 6335714dca21..13bf027af969 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c @@ -215,6 +215,20 @@ int lan9645x_mact_learn(struct lan9645x *lan9645x, int= port, return err; } =20 +int lan9645x_mact_learn_cpu_copy(struct lan9645x *lan9645x, int port, + const unsigned char *addr, u16 vid, + enum macaccess_entry_type type, bool cpu_copy) +{ + int err; + + mutex_lock(&lan9645x->mact_lock); + err =3D __lan9645x_mact_learn_cpu_copy(lan9645x, port, addr, vid, type, + cpu_copy); + mutex_unlock(&lan9645x->mact_lock); + + return err; +} + int lan9645x_mact_flush(struct lan9645x *lan9645x, int port) { int err; diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/n= et/dsa/microchip/lan9645x/lan9645x_main.c index 32c0301030a4..24a60f40f6b8 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c @@ -72,6 +72,7 @@ static void lan9645x_teardown(struct dsa_switch *ds) destroy_workqueue(lan9645x->owq); lan9645x_npi_port_deinit(lan9645x, lan9645x->npi); lan9645x_mac_deinit(lan9645x); + lan9645x_mdb_deinit(lan9645x); mutex_destroy(&lan9645x->fwd_domain_lock); } =20 @@ -159,6 +160,7 @@ static int lan9645x_setup(struct dsa_switch *ds) mutex_init(&lan9645x->fwd_domain_lock); lan9645x_vlan_init(lan9645x); lan9645x_mac_init(lan9645x); + lan9645x_mdb_init(lan9645x); =20 /* Link Aggregation Mode: NETDEV_LAG_HASH_L2 */ lan_wr(ANA_AGGR_CFG_AC_SMAC_ENA | @@ -669,6 +671,61 @@ static int lan9645x_fdb_del(struct dsa_switch *ds, int= port, return err; } =20 +static int lan9645x_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct net_device *bridge_dev =3D lan9645x_db2bridge(db); + struct lan9645x *lan9645x =3D ds->priv; + + dev_dbg(lan9645x->dev, "port=3D%d addr=3D%pM vid=3D%u\n", port, mdb->addr, + mdb->vid); + + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_mdb_present_in_other_db(ds, port, mdb, db)) + return 0; + + if (port =3D=3D lan9645x->npi) + port =3D CPU_PORT; + + return lan9645x_mdb_port_add(lan9645x, port, mdb, bridge_dev); +} + +static int lan9645x_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct net_device *bridge_dev =3D lan9645x_db2bridge(db); + struct lan9645x *lan9645x =3D ds->priv; + int err; + + dev_dbg(lan9645x->dev, "port=3D%d addr=3D%pM vid=3D%u\n", port, mdb->addr, + mdb->vid); + + if (IS_ERR(bridge_dev)) + return PTR_ERR(bridge_dev); + + if (dsa_is_cpu_port(ds, port) && !bridge_dev && + dsa_mdb_present_in_other_db(ds, port, mdb, db)) + return 0; + + if (port =3D=3D lan9645x->npi) + port =3D CPU_PORT; + + err =3D lan9645x_mdb_port_del(lan9645x, port, mdb, bridge_dev); + if (err =3D=3D -ENOENT) { + dev_dbg(lan9645x->dev, + "mdb not found port=3D%d addr=3D%pM vid=3D%u\n", port, + mdb->addr, mdb->vid); + return 0; + } + + return err; +} + static const struct dsa_switch_ops lan9645x_switch_ops =3D { .get_tag_protocol =3D lan9645x_get_tag_protocol, =20 @@ -702,6 +759,10 @@ static const struct dsa_switch_ops lan9645x_switch_ops= =3D { .port_fdb_dump =3D lan9645x_fdb_dump, .port_fdb_add =3D lan9645x_fdb_add, .port_fdb_del =3D lan9645x_fdb_del, + + /* Multicast database */ + .port_mdb_add =3D lan9645x_mdb_add, + .port_mdb_del =3D lan9645x_mdb_del, }; =20 static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x) diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/n= et/dsa/microchip/lan9645x/lan9645x_main.h index 012d5bb17013..c7e4276dd74a 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h @@ -218,6 +218,14 @@ struct lan9645x { /* VLAN entries */ struct lan9645x_vlan vlans[VLAN_N_VID]; =20 + /* Multicast Forwarding Database */ + struct list_head mdb_entries; + struct list_head pgid_entries; + /* lock for mdb_entries and pgid_entries. Must be taken before mact_lock + * if both are taken. + */ + struct mutex mdb_lock; + int num_port_dis; bool dd_dis; bool tsn_dis; @@ -422,5 +430,18 @@ int lan9645x_mact_entry_del(struct lan9645x *lan9645x,= int pgid, const unsigned char *mac, u16 vid); int lan9645x_mact_entry_add(struct lan9645x *lan9645x, int pgid, const unsigned char *mac, u16 vid); +int lan9645x_mact_learn_cpu_copy(struct lan9645x *lan9645x, int port, + const unsigned char *addr, u16 vid, + enum macaccess_entry_type type, bool cpu_copy); + +/* Multicast Database lan9645x_mdb.c */ +int lan9645x_mdb_port_add(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge); +int lan9645x_mdb_port_del(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge); +void lan9645x_mdb_init(struct lan9645x *lan9645x); +void lan9645x_mdb_deinit(struct lan9645x *lan9645x); =20 #endif /* __LAN9645X_MAIN_H__ */ diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c b/drivers/ne= t/dsa/microchip/lan9645x/lan9645x_mdb.c new file mode 100644 index 000000000000..4cf92887a4eb --- /dev/null +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2026 Microchip Technology Inc. + */ + +#include "lan9645x_main.h" + +/* HW ignores dest_idx for IPv4/IPv6 types, so we use this dummy index */ +#define IP_ENTRY_PGID 0 + +struct lan9645x_pgid_entry { + struct list_head list; + int index; + refcount_t refcount; + u16 ports; +}; + +struct lan9645x_mdb_entry { + struct list_head list; + unsigned char mac[ETH_ALEN]; + u16 vid; + u16 ports; + struct lan9645x_pgid_entry *pgid; +}; + +void lan9645x_mdb_deinit(struct lan9645x *lan9645x) +{ + mutex_destroy(&lan9645x->mdb_lock); +} + +void lan9645x_mdb_init(struct lan9645x *lan9645x) +{ + INIT_LIST_HEAD(&lan9645x->mdb_entries); + INIT_LIST_HEAD(&lan9645x->pgid_entries); + mutex_init(&lan9645x->mdb_lock); + + /* Use CPU queues to communicate frame classification to the CPU */ + lan_rmw(ANA_CPUQ_CFG_CPUQ_IGMP_SET(LAN9645X_CPUQ_IGMP) | + ANA_CPUQ_CFG_CPUQ_MLD_SET(LAN9645X_CPUQ_MLD) | + ANA_CPUQ_CFG_CPUQ_IPMC_CTRL_SET(LAN9645X_CPUQ_IPMC_CTRL), + ANA_CPUQ_CFG_CPUQ_IGMP | + ANA_CPUQ_CFG_CPUQ_MLD | + ANA_CPUQ_CFG_CPUQ_IPMC_CTRL, + lan9645x, ANA_CPUQ_CFG); +} + +static enum macaccess_entry_type lan9645x_mdb_classify(const unsigned char= *mac) +{ + if (ether_addr_is_ipv4_mcast(mac)) + return ENTRYTYPE_MACV4; + if (ether_addr_is_ipv6_mcast(mac)) + return ENTRYTYPE_MACV6; + return ENTRYTYPE_LOCKED; +} + +static int lan9645x_mdb_pgid_index(struct lan9645x_mdb_entry *mdb_entry, + enum macaccess_entry_type type) +{ + switch (type) { + case ENTRYTYPE_MACV4: + case ENTRYTYPE_MACV6: + return IP_ENTRY_PGID; + default: + return mdb_entry->pgid->index; + } +} + +static struct lan9645x_mdb_entry * +lan9645x_mdb_entry_lookup(struct lan9645x *lan9645x, const unsigned char *= mac, + u16 vid) +{ + struct lan9645x_mdb_entry *mdb; + + list_for_each_entry(mdb, &lan9645x->mdb_entries, list) { + if (ether_addr_equal(mdb->mac, mac) && mdb->vid =3D=3D vid) + return mdb; + } + + return NULL; +} + +static struct lan9645x_mdb_entry * +lan9645x_mdb_entry_alloc(struct lan9645x *lan9645x, + const unsigned char addr[ETH_ALEN], u16 vid) +{ + struct lan9645x_mdb_entry *mdb_entry; + + mdb_entry =3D kzalloc_obj(*mdb_entry); + if (!mdb_entry) + return ERR_PTR(-ENOMEM); + + ether_addr_copy(mdb_entry->mac, addr); + mdb_entry->vid =3D vid; + + list_add_tail(&mdb_entry->list, &lan9645x->mdb_entries); + + dev_dbg(lan9645x->dev, "vid=3D%u addr=3D%pM\n", mdb_entry->vid, + mdb_entry->mac); + + return mdb_entry; +} + +static void lan9645x_mdb_encode_mac(unsigned char *mac, + struct lan9645x_mdb_entry *mdb_entry, + enum macaccess_entry_type type) +{ + ether_addr_copy(mac, mdb_entry->mac); + + /* The HW encodes the portmask in the high bits of the mac for ip + * multicast entries, to save on the limited PGID resources. + * + * IPv4 Multicast DMAC: 0x01005Exxxxxx + * IPv6 Multicast DMAC: 0x3333xxxxxxxx + * + * which gives us 24 or 16 bits to encode the portmask. + */ + if (type =3D=3D ENTRYTYPE_MACV4) { + mac[0] =3D 0; + mac[1] =3D mdb_entry->ports >> 8; + mac[2] =3D mdb_entry->ports & 0xff; + } else if (type =3D=3D ENTRYTYPE_MACV6) { + mac[0] =3D mdb_entry->ports >> 8; + mac[1] =3D mdb_entry->ports & 0xff; + } +} + +static void lan9645x_pgid_entry_put(struct lan9645x *lan9645x, + struct lan9645x_pgid_entry *pgid_entry) +{ + if (!pgid_entry) + return; + + if (!refcount_dec_and_test(&pgid_entry->refcount)) + return; + + dev_dbg(lan9645x->dev, "pgid=3D%d ports=3D0x%x", pgid_entry->index, + pgid_entry->ports); + /* We leave the PGID written in HW, as no entry is pointing to it. */ + list_del(&pgid_entry->list); + kfree(pgid_entry); +} + +static void lan9645x_mdb_entry_dealloc(struct lan9645x *lan9645x, + struct lan9645x_mdb_entry *mdb_entry) +{ + dev_dbg(lan9645x->dev, "vid=3D%u addr=3D%pM\n", mdb_entry->vid, + mdb_entry->mac); + list_del(&mdb_entry->list); + lan9645x_pgid_entry_put(lan9645x, mdb_entry->pgid); + kfree(mdb_entry); +} + +static struct lan9645x_pgid_entry * +lan9645x_mdb_pgid_entry_lookup(struct lan9645x *lan9645x, u16 ports) +{ + struct lan9645x_pgid_entry *pgid_entry; + + list_for_each_entry(pgid_entry, &lan9645x->pgid_entries, list) { + if (pgid_entry->ports =3D=3D ports && + refcount_inc_not_zero(&pgid_entry->refcount)) + return pgid_entry; + } + + return NULL; +} + +static struct lan9645x_pgid_entry * +lan9645x_pgid_entry_alloc(struct lan9645x *lan9645x, int index, u16 ports) +{ + struct lan9645x_pgid_entry *pgid_entry; + + pgid_entry =3D kzalloc_obj(*pgid_entry); + if (!pgid_entry) + return ERR_PTR(-ENOMEM); + + pgid_entry->ports =3D ports; + pgid_entry->index =3D index; + refcount_set(&pgid_entry->refcount, 1); + + list_add_tail(&pgid_entry->list, &lan9645x->pgid_entries); + + dev_dbg(lan9645x->dev, "index=3D%d ports=3D0x%x", pgid_entry->index, + pgid_entry->ports); + + lan_rmw(ANA_PGID_PGID_SET(pgid_entry->ports), + ANA_PGID_PGID, lan9645x, + ANA_PGID(pgid_entry->index)); + + return pgid_entry; +} + +static struct lan9645x_pgid_entry * +lan9645x_mdb_pgid_entry_create(struct lan9645x *lan9645x, u16 ports) +{ + struct lan9645x_pgid_entry *pgid_entry =3D NULL; + int index; + + for (index =3D PGID_GP_START; index < PGID_GP_END; index++) { + bool used =3D false; + + list_for_each_entry(pgid_entry, &lan9645x->pgid_entries, list) { + if (pgid_entry->index =3D=3D index) { + used =3D true; + break; + } + } + + if (!used) + return lan9645x_pgid_entry_alloc(lan9645x, index, + ports); + } + + return ERR_PTR(-ENOSPC); +} + +static struct lan9645x_pgid_entry * +lan9645x_mdb_pgid_entry_get(struct lan9645x *lan9645x, + struct lan9645x_mdb_entry *mdb_entry, + enum macaccess_entry_type type) +{ + struct lan9645x_pgid_entry *pgid_entry; + u16 pgid_ports; + + if (type =3D=3D ENTRYTYPE_MACV4 || type =3D=3D ENTRYTYPE_MACV6 || + !mdb_entry->ports) + return NULL; + + /* CPU_PORT forwarding is handled by cpu_copy flag on mac table entry. + * So we can strip CPU_PORT here to allow better PGID sharing. + */ + pgid_ports =3D mdb_entry->ports & ~BIT(CPU_PORT); + + pgid_entry =3D lan9645x_mdb_pgid_entry_lookup(lan9645x, pgid_ports); + if (!pgid_entry) + return lan9645x_mdb_pgid_entry_create(lan9645x, pgid_ports); + + return pgid_entry; +} + +static int __lan9645x_mdb_add(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + struct lan9645x_pgid_entry *old_pgid, *new_pgid; + struct lan9645x_mdb_entry *mdb_entry; + unsigned char mac[ETH_ALEN]; + int err, pgid_index; + bool cpu_copy; + + mdb_entry =3D lan9645x_mdb_entry_lookup(lan9645x, addr, vid); + if (!mdb_entry) { + mdb_entry =3D lan9645x_mdb_entry_alloc(lan9645x, addr, vid); + if (IS_ERR(mdb_entry)) + return PTR_ERR(mdb_entry); + } + + if (mdb_entry->ports & BIT(chip_port)) + return 0; + + mdb_entry->ports |=3D BIT(chip_port); + + /* Encode mac for IP mc */ + lan9645x_mdb_encode_mac(mac, mdb_entry, type); + + /* Update PGID ptr for non-IP entries (L2 multicast) */ + old_pgid =3D mdb_entry->pgid; + new_pgid =3D lan9645x_mdb_pgid_entry_get(lan9645x, mdb_entry, type); + if (IS_ERR(new_pgid)) { + /* Out of PGIDs or mem. Continue forwarding to old port + * group, or remove if fresh mdb_entry. + */ + mdb_entry->ports &=3D ~BIT(chip_port); + if (!mdb_entry->ports) + lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry); + + return PTR_ERR(new_pgid); + } + mdb_entry->pgid =3D new_pgid; + + cpu_copy =3D !!(mdb_entry->ports & BIT(CPU_PORT)); + pgid_index =3D lan9645x_mdb_pgid_index(mdb_entry, type); + + /* Make sure to write on top of existing entry, so we do not disrupt + * flowing traffic. + */ + err =3D lan9645x_mact_learn_cpu_copy(lan9645x, pgid_index, mac, + mdb_entry->vid, type, cpu_copy); + lan9645x_pgid_entry_put(lan9645x, old_pgid); + return err; +} + +static int __lan9645x_mdb_del(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + struct lan9645x_pgid_entry *old_pgid, *new_pgid; + struct lan9645x_mdb_entry *mdb_entry; + unsigned char mac[ETH_ALEN]; + int err, pgid_index; + bool cpu_copy; + + mdb_entry =3D lan9645x_mdb_entry_lookup(lan9645x, addr, vid); + if (!mdb_entry) + return -ENOENT; + + if (!(mdb_entry->ports & BIT(chip_port))) + return 0; + + mdb_entry->ports &=3D ~BIT(chip_port); + + /* Encode mac for IP mc */ + lan9645x_mdb_encode_mac(mac, mdb_entry, type); + + /* Update PGID ptr for non-IP entries (L2 multicast) */ + old_pgid =3D mdb_entry->pgid; + new_pgid =3D lan9645x_mdb_pgid_entry_get(lan9645x, mdb_entry, type); + if (IS_ERR(new_pgid)) { + /* Continue forwarding to old port group. */ + mdb_entry->ports |=3D BIT(chip_port); + return PTR_ERR(new_pgid); + } + mdb_entry->pgid =3D new_pgid; + + if (!mdb_entry->ports) { + lan9645x_mact_forget(lan9645x, mac, mdb_entry->vid, type); + lan9645x_pgid_entry_put(lan9645x, old_pgid); + lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry); + return 0; + } + + cpu_copy =3D !!(mdb_entry->ports & BIT(CPU_PORT)); + pgid_index =3D lan9645x_mdb_pgid_index(mdb_entry, type); + + err =3D lan9645x_mact_learn_cpu_copy(lan9645x, pgid_index, mac, + mdb_entry->vid, type, cpu_copy); + lan9645x_pgid_entry_put(lan9645x, old_pgid); + return err; +} + +static int lan9645x_mdb_add(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + int err; + + mutex_lock(&lan9645x->mdb_lock); + err =3D __lan9645x_mdb_add(lan9645x, chip_port, addr, vid, type); + mutex_unlock(&lan9645x->mdb_lock); + return err; +} + +static int lan9645x_mdb_del(struct lan9645x *lan9645x, int chip_port, + const unsigned char addr[ETH_ALEN], u16 vid, + enum macaccess_entry_type type) +{ + int err; + + mutex_lock(&lan9645x->mdb_lock); + err =3D __lan9645x_mdb_del(lan9645x, chip_port, addr, vid, type); + mutex_unlock(&lan9645x->mdb_lock); + return err; +} + +int lan9645x_mdb_port_add(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge) +{ + enum macaccess_entry_type type; + u16 vid =3D mdb->vid; + + type =3D lan9645x_mdb_classify(mdb->addr); + + if (!vid) + vid =3D lan9645x_vlan_unaware_pvid(!!bridge); + + return lan9645x_mdb_add(lan9645x, port, mdb->addr, vid, type); +} + +int lan9645x_mdb_port_del(struct lan9645x *lan9645x, int port, + const struct switchdev_obj_port_mdb *mdb, + struct net_device *bridge) +{ + enum macaccess_entry_type type; + u16 vid =3D mdb->vid; + + type =3D lan9645x_mdb_classify(mdb->addr); + + if (!vid) + vid =3D lan9645x_vlan_unaware_pvid(!!bridge); + + return lan9645x_mdb_del(lan9645x, port, mdb->addr, vid, type); +} diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c b/drivers/n= et/dsa/microchip/lan9645x/lan9645x_port.c index 3fe8de0ce902..2c13249e79d3 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c @@ -175,6 +175,14 @@ int lan9645x_port_setup(struct dsa_switch *ds, int por= t) ANA_PORT_CFG_PORTID_VAL, lan9645x, ANA_PORT_CFG(p->chip_port)); =20 + lan_rmw(ANA_CPU_FWD_CFG_IGMP_REDIR_ENA_SET(true) | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA_SET(true) | + ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA_SET(true), + ANA_CPU_FWD_CFG_IGMP_REDIR_ENA | + ANA_CPU_FWD_CFG_MLD_REDIR_ENA | + ANA_CPU_FWD_CFG_IPMC_CTRL_COPY_ENA, + lan9645x, ANA_CPU_FWD_CFG(p->chip_port)); + if (p->chip_port !=3D lan9645x->npi) lan9645x_vlan_set_hostmode(p); =20 diff --git a/include/linux/dsa/lan9645x.h b/include/linux/dsa/lan9645x.h index 34c18bf975d0..ac0e70c704a5 100644 --- a/include/linux/dsa/lan9645x.h +++ b/include/linux/dsa/lan9645x.h @@ -131,4 +131,15 @@ #define IFH_DUPL_DISC_ENA_SZ 1 #define IFH_RCT_AVAIL_SZ 1 =20 +/* Chip has 8 cpu queues. The cpu queues used by a frame is passed as a ma= sk in + * the IFH on extraction. We use this to avoid classifying IGMP and MLD fr= ames + * in the tag driver. + */ +enum { + LAN9645X_CPUQ_DEF =3D 0, + LAN9645X_CPUQ_IGMP =3D 1, + LAN9645X_CPUQ_MLD =3D 2, + LAN9645X_CPUQ_IPMC_CTRL =3D 3, +}; + #endif /* _NET_DSA_TAG_LAN9645X_H_ */ diff --git a/net/dsa/tag_lan9645x.c b/net/dsa/tag_lan9645x.c index 9130fb0d61be..e15ed29e5e87 100644 --- a/net/dsa/tag_lan9645x.c +++ b/net/dsa/tag_lan9645x.c @@ -152,6 +152,20 @@ static void lan9645x_xmit_get_vlan_info(struct sk_buff= *skb, LAN9645X_IFH_TAG_TYPE_C; } =20 +static void lan9645x_offload_fwd_mark(struct sk_buff *skb, u32 cpuq) +{ + u32 cpu_redir; + + /* IGMP/MLD are trapped to CPU, and must be forwarded by the stack */ + cpu_redir =3D BIT(LAN9645X_CPUQ_IGMP) | BIT(LAN9645X_CPUQ_MLD); + if (cpuq & cpu_redir) { + skb->offload_fwd_mark =3D 0; + return; + } + + return dsa_default_offload_fwd_mark(skb); +} + static struct sk_buff *lan9645x_xmit(struct sk_buff *skb, struct net_device *ndev) { @@ -192,7 +206,7 @@ static struct sk_buff *lan9645x_xmit(struct sk_buff *sk= b, static struct sk_buff *lan9645x_rcv(struct sk_buff *skb, struct net_device *ndev) { - u32 src_port, qos_class, vlan_tci, tag_type, popcnt, etype_ofs; + u32 src_port, qos_class, vlan_tci, tag_type, popcnt, etype_ofs, cpuq; u8 *orig_skb_data =3D skb->data; struct dsa_port *dp; u32 ifh_gap_len =3D 0; @@ -212,6 +226,7 @@ static struct sk_buff *lan9645x_rcv(struct sk_buff *skb, tag_type =3D lan9645x_ifh_get(ifh, IFH_TAG_TYPE, IFH_TAG_TYPE_SZ); vlan_tci =3D lan9645x_ifh_get(ifh, IFH_TCI, IFH_TCI_SZ); qos_class =3D lan9645x_ifh_get(ifh, IFH_QOS_CLASS, IFH_QOS_CLASS_SZ); + cpuq =3D lan9645x_ifh_get(ifh, IFH_CPUQ, IFH_CPUQ_SZ); =20 /* Set skb->data at start of real header * @@ -242,7 +257,7 @@ static struct sk_buff *lan9645x_rcv(struct sk_buff *skb, return NULL; } =20 - dsa_default_offload_fwd_mark(skb); + lan9645x_offload_fwd_mark(skb, cpuq); =20 skb->priority =3D qos_class; =20 --=20 2.52.0