From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D641BC43602 for ; Mon, 29 Jun 2026 16:18:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=7Nmcw54XMzkAgJYR0a11hlKp04c5pmgeuRkiUtqlYk8=; b=P+7mRnC6hc6FEs0q7VxTpSVxpi c9T5ENpj2bFKzc9UIHOwmaEy9H5bp46FmQKVnIVJwBIxwu3zQEov8Wr5BGxvIVdjb3+WUV/gotgMf Woohc5f4cx4A/V13bqaGkqZ5D+N0soXObSN61uJ0rIkOlCNNJcpdzX9wPoexpYLmzcS0OL7nT8/r6 gNHU2h/i6iOI6qU+52qN/mQgci1I2ZqAVvnfbRcbkwlRygBCJ77/UA2E904rb5DYgO9tTlR1UL4XO pgl7ELPQESgaQjpacwhnmQfwC6OT1AZ0Z9j/Q29wntfW7n7juIZFVuZYS6jYPYpWjL15UHFyNF2Tf FGD7f8NA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1weEgO-0000000FBYx-3pqT; Mon, 29 Jun 2026 16:18:04 +0000 Received: from sea.source.kernel.org ([172.234.252.31]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1weEgG-0000000FBUW-1zSR; Mon, 29 Jun 2026 16:17:56 +0000 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) by sea.source.kernel.org (Postfix) with ESMTP id 3BDA843885; Mon, 29 Jun 2026 16:17:56 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 937771F000E9; Mon, 29 Jun 2026 16:17:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1782749876; bh=7Nmcw54XMzkAgJYR0a11hlKp04c5pmgeuRkiUtqlYk8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=QXdCaQFxNTWK9Ix9WNJXxpGvNvjxItOups4hxW31SnjjwzbjvAduvs0WyoyjfxlUN 0Uxk9bdV2xmwAKA+bStnm+9ClFLzxa0nrkyeA9iwR4j7/YBejaNgSU5dW7lYOF+Hl2 /yj1UF2jtN1A3Vg/ePrVGYlRl5+wBEuAP7uP2zLjMll1Ez1lGZvx2W5V7zq3Ciawpo KpnKWAqceRoZ+xUVQBzzFhQ3btc4GKQFRijBiTtbLzGXIpGqLyfu2k5AGt3m4bf7by dnFVxlX6hx0h/u8U58T/nrI6lTpYEi9HYJguVq5Yh7+Y2rJoQsFDboIhbyoDA9Jc1Y zj5M0ZRxq6N0g== From: Lorenzo Bianconi Date: Mon, 29 Jun 2026 18:17:30 +0200 Subject: [PATCH net-next v6 2/2] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260629-airoha-ethtool-priv_flags-v6-2-86bc600d31bc@kernel.org> References: <20260629-airoha-ethtool-priv_flags-v6-0-86bc600d31bc@kernel.org> In-Reply-To: <20260629-airoha-ethtool-priv_flags-v6-0-86bc600d31bc@kernel.org> To: Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Lorenzo Bianconi Cc: Simon Horman , Alexander Lobakin , linux-arm-kernel@lists.infradead.org, linux-mediatek@lists.infradead.org, netdev@vger.kernel.org, Madhur Agrawal X-Mailer: b4 0.14.3 X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org GDM3 and GDM4 ports require GDM2 loopback to be enabled for hardware QoS offload to function. Without it, HTB and ETS offload on these ports do not work. Previously, GDM3/GDM4 ports were automatically configured as WAN with GDM2 loopback enabled during ndo_init(). Add the capability to configure GDM3/GDM4 as WAN/LAN on demand when QoS offload is created or destroyed. Hook airoha_enable_qos_for_gdm34() into TC_HTB_CREATE so that requesting HTB offload on a GDM3/GDM4 LAN port switches it to WAN mode and enables GDM2 loopback, with proper rollback on failure. Introduce the AIROHA_DEV_F_QOS flag to track whether a device has an active HTB qdisc; clear it on TC_HTB_DESTROY. The device keeps its WAN role after qdisc teardown so that its configuration is preserved until another device explicitly needs the WAN role for QoS offload. If another GDM3/GDM4 device already holds the WAN role without an active QoS qdisc, demote it to LAN before promoting the requesting device. Skip the demotion when the requesting device is itself already the WAN device. Since airoha_dev_set_qdma() can now be called on a running device to migrate between QDMA blocks, make dev->qdma an RCU pointer so the TX path can safely dereference it without holding RTNL. Hold flow_offload_mutex in airoha_enable_qos_for_gdm34() and airoha_disable_qos_for_gdm34() around the dev->flags update, airoha_dev_set_qdma() and GDM2 loopback configuration, serializing against concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER offload path. Introduce airoha_qdma_deref() helper that wraps rcu_dereference_protected() with a lockdep condition accepting either rtnl_lock or flow_offload_mutex, and use it across all control-path dereferences of the RCU-protected dev->qdma pointer. Add airoha_disable_gdm2_loopback() to disable GDM2 hw loopback. Tested-by: Madhur Agrawal Reviewed-by: Alexander Lobakin Reviewed-by: Simon Horman Signed-off-by: Lorenzo Bianconi --- drivers/net/ethernet/airoha/airoha_eth.c | 229 ++++++++++++++++++++++++++---- drivers/net/ethernet/airoha/airoha_eth.h | 13 +- drivers/net/ethernet/airoha/airoha_ppe.c | 9 +- drivers/net/ethernet/airoha/airoha_regs.h | 1 + 4 files changed, 219 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 8bba54ebcf07..231c8f2f20dd 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -929,7 +929,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) if (!dev) continue; - if (dev->qdma != qdma) + if (rcu_access_pointer(dev->qdma) != qdma) continue; netdev = netdev_from_priv(dev); @@ -1837,13 +1837,14 @@ static int airoha_dev_open(struct net_device *netdev) struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; u32 cur_len, pse_port = FE_PSE_PORT_PPE1; - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; netif_tx_start_all_queues(netdev); err = airoha_set_vip_for_gdm_port(dev, true); if (err) return err; + qdma = airoha_qdma_deref(dev); if (netdev_uses_dsa(netdev)) airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id), GDM_STAG_EN_MASK); @@ -1903,7 +1904,6 @@ static int airoha_dev_stop(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; - struct airoha_qdma *qdma = dev->qdma; netif_tx_disable(netdev); airoha_set_vip_for_gdm_port(dev, false); @@ -1911,7 +1911,7 @@ static int airoha_dev_stop(struct net_device *netdev) if (--port->users) airoha_set_port_mtu(dev->eth, port); else - airoha_set_gdm_port_fwd_cfg(qdma->eth, + airoha_set_gdm_port_fwd_cfg(dev->eth, REG_GDM_FWD_CFG(port->id), FE_PSE_PORT_DROP); return 0; @@ -1998,6 +1998,53 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) return 0; } +static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev) +{ + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + int i, src_port; + u32 pse_port; + + src_port = eth->soc->ops.get_sport(dev->port, dev->nbq); + if (src_port < 0) + return src_port; + + airoha_fe_clear(eth, + REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)), + SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK)); + + airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + GDM_STRIP_CRC_MASK); + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + FE_PSE_PORT_DROP); + airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX), + LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK); + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 + : FE_PSE_PORT_PPE1; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + pse_port); + + airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK, + FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX)); + + for (i = 0; i < eth->soc->num_ppe; i++) + airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX), + DFT_CPORT_MASK(AIROHA_GDM2_IDX)); + + /* Enable VIP and IFC for GDM2 */ + airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); + airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX)); + + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { + u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq); + + airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask, + FC_MAP6_DEF_VALUE & mask); + } + + return 0; +} + static struct airoha_gdm_dev * airoha_get_wan_gdm_dev(struct airoha_eth *eth) { @@ -2024,15 +2071,25 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth) static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev) { struct net_device *netdev = netdev_from_priv(dev); + struct airoha_qdma *cur_qdma, *qdma; struct airoha_eth *eth = dev->eth; int ppe_id; /* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */ - dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; - netdev->irq = dev->qdma->irq_banks[0].irq; + qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)]; + cur_qdma = airoha_qdma_deref(dev); + + rcu_assign_pointer(dev->qdma, qdma); + netdev->irq = qdma->irq_banks[0].irq; ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev)); + + if (!cur_qdma) + return; + + synchronize_rcu(); + netif_tx_wake_all_queues(netdev); } static int airoha_dev_init(struct net_device *netdev) @@ -2187,9 +2244,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; u32 nr_frags, tag, msg0, msg1, len; struct airoha_queue_entry *e; + struct airoha_qdma *qdma; struct netdev_queue *txq; struct airoha_queue *q; LIST_HEAD(tx_list); @@ -2198,6 +2255,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, u16 index; u8 fport; + rcu_read_lock(); + qdma = rcu_dereference(dev->qdma); qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); tag = airoha_get_dsa_tag(skb, netdev); @@ -2247,6 +2306,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, netif_tx_stop_queue(txq); q->txq_stopped = true; spin_unlock_bh(&q->lock); + rcu_read_unlock(); + return NETDEV_TX_BUSY; } @@ -2309,6 +2370,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); spin_unlock_bh(&q->lock); + rcu_read_unlock(); return NETDEV_TX_OK; @@ -2324,6 +2386,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, error: dev_kfree_skb_any(skb); netdev->stats.tx_dropped++; + rcu_read_unlock(); return NETDEV_TX_OK; } @@ -2403,17 +2466,19 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, const u16 *weights, u8 n_weights) { struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma; int i; + qdma = airoha_qdma_deref(dev); for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++) - airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel), + airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel), TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)); for (i = 0; i < n_weights; i++) { u32 status; int err; - airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG, + airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG, TWRR_RW_CMD_MASK | FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) | FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) | @@ -2421,12 +2486,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev, err = read_poll_timeout(airoha_qdma_rr, status, status & TWRR_RW_CMD_DONE, USEC_PER_MSEC, 10 * USEC_PER_MSEC, - true, dev->qdma, REG_TXWRR_WEIGHT_CFG); + true, qdma, REG_TXWRR_WEIGHT_CFG); if (err) return err; } - airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3), + airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3), CHAN_QOS_MODE_MASK(channel), __field_prep(CHAN_QOS_MODE_MASK(channel), mode)); @@ -2490,13 +2555,15 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel, struct tc_ets_qopt_offload *opt) { struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; + u64 cpu_tx_packets, fwd_tx_packets, tx_packets; + struct airoha_qdma *qdma; - u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1)); - u64 fwd_tx_packets = airoha_qdma_rr(qdma, - REG_CNTR_VAL((channel << 1) + 1)); - u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) + - (fwd_tx_packets - dev->fwd_tx_packets); + qdma = airoha_qdma_deref(dev); + cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1)); + fwd_tx_packets = airoha_qdma_rr(qdma, + REG_CNTR_VAL((channel << 1) + 1)); + tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) + + (fwd_tx_packets - dev->fwd_tx_packets); _bstats_update(opt->stats.bstats, 0, tx_packets); dev->cpu_tx_packets = cpu_tx_packets; @@ -2756,16 +2823,18 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev, u32 bucket_size) { struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_qdma *qdma; int i, err; + qdma = airoha_qdma_deref(dev); for (i = 0; i <= TRTCM_PEAK_MODE; i++) { - err = airoha_qdma_set_trtcm_config(dev->qdma, channel, + err = airoha_qdma_set_trtcm_config(qdma, channel, REG_EGRESS_TRTCM_CFG, i, !!rate, TRTCM_METER_MODE); if (err) return err; - err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel, + err = airoha_qdma_set_trtcm_token_bucket(qdma, channel, REG_EGRESS_TRTCM_CFG, i, rate, bucket_size); if (err) @@ -2801,11 +2870,12 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev, u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS; int err, num_tx_queues = AIROHA_NUM_TX_RING + channel + 1; struct airoha_gdm_dev *dev = netdev_priv(netdev); - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; /* Here we need to check the requested QDMA channel is not already * in use by another net_device running on the same QDMA block. */ + qdma = airoha_qdma_deref(dev); if (test_and_set_bit(channel, qdma->qos_channel_map)) { NL_SET_ERR_MSG_MOD(opt->extack, "qdma qos channel already in use"); @@ -2841,7 +2911,7 @@ static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev, u32 rate, u32 bucket_size, enum trtcm_unit_type unit_type) { - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma = airoha_qdma_deref(dev); int i; for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { @@ -3016,10 +3086,11 @@ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue) { struct airoha_gdm_dev *dev = netdev_priv(netdev); int num_tx_queues = AIROHA_NUM_TX_RING; - struct airoha_qdma *qdma = dev->qdma; + struct airoha_qdma *qdma; airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0); + qdma = airoha_qdma_deref(dev); clear_bit(queue, qdma->qos_channel_map); clear_bit(queue, dev->qos_sq_bmap); @@ -3045,6 +3116,95 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev, return 0; } +static void airoha_disable_qos_for_gdm34(struct net_device *netdev) +{ + struct airoha_gdm_dev *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + int err; + + if (port->id != AIROHA_GDM3_IDX && + port->id != AIROHA_GDM4_IDX) + return; + + err = airoha_disable_gdm2_loopback(dev); + if (err) + netdev_warn(netdev, + "failed disabling GDM2 loopback: %d\n", err); + + dev->flags &= ~AIROHA_DEV_F_WAN; + airoha_dev_set_qdma(dev); + + airoha_set_macaddr(dev, netdev->dev_addr); + if (netif_running(netdev)) + airoha_set_gdm_port_fwd_cfg(dev->eth, + REG_GDM_FWD_CFG(port->id), + FE_PSE_PORT_PPE1); +} + +static int airoha_enable_qos_for_gdm34(struct net_device *netdev, + struct netlink_ext_ack *extack) +{ + struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev); + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + int err = -EBUSY; + + if (port->id != AIROHA_GDM3_IDX && + port->id != AIROHA_GDM4_IDX) { + /* HW QoS is always supported by GDM1 and GDM2 */ + return 0; + } + + if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */ + return 0; + + mutex_lock(&flow_offload_mutex); + + wan_dev = airoha_get_wan_gdm_dev(eth); + if (wan_dev) { + if ((wan_dev->flags & AIROHA_DEV_F_QOS) || + wan_dev->port->id == AIROHA_GDM2_IDX) { + NL_SET_ERR_MSG_MOD(extack, + "QoS configured for WAN device"); + goto error_unlock; + } + airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev)); + } + + dev->flags |= AIROHA_DEV_F_WAN; + airoha_dev_set_qdma(dev); + err = airoha_enable_gdm2_loopback(dev); + if (err) + goto error_disable_wan; + + err = airoha_set_macaddr(dev, netdev->dev_addr); + if (err) + goto error_disable_loopback; + + if (netif_running(netdev)) { + u32 pse_port; + + pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2 + : FE_PSE_PORT_PPE1; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), + pse_port); + } + + mutex_unlock(&flow_offload_mutex); + + return 0; + +error_disable_loopback: + airoha_disable_gdm2_loopback(dev); +error_disable_wan: + dev->flags &= ~AIROHA_DEV_F_WAN; + airoha_dev_set_qdma(dev); +error_unlock: + mutex_unlock(&flow_offload_mutex); + + return err; +} + static int airoha_tc_htb_destroy(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); @@ -3053,6 +3213,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev) for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS) airoha_tc_remove_htb_queue(netdev, q); + dev->flags &= ~AIROHA_DEV_F_QOS; + return 0; } @@ -3072,24 +3234,33 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev, return 0; } -static int airoha_tc_setup_qdisc_htb(struct net_device *dev, +static int airoha_tc_setup_qdisc_htb(struct net_device *netdev, struct tc_htb_qopt_offload *opt) { switch (opt->command) { - case TC_HTB_CREATE: + case TC_HTB_CREATE: { + struct airoha_gdm_dev *dev = netdev_priv(netdev); + int err; + + err = airoha_enable_qos_for_gdm34(netdev, opt->extack); + if (err) + return err; + + dev->flags |= AIROHA_DEV_F_QOS; break; + } case TC_HTB_DESTROY: - return airoha_tc_htb_destroy(dev); + return airoha_tc_htb_destroy(netdev); case TC_HTB_NODE_MODIFY: - return airoha_tc_htb_modify_queue(dev, opt); + return airoha_tc_htb_modify_queue(netdev, opt); case TC_HTB_LEAF_ALLOC_QUEUE: - return airoha_tc_htb_alloc_leaf_queue(dev, opt); + return airoha_tc_htb_alloc_leaf_queue(netdev, opt); case TC_HTB_LEAF_DEL: case TC_HTB_LEAF_DEL_LAST: case TC_HTB_LEAF_DEL_LAST_FORCE: - return airoha_tc_htb_delete_leaf_queue(dev, opt); + return airoha_tc_htb_delete_leaf_queue(netdev, opt); case TC_HTB_LEAF_QUERY_QUEUE: - return airoha_tc_get_htb_get_leaf_queue(dev, opt); + return airoha_tc_get_htb_get_leaf_queue(netdev, opt); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 87ab3ea10664..b97224a5495c 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -537,11 +537,12 @@ struct airoha_qdma { enum airoha_dev_flags { AIROHA_DEV_F_WAN = BIT(0), + AIROHA_DEV_F_QOS = BIT(1), }; struct airoha_gdm_dev { + struct airoha_qdma __rcu *qdma; struct airoha_gdm_port *port; - struct airoha_qdma *qdma; struct airoha_eth *eth; DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS); @@ -676,6 +677,16 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev); bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, struct airoha_gdm_dev *dev); +extern struct mutex flow_offload_mutex; + +static inline struct airoha_qdma * +airoha_qdma_deref(struct airoha_gdm_dev *dev) +{ + return rcu_dereference_protected(dev->qdma, + lockdep_rtnl_is_held() || + lockdep_is_held(&flow_offload_mutex)); +} + void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 42f4b0f21d17..0f260c50ac3c 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -15,7 +15,10 @@ #include "airoha_regs.h" #include "airoha_eth.h" -static DEFINE_MUTEX(flow_offload_mutex); +/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port + * configuration. + */ +DEFINE_MUTEX(flow_offload_mutex); static DEFINE_SPINLOCK(ppe_lock); static const struct rhashtable_params airoha_flow_table_params = { @@ -86,8 +89,8 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) { - struct airoha_qdma *qdma = dev->qdma; - struct airoha_eth *eth = qdma->eth; + struct airoha_qdma *qdma = airoha_qdma_deref(dev); + struct airoha_eth *eth = dev->eth; u8 qdma_id = qdma - ð->qdma[0]; u32 fe_cpu_port; diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h index 436f3c8779c1..4e17dfbcf2b8 100644 --- a/drivers/net/ethernet/airoha/airoha_regs.h +++ b/drivers/net/ethernet/airoha/airoha_regs.h @@ -376,6 +376,7 @@ #define REG_SRC_PORT_FC_MAP6 0x2298 #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) +#define FC_MAP6_DEF_VALUE 0x1b1a1918 #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 -- 2.54.0