* [PATCH net-next v7 1/3] net: airoha: rename airoha_priv_flags to airoha_dev_flags
From: Lorenzo Bianconi @ 2026-07-01 8:09 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428@kernel.org>
Rename the airoha_priv_flags enum to airoha_dev_flags and the
AIROHA_PRIV_F_WAN flag to AIROHA_DEV_F_WAN. The "priv_flags" naming
dates back to an earlier design that used ethtool private flags; since
this series switched to tc qdisc offload for LAN/WAN configuration,
align the naming to reflect that these are per-device flags rather than
ethtool private flags. No functional change.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
drivers/net/ethernet/airoha/airoha_eth.h | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 932b3a3df2e5..8bba54ebcf07 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2048,7 +2048,7 @@ static int airoha_dev_init(struct net_device *netdev)
fallthrough;
case AIROHA_GDM2_IDX:
/* GDM2 is always used as wan */
- dev->flags |= AIROHA_PRIV_F_WAN;
+ dev->flags |= AIROHA_DEV_F_WAN;
break;
default:
break;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index d7ff8c5200e2..87ab3ea10664 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -535,8 +535,8 @@ struct airoha_qdma {
DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
-enum airoha_priv_flags {
- AIROHA_PRIV_F_WAN = BIT(0),
+enum airoha_dev_flags {
+ AIROHA_DEV_F_WAN = BIT(0),
};
struct airoha_gdm_dev {
@@ -659,7 +659,7 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
- return !(dev->flags & AIROHA_PRIV_F_WAN);
+ return !(dev->flags & AIROHA_DEV_F_WAN);
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v7 2/3] net: airoha: fix ETS QoS stats counter underflow and cross-channel corruption
From: Lorenzo Bianconi @ 2026-07-01 8:09 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428@kernel.org>
airoha_qdma_get_tx_ets_stats() has two bugs:
- The hardware counters read via airoha_qdma_rr() are 32-bit values
but are stored in u64 locals and subtracted from u64 baselines. When
a 32-bit hardware counter wraps around, the subtraction produces a
large underflow value passed to _bstats_update().
- The baseline counters (cpu_tx_packets, fwd_tx_packets) are stored as
single per-device fields, but airoha_qdma_get_tx_ets_stats() is
called with different channel values (0-3). Each call reads a
different channel's hardware counter but overwrites the same
baseline, corrupting the delta computation for other channels.
Fix both by:
- Narrowing the counter locals and baselines to u32 so that 32-bit
unsigned subtraction handles wrap-around naturally.
- Grouping the baselines into a per-channel qos_stats array so each
channel tracks its own previous counter value independently.
Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support")
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++-------
drivers/net/ethernet/airoha/airoha_eth.h | 7 ++++---
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 8bba54ebcf07..2c9ceb9f16f8 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2491,16 +2491,20 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_qdma *qdma = dev->qdma;
+ u32 cpu_tx_packets, fwd_tx_packets;
+ u64 tx_packets;
- 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);
+ 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 = (u32)(cpu_tx_packets -
+ dev->qos_stats[channel].cpu_tx_packets) +
+ (u32)(fwd_tx_packets -
+ dev->qos_stats[channel].fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
- dev->cpu_tx_packets = cpu_tx_packets;
- dev->fwd_tx_packets = fwd_tx_packets;
+ dev->qos_stats[channel].cpu_tx_packets = cpu_tx_packets;
+ dev->qos_stats[channel].fwd_tx_packets = fwd_tx_packets;
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 87ab3ea10664..ac5f571f3e53 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -545,9 +545,10 @@ struct airoha_gdm_dev {
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
- /* qos stats counters */
- u64 cpu_tx_packets;
- u64 fwd_tx_packets;
+ struct {
+ u32 cpu_tx_packets;
+ u32 fwd_tx_packets;
+ } qos_stats[AIROHA_NUM_QOS_CHANNELS];
u32 flags;
int nbq;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v7 3/3] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
From: Lorenzo Bianconi @ 2026-07-01 8:09 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
In-Reply-To: <20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428@kernel.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 <madhur.agrawal@airoha.com>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 219 ++++++++++++++++++++++++++----
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, 214 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 2c9ceb9f16f8..609a5ea67fb7 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,26 @@ 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;
+
+ memset(dev->qos_stats, 0, sizeof(dev->qos_stats));
+ synchronize_rcu();
+ netif_tx_wake_all_queues(netdev);
}
static int airoha_dev_init(struct net_device *netdev)
@@ -2187,9 +2245,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 +2256,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 +2307,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 +2371,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 +2387,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 +2467,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 +2487,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,10 +2556,11 @@ 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;
u32 cpu_tx_packets, fwd_tx_packets;
+ struct airoha_qdma *qdma;
u64 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));
@@ -2760,16 +2827,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)
@@ -2805,11 +2874,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");
@@ -2845,7 +2915,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++) {
@@ -3020,10 +3090,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);
@@ -3049,6 +3120,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);
@@ -3057,6 +3217,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;
}
@@ -3076,24 +3238,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 ac5f571f3e53..a314330fcd48 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);
@@ -677,6 +678,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
^ permalink raw reply related
* Re: [PATCH v3] net/sched: dualpi2: clear stale classification on filter miss
From: patchwork-bot+netdevbpf @ 2026-07-01 8:10 UTC (permalink / raw)
To: Samuel Moelius
Cc: jhs, jiri, davem, edumazet, kuba, pabeni, horms, olga,
koen.de_schepper, henrist, olivier.tilmans, netdev, linux-kernel
In-Reply-To: <20260628134846.2211556.3eb480ed8de5.dualpi2-filter-no-match@trailofbits.com>
Hello:
This patch was applied to netdev/net.git (main)
by David S. Miller <davem@davemloft.net>:
On Sun, 28 Jun 2026 13:48:47 +0000 you wrote:
> DualPI2 leaves previous classification state attached to an skb when
> filter classification returns no match. The enqueue path can then act
> on stale state from an earlier classification attempt.
>
> A filter miss should fall back to the default class without reusing old
> per-packet classification data.
>
> [...]
Here is the summary with links:
- [v3] net/sched: dualpi2: clear stale classification on filter miss
https://git.kernel.org/netdev/net/c/bf83ee45874e
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2] net/sched: hhf: clear heavy-hitter state on reset
From: patchwork-bot+netdevbpf @ 2026-07-01 8:10 UTC (permalink / raw)
To: Samuel Moelius
Cc: jhs, jiri, davem, edumazet, kuba, pabeni, horms, vtlam, netdev,
linux-kernel
In-Reply-To: <20260629164458.195029.ab92a1db1120.hhf-reset-stale-classifier@trailofbits.com>
Hello:
This patch was applied to netdev/net.git (main)
by David S. Miller <davem@davemloft.net>:
On Mon, 29 Jun 2026 16:44:59 +0000 you wrote:
> HHF reset does not clear the classifier state used to identify heavy
> hitters. Packets after reset can therefore be scheduled using flow
> history from before the reset.
>
> The reset operation should return the qdisc to an empty state.
>
> Clear the heavy-hitter classifier tables when HHF is reset.
>
> [...]
Here is the summary with links:
- [v2] net/sched: hhf: clear heavy-hitter state on reset
https://git.kernel.org/netdev/net/c/a225f8c20712
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next v2] ipv4: igmp: remove multicast group from hash table on device destruction
From: Ido Schimmel @ 2026-07-01 8:11 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: davem, dsahern, edumazet, horms, jedrzej.jagielski, kuba,
linux-kernel, netdev, pabeni, xiyou.wangcong, yuyanghuang
In-Reply-To: <20260630211527.3365952-1-kuniyu@google.com>
On Tue, Jun 30, 2026 at 09:13:11PM +0000, Kuniyuki Iwashima wrote:
> From: Ido Schimmel <idosch@nvidia.com>
> Date: Tue, 30 Jun 2026 19:59:34 +0300
> > On Tue, Jun 30, 2026 at 04:55:22PM +0900, Yuyang Huang wrote:
> > > > Hi,
> > > >
> > > > why sending this to net-next not to net if that's a bug fix?
> > > >
> > > > In the v1 thread it was said
> > > > >This is a long-standing bug, not a recent regression.
> > > >
> > > > so why do not cc stable kernel to get rid of this bug from
> > > > stable kernels in such case?
> > >
> > > Thanks for the advise, will send this patch to stable kernel.
> >
> > Please target v3 at net and add a trace given you're claiming for a
> > use-after-free. That way we know that the problem is real and not a
> > false-positive from some tool. You can reproduce it by adding enough
> > delay in inetdev_destroy():
>
> I guess delay was added between ip_mc_destroy_dev() and
> RCU_INIT_POINTER(dev->ip_ptr, NULL) ?
Yes, to increase the race window.
> I feel like we should clear it first and destroy everything
> as done in IPv6 addrconf_ifdown().
I agree, but let's do it as a separate change in net-next. The current
one line fix is correct and fixes the root cause. Clearing the pointer
happens to fix the problem because it relies on mc_hash only being
accessible via dev->in_dev (vs reaching in_dev via a different path).
^ permalink raw reply
* Re: [PATCH net] net: phy: motorcomm: read EEE abilities in yt8521_get_features()
From: Breno Leitao @ 2026-07-01 8:11 UTC (permalink / raw)
To: xiaoning.wang
Cc: Frank.Sae, andrew, hkallweit1, linux, davem, edumazet, kuba,
pabeni, netdev, linux-kernel, imx, xiaoning.wang
In-Reply-To: <20260701075730.133707-1-xiaoning.wang@oss.nxp.com>
On Wed, Jul 01, 2026 at 03:57:30PM +0800, xiaoning.wang@oss.nxp.com wrote:
> From: Clark Wang <xiaoning.wang@nxp.com>
>
> In phy_probe(), genphy_c45_read_eee_abilities() is only called when a
> driver uses phydrv->features. Drivers that implement .get_features are
> responsible for reading the EEE abilities themselves.
>
> yt8521_get_features() does not do this, so phydev->supported_eee stays
> empty for YT8521/YT8531S and "ethtool --show-eee" reports "EEE status:
> not supported", even though the PHY has the standard EEE capability
> registers.
>
> Call genphy_c45_read_eee_abilities() at the end of yt8521_get_features()
> to populate supported_eee.
>
> Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy")
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> ---
> drivers/net/phy/motorcomm.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
> index b49897500a59..46efa3406841 100644
> --- a/drivers/net/phy/motorcomm.c
> +++ b/drivers/net/phy/motorcomm.c
> @@ -2439,6 +2439,9 @@ static int yt8521_get_features(struct phy_device *phydev)
> /* add fiber's features to phydev->supported */
> yt8521_prepare_fiber_features(phydev, phydev->supported);
> }
> +
> + genphy_c45_read_eee_abilities(phydev);
Don't you want to return error if genphy_c45_read_eee_abilities() fails?
^ permalink raw reply
* [PATCH v2 0/2] Add and use neigh_parms_lookup_dev()
From: Paritosh Potukuchi @ 2026-07-01 8:15 UTC (permalink / raw)
To: netdev; +Cc: linux-kernel, Paritosh Potukuchi
This series follows up on a previous submission where it was suggested
that neigh_parms_lookup_dev() be accompanied by its users.
Patch 1 adds neigh_parms_lookup_dev() to expose per-device
neigh_parms lookup outside of the neighbour subsystem.
Patch 2 updates bonding to reuse an existing neigh_setup()
callback from the slave's neigh_parms when available, while
preserving the existing ndo_neigh_setup() fallback path.
v2:
- Convert the previous submission into a patch series
- Add bonding user of neigh_parms_lookup_dev() as requested.
Previous post's link:
https://lore.kernel.org/netdev/CAAVpQUBf+asQukcRw7sJz6vS2VdeNO5+Q5ucoCxf4JgK25nZ7g@mail.gmail.com/T/#t
Paritosh Potukuchi (2):
net: neighbour: add neigh_parms_lookup_dev() helper
bonding: reuse neigh_setup from slave neigh_parms
drivers/net/bonding/bond_main.c | 10 +++++++++-
include/net/neighbour.h | 2 ++
net/core/neighbour.c | 8 ++++++++
3 files changed, 19 insertions(+), 1 deletion(-)
--
2.43.0
^ permalink raw reply
* [PATCH v2 1/2] net: neighbour: add neigh_parms_lookup_dev() helper
From: Paritosh Potukuchi @ 2026-07-01 8:15 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, Paritosh Potukuchi, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
Ido Schimmel, Petr Machata
In-Reply-To: <20260701081602.3185086-1-paritosh.potukuchi@amd.com>
Provide a helper to lookup neigh_parms associated
with a given (neigh_table, net_device) pair.
The existing lookup_neigh_parms() helper is internal to the
neighbour subsystem and cannot be used by other subsystems.
Some stacked/virtual devices like bond require access to the
underlying device's neigh_parms.
neigh_parms_lookup_dev() is designed to be a wrapper around
lookup_neigh_parms(). The function provides controlled access
to per device neigh_parms.
The caller is expected to hold rcu_read_lock().
This does not break any existing functionality.
Signed-off-by: Paritosh Potukuchi <paritosh.potukuchi@amd.com>
---
include/net/neighbour.h | 2 ++
net/core/neighbour.c | 8 ++++++++
2 files changed, 10 insertions(+)
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 8860cc2175fc..1b3b06eda886 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -438,6 +438,8 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
proc_handler *proc_handler);
void neigh_sysctl_unregister(struct neigh_parms *p);
+struct neigh_parms *neigh_parms_lookup_dev(struct neigh_table *tbl, struct net_device *dev);
+
static inline void __neigh_parms_put(struct neigh_parms *parms)
{
refcount_dec(&parms->refcnt);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eedb64..6d32c2668af3 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1757,6 +1757,14 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
return NULL;
}
+/* Caller must hold rcu_read_lock()*/
+
+struct neigh_parms *neigh_parms_lookup_dev(struct neigh_table *tbl, struct net_device *dev)
+{
+ return lookup_neigh_parms(tbl, dev_net(dev), dev->ifindex);
+}
+EXPORT_SYMBOL(neigh_parms_lookup_dev);
+
struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
struct neigh_table *tbl)
{
--
2.43.0
^ permalink raw reply related
* [PATCH v2 2/2] bonding: reuse neigh_setup from slave neigh_parms
From: Paritosh Potukuchi @ 2026-07-01 8:16 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, Paritosh Potukuchi, Jay Vosburgh, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
In-Reply-To: <20260701081602.3185086-1-paritosh.potukuchi@amd.com>
bond_neigh_init() currently relies on the slave device's
ndo_neigh_setup() callback to obtain a neigh_setup() handler.
When an initialized neigh_parms instance already exists for the
slave device, reuse the neigh_setup() callback stored in it instead
of invoking ndo_neigh_setup() again.
If no neigh_parms instance is found, or no neigh_setup() callback is
present, retain the existing ndo_neigh_setup() fallback path.
This avoids unnecessary ndo_neigh_setup() invocations while preserving
existing behaviour.
Signed-off-by: Paritosh Potukuchi <paritosh.potukuchi@amd.com>
---
drivers/net/bonding/bond_main.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e044fc733b8c..d2e4dae4e97c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -4719,7 +4719,7 @@ static int bond_neigh_init(struct neighbour *n)
{
struct bonding *bond = netdev_priv(n->dev);
const struct net_device_ops *slave_ops;
- struct neigh_parms parms;
+ struct neigh_parms parms, *p;
struct slave *slave;
int ret = 0;
@@ -4727,6 +4727,14 @@ static int bond_neigh_init(struct neighbour *n)
slave = bond_first_slave_rcu(bond);
if (!slave)
goto out;
+
+ p = neigh_parms_lookup_dev(n->tbl, slave->dev);
+
+ if (p && p->neigh_setup) {
+ ret = p->neigh_setup(n);
+ goto out;
+ }
+
slave_ops = slave->dev->netdev_ops;
if (!slave_ops->ndo_neigh_setup)
goto out;
--
2.43.0
^ permalink raw reply related
* RE: [PATCH net] net: phy: motorcomm: read EEE abilities in yt8521_get_features()
From: Clark Wang @ 2026-07-01 8:16 UTC (permalink / raw)
To: Breno Leitao, Clark Wang (OSS)
Cc: Frank.Sae@motor-comm.com, andrew@lunn.ch, hkallweit1@gmail.com,
linux@armlinux.org.uk, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, imx@lists.linux.dev
In-Reply-To: <akTLmyF0r70Jyn9m@gmail.com>
> > In phy_probe(), genphy_c45_read_eee_abilities() is only called when a
> > driver uses phydrv->features. Drivers that implement .get_features are
> > responsible for reading the EEE abilities themselves.
> >
> > yt8521_get_features() does not do this, so phydev->supported_eee stays
> > empty for YT8521/YT8531S and "ethtool --show-eee" reports "EEE status:
> > not supported", even though the PHY has the standard EEE capability
> > registers.
> >
> > Call genphy_c45_read_eee_abilities() at the end of
> > yt8521_get_features() to populate supported_eee.
> >
> > Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521
> > gigabit ethernet phy")
> > Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> > ---
> > drivers/net/phy/motorcomm.c | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> > diff --git a/drivers/net/phy/motorcomm.c
> b/drivers/net/phy/motorcomm.c
> > index b49897500a59..46efa3406841 100644
> > --- a/drivers/net/phy/motorcomm.c
> > +++ b/drivers/net/phy/motorcomm.c
> > @@ -2439,6 +2439,9 @@ static int yt8521_get_features(struct phy_device
> *phydev)
> > /* add fiber's features to phydev->supported */
> > yt8521_prepare_fiber_features(phydev, phydev->supported);
> > }
> > +
> > + genphy_c45_read_eee_abilities(phydev);
>
> Don't you want to return error if genphy_c45_read_eee_abilities() fails?
EEE is an optional functionality, and the call in genphy_read_abilities() has the following comment. Therefore, I do not return its error here either.
"
/* This is optional functionality. If not supported, we may get an error
* which should be ignored.
*/
"
^ permalink raw reply
* Re: [PATCH RESEND net-next] net: airoha: Make use of the helper function dev_err_probe()
From: Lorenzo Bianconi @ 2026-07-01 8:17 UTC (permalink / raw)
To: zhulei; +Cc: andrew+netdev, davem, edumazet, kuba, pabeni, netdev
In-Reply-To: <23f58025.6d6d.19f1cb25385.Coremail.zhulei_szu@163.com>
[-- Attachment #1: Type: text/plain, Size: 1820 bytes --]
> At 2026-06-30 18:38:38, "Lorenzo Bianconi" <lorenzo@kernel.org> wrote:
> >> From: Lei Zhu <zhulei@kylinos.cn>
> >>
> >> Use dev_err_probe() to reduce code size and simplify the code.
> >>
> >> Signed-off-by: Lei Zhu <zhulei@kylinos.cn>
> >> ---
> >> The last submission was when net-next is closed.Resending it.
> >>
> >> drivers/net/ethernet/airoha/airoha_eth.c | 21 +++++++++------------
> >> 1 file changed, 9 insertions(+), 12 deletions(-)
> >>
> >> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> >> index 31cdb11cd78d..189f64e83a46 100644
> >> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> >> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> >> @@ -3071,10 +3071,9 @@ static int airoha_probe(struct platform_device *pdev)
> >> eth->dev = &pdev->dev;
> >>
> >> err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32));
> >
> >I do not think dma_set_mask_and_coherent() can return -EPROBE_DEFER, so there
> >is no point adding dev_err_probe() here.
> >
> >Regards,
> >Lorenzo
> >
> Hi Lorenzo,
>
> Thanks for your review.
>
> Before making this patch, I referred to the comments of dev_err_probe:
> "even if @err is known to never be -EPROBE_DEFER, the benefit compared
> to a normal dev_err() is the standardized format of the error code."
>
> In the probe function, I noticed devm_platform_ioremap_resource_byname
> already uses dev_err_probe, while other functions still use dev_err.
> Replace them with dev_err_probe for consistency, more compact error paths,
> and better readability of error codes.
>
> Best regards
> Lei
>
I do not have a strong opinion about it, from my pov it is more clear if we use
dev_err_probe() when it really does something, but up to you.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH net] net: phy: motorcomm: read EEE abilities in yt8521_get_features()
From: Breno Leitao @ 2026-07-01 8:19 UTC (permalink / raw)
To: Clark Wang
Cc: Clark Wang (OSS), Frank.Sae@motor-comm.com, andrew@lunn.ch,
hkallweit1@gmail.com, linux@armlinux.org.uk, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
imx@lists.linux.dev
In-Reply-To: <GV2PR04MB12213F4758648CD71F5D5B3B7F3F62@GV2PR04MB12213.eurprd04.prod.outlook.com>
On Wed, Jul 01, 2026 at 08:16:13AM +0000, Clark Wang wrote:
> > > In phy_probe(), genphy_c45_read_eee_abilities() is only called when a
> > > driver uses phydrv->features. Drivers that implement .get_features are
> > > responsible for reading the EEE abilities themselves.
> > >
> > > yt8521_get_features() does not do this, so phydev->supported_eee stays
> > > empty for YT8521/YT8531S and "ethtool --show-eee" reports "EEE status:
> > > not supported", even though the PHY has the standard EEE capability
> > > registers.
> > >
> > > Call genphy_c45_read_eee_abilities() at the end of
> > > yt8521_get_features() to populate supported_eee.
> > >
> > > Fixes: 70479a40954c ("net: phy: Add driver for Motorcomm yt8521
> > > gigabit ethernet phy")
> > > Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Breno Leitao <leitao@debian.org>
> > > + genphy_c45_read_eee_abilities(phydev);
> >
> > Don't you want to return error if genphy_c45_read_eee_abilities() fails?
>
> EEE is an optional functionality, and the call in genphy_read_abilities() has the following comment. Therefore, I do not return its error here either.
> "
> /* This is optional functionality. If not supported, we may get an error
> * which should be ignored.
> */
> "
Ack. I've look at the code, and no one is even checking for the return
value anyway.
^ permalink raw reply
* [PATCH net-next 0/2] macvlan: RTNL-less macvlan_fill_info()
From: Eric Dumazet @ 2026-07-01 8:22 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, netdev, eric.dumazet,
Eric Dumazet
This series removes the RTNL lock dependency from macvlan_fill_info(),
allowing it to run under RCU read lock.
The first patch annotates data races on 'mode' and 'flags' fields which
are accessed locklessly in the RX/TX paths.
The second patch transitions macvlan_fill_info() to RCU, adding necessary
annotations for other fields and handling concurrent updates to the MAC
address list by computing the count dynamically.
Eric Dumazet (2):
macvlan: annotate data-races around vlan->mode and vlan->flags
macvlan: no longer rely on RTNL in macvlan_fill_info()
drivers/net/macvlan.c | 109 ++++++++++++++++++++++++++----------------
1 file changed, 69 insertions(+), 40 deletions(-)
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply
* [PATCH net-next 1/2] macvlan: annotate data-races around vlan->mode and vlan->flags
From: Eric Dumazet @ 2026-07-01 8:22 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, netdev, eric.dumazet,
Eric Dumazet
In-Reply-To: <20260701082214.2974946-1-edumazet@google.com>
Both fields can be changed in macvlan_changelink() while being read
locklessly.
Add READ_ONCE()/WRITE_ONCE() annotations.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/macvlan.c | 38 +++++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index c40fa331836bb2395267914807542ae5094e1a3c..8b69cc9b70f98d7991110f5eda76d58d5d96fa81 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -277,7 +277,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
return;
hash_for_each_rcu(port->vlan_hash, i, vlan, hlist) {
- if (vlan->dev == src || !(vlan->mode & mode))
+ if (vlan->dev == src || !(READ_ONCE(vlan->mode) & mode))
continue;
hash = mc_hash(vlan, eth->h_dest);
@@ -306,7 +306,7 @@ static void macvlan_multicast_rx(const struct macvlan_port *port,
MACVLAN_MODE_VEPA |
MACVLAN_MODE_PASSTHRU|
MACVLAN_MODE_BRIDGE);
- else if (src->mode == MACVLAN_MODE_VEPA)
+ else if (READ_ONCE(src->mode) == MACVLAN_MODE_VEPA)
/* flood to everyone except source */
macvlan_broadcast(skb, port, src->dev,
MACVLAN_MODE_VEPA |
@@ -447,7 +447,7 @@ static bool macvlan_forward_source(struct sk_buff *skb,
if (!vlan)
continue;
- if (vlan->flags & MACVLAN_FLAG_NODST)
+ if (READ_ONCE(vlan->flags) & MACVLAN_FLAG_NODST)
consume = true;
macvlan_forward_source_one(skb, vlan);
}
@@ -487,14 +487,18 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
return RX_HANDLER_CONSUMED;
}
src = macvlan_hash_lookup(port, eth->h_source);
- if (src && src->mode != MACVLAN_MODE_VEPA &&
- src->mode != MACVLAN_MODE_BRIDGE) {
- /* forward to original port. */
- vlan = src;
- ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:
- __netif_rx(skb);
- handle_res = RX_HANDLER_CONSUMED;
- goto out;
+ if (src) {
+ enum macvlan_mode mode = READ_ONCE(src->mode);
+
+ if (mode != MACVLAN_MODE_VEPA &&
+ mode != MACVLAN_MODE_BRIDGE) {
+ /* forward to original port. */
+ vlan = src;
+ ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:
+ __netif_rx(skb);
+ handle_res = RX_HANDLER_CONSUMED;
+ goto out;
+ }
}
hash = mc_hash(NULL, eth->h_dest);
@@ -515,7 +519,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
struct macvlan_dev, list);
else
vlan = macvlan_hash_lookup(port, eth->h_dest);
- if (!vlan || vlan->mode == MACVLAN_MODE_SOURCE)
+ if (!vlan || READ_ONCE(vlan->mode) == MACVLAN_MODE_SOURCE)
return RX_HANDLER_PASS;
dev = vlan->dev;
@@ -548,7 +552,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
const struct macvlan_port *port = vlan->port;
const struct macvlan_dev *dest;
- if (vlan->mode == MACVLAN_MODE_BRIDGE) {
+ if (READ_ONCE(vlan->mode) == MACVLAN_MODE_BRIDGE) {
const struct ethhdr *eth = skb_eth_hdr(skb);
/* send to other bridge ports directly */
@@ -559,7 +563,7 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
}
dest = macvlan_hash_lookup(port, eth->h_dest);
- if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
+ if (dest && READ_ONCE(dest->mode) == MACVLAN_MODE_BRIDGE) {
/* send to lowerdev first for its network taps */
dev_forward_skb(vlan->lowerdev, skb);
@@ -777,7 +781,7 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
if (ether_addr_equal(dev->dev_addr, addr->__data))
return 0;
- if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
+ if (READ_ONCE(vlan->mode) == MACVLAN_MODE_PASSTHRU) {
macvlan_set_addr_change(vlan->port);
return dev_set_mac_address(vlan->lowerdev, addr, NULL);
}
@@ -1645,7 +1649,7 @@ static int macvlan_changelink(struct net_device *dev,
if (err < 0)
return err;
}
- vlan->flags = flags;
+ WRITE_ONCE(vlan->flags, flags);
}
if (data && data[IFLA_MACVLAN_BC_QUEUE_LEN]) {
@@ -1658,7 +1662,7 @@ static int macvlan_changelink(struct net_device *dev,
vlan, nla_get_s32(data[IFLA_MACVLAN_BC_CUTOFF]));
if (set_mode)
- vlan->mode = mode;
+ WRITE_ONCE(vlan->mode, mode);
if (data && data[IFLA_MACVLAN_MACADDR_MODE]) {
if (vlan->mode != MACVLAN_MODE_SOURCE)
return -EINVAL;
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* [PATCH net-next 2/2] macvlan: no longer rely on RTNL in macvlan_fill_info()
From: Eric Dumazet @ 2026-07-01 8:22 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, netdev, eric.dumazet,
Eric Dumazet
In-Reply-To: <20260701082214.2974946-1-edumazet@google.com>
Add READ_ONCE()/WRITE_ONCE() annotations on vlan->mode, vlan->flags,
vlan->bc_queue_len_req and port->bc_cutoff.
Fill IFLA_MACVLAN_MACADDR_DATA nested attribute and compute
on the fly the precise number of elements we put in it,
to fill an accurate IFLA_MACVLAN_MACADDR_COUNT attribute
as some user space applications could depend on its value
and the attributes order.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
drivers/net/macvlan.c | 71 +++++++++++++++++++++++++++++--------------
1 file changed, 48 insertions(+), 23 deletions(-)
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 8b69cc9b70f98d7991110f5eda76d58d5d96fa81..9a4bc99dbf53b5f2cd6345f0af899f56fdb3a46b 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -171,7 +171,7 @@ static int macvlan_hash_add_source(struct macvlan_dev *vlan,
RCU_INIT_POINTER(entry->vlan, vlan);
h = &port->vlan_source_hash[macvlan_eth_hash(addr)];
hlist_add_head_rcu(&entry->hlist, h);
- vlan->macaddr_count++;
+ WRITE_ONCE(vlan->macaddr_count, vlan->macaddr_count + 1);
return 0;
}
@@ -402,7 +402,7 @@ static void macvlan_flush_sources(struct macvlan_port *port,
if (rcu_access_pointer(entry->vlan) == vlan)
macvlan_hash_del_source(entry);
- vlan->macaddr_count = 0;
+ WRITE_ONCE(vlan->macaddr_count, 0);
}
static void macvlan_forward_source_one(struct sk_buff *skb,
@@ -874,7 +874,7 @@ static void update_port_bc_cutoff(struct macvlan_dev *vlan, int cutoff)
if (vlan->port->bc_cutoff == cutoff)
return;
- vlan->port->bc_cutoff = cutoff;
+ WRITE_ONCE(vlan->port->bc_cutoff, cutoff);
macvlan_recompute_bc_filter(vlan);
}
@@ -1427,7 +1427,7 @@ static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode,
entry = macvlan_hash_lookup_source(vlan, addr);
if (entry) {
macvlan_hash_del_source(entry);
- vlan->macaddr_count--;
+ WRITE_ONCE(vlan->macaddr_count, vlan->macaddr_count - 1);
}
} else if (mode == MACVLAN_MACADDR_FLUSH) {
macvlan_flush_sources(vlan->port, vlan);
@@ -1653,7 +1653,8 @@ static int macvlan_changelink(struct net_device *dev,
}
if (data && data[IFLA_MACVLAN_BC_QUEUE_LEN]) {
- vlan->bc_queue_len_req = nla_get_u32(data[IFLA_MACVLAN_BC_QUEUE_LEN]);
+ WRITE_ONCE(vlan->bc_queue_len_req,
+ nla_get_u32(data[IFLA_MACVLAN_BC_QUEUE_LEN]));
update_port_bc_queue_len(vlan->port);
}
@@ -1676,10 +1677,12 @@ static int macvlan_changelink(struct net_device *dev,
static size_t macvlan_get_size_mac(const struct macvlan_dev *vlan)
{
- if (vlan->macaddr_count == 0)
+ unsigned int macaddr_count = READ_ONCE(vlan->macaddr_count);
+
+ if (!macaddr_count)
return 0;
return nla_total_size(0) /* IFLA_MACVLAN_MACADDR_DATA */
- + vlan->macaddr_count * nla_total_size(sizeof(u8) * ETH_ALEN);
+ + macaddr_count * nla_total_size(sizeof(u8) * ETH_ALEN);
}
static size_t macvlan_get_size(const struct net_device *dev)
@@ -1702,53 +1705,75 @@ static int macvlan_fill_info_macaddr(struct sk_buff *skb,
const int i)
{
struct hlist_head *h = &vlan->port->vlan_source_hash[i];
- struct macvlan_source_entry *entry;
+ const struct macvlan_source_entry *entry;
+ int cnt = 0;
- hlist_for_each_entry_rcu(entry, h, hlist, lockdep_rtnl_is_held()) {
+ hlist_for_each_entry_rcu(entry, h, hlist) {
if (rcu_access_pointer(entry->vlan) != vlan)
continue;
if (nla_put(skb, IFLA_MACVLAN_MACADDR, ETH_ALEN, entry->addr))
- return 1;
+ return -EMSGSIZE;
+ cnt++;
}
- return 0;
+ return cnt;
}
static int macvlan_fill_info(struct sk_buff *skb,
const struct net_device *dev)
{
- struct macvlan_dev *vlan = netdev_priv(dev);
+ const struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
- int i;
- struct nlattr *nest;
+ unsigned int macaddr_count = 0;
+ struct nlattr *nest, *attr;
+ int bc_cutoff, cnt, i;
- if (nla_put_u32(skb, IFLA_MACVLAN_MODE, vlan->mode))
+ rcu_read_lock();
+ if (nla_put_u32(skb, IFLA_MACVLAN_MODE, READ_ONCE(vlan->mode)))
goto nla_put_failure;
- if (nla_put_u16(skb, IFLA_MACVLAN_FLAGS, vlan->flags))
+
+ if (nla_put_u16(skb, IFLA_MACVLAN_FLAGS, READ_ONCE(vlan->flags)))
goto nla_put_failure;
- if (nla_put_u32(skb, IFLA_MACVLAN_MACADDR_COUNT, vlan->macaddr_count))
+
+ attr = nla_reserve(skb, IFLA_MACVLAN_MACADDR_COUNT, sizeof(u32));
+ if (!attr)
goto nla_put_failure;
- if (vlan->macaddr_count > 0) {
+
+ if (READ_ONCE(vlan->macaddr_count) > 0) {
nest = nla_nest_start_noflag(skb, IFLA_MACVLAN_MACADDR_DATA);
if (nest == NULL)
goto nla_put_failure;
for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
- if (macvlan_fill_info_macaddr(skb, vlan, i))
+ cnt = macvlan_fill_info_macaddr(skb, vlan, i);
+ if (cnt < 0)
goto nla_put_failure;
+ macaddr_count += cnt;
}
- nla_nest_end(skb, nest);
+ if (!macaddr_count)
+ nla_nest_cancel(skb, nest);
+ else if (nla_nest_end_safe(skb, nest) < 0)
+ goto nla_put_failure;
}
- if (nla_put_u32(skb, IFLA_MACVLAN_BC_QUEUE_LEN, vlan->bc_queue_len_req))
+ *(u32 *)nla_data(attr) = macaddr_count;
+
+ if (nla_put_u32(skb, IFLA_MACVLAN_BC_QUEUE_LEN,
+ READ_ONCE(vlan->bc_queue_len_req)))
goto nla_put_failure;
+
if (nla_put_u32(skb, IFLA_MACVLAN_BC_QUEUE_LEN_USED,
READ_ONCE(port->bc_queue_len_used)))
goto nla_put_failure;
- if (port->bc_cutoff != 1 &&
- nla_put_s32(skb, IFLA_MACVLAN_BC_CUTOFF, port->bc_cutoff))
+
+ bc_cutoff = READ_ONCE(port->bc_cutoff);
+ if (bc_cutoff != 1 &&
+ nla_put_s32(skb, IFLA_MACVLAN_BC_CUTOFF, bc_cutoff))
goto nla_put_failure;
+
+ rcu_read_unlock();
return 0;
nla_put_failure:
+ rcu_read_unlock();
return -EMSGSIZE;
}
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related
* Re: [PATCH] net: phylink: reject unsupported speed/duplex in ksettings_set() with PHY
From: Maxime Chevallier @ 2026-07-01 8:29 UTC (permalink / raw)
To: muhammad.nazim.amirul.nazle.asmade, linux, andrew, hkallweit1
Cc: davem, edumazet, kuba, pabeni, netdev, linux-kernel
In-Reply-To: <20260701031746.23448-1-muhammad.nazim.amirul.nazle.asmade@altera.com>
Hi,
On 7/1/26 05:17, muhammad.nazim.amirul.nazle.asmade@altera.com wrote:
> From: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
Target tree tag is still also missing in the subject :)
> When using ethtool to change speed and duplex on a phylink-managed
> interface with a PHY attached, the requested speed/duplex combination
> is not validated against the MAC's supported capabilities before being
> passed down to the PHY layer.
>
> commit df0acdc59b09 ("net: phylink: fix ksettings_set() ethtool call")
> and commit 03c44a21d033 ("net: phylink: actually fix ksettings_set()
> ethtool call") introduced masking of the PHY advertising modes against
> pl->supported, but did not add an explicit check that the requested
> speed/duplex itself is within the MAC's capability set.
>
> The AUTONEG_DISABLE path in the non-PHY case already uses
> phy_caps_lookup() to validate speed/duplex against pl->supported.
> Extend the same validation to the pl->phydev path so that ethtool
> requests for unsupported speed/duplex combinations are rejected with
> -EINVAL before reaching the PHY layer.
>
> Signed-off-by: Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
> ---
> drivers/net/phy/phylink.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> index 087ac63f9193..22f9bbd381bd 100644
> --- a/drivers/net/phy/phylink.c
> +++ b/drivers/net/phy/phylink.c
> @@ -2989,6 +2989,10 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
> if (pl->phydev) {
> struct ethtool_link_ksettings phy_kset = *kset;
>
> + if (!phy_caps_lookup(kset->base.speed, kset->base.duplex,
> + pl->supported, true))
> + return -EINVAL;
> +
> linkmode_and(phy_kset.link_modes.advertising,
> phy_kset.link_modes.advertising,
> pl->supported);
I can indeed reproduce that, with a 1000FD-only mac, running
ethtool -s eth2 speed 1000 duplex half autoneg off
brings the link down, no error reported, and running
ethtool -s eth2 speed 1000 duplex half autoneg on
also brings the link down, with
Advertised link modes: Not reported
This is expected, but yeah no error reported.
I think rejecting these settings makes sense, I'm however wondering
wether this is a fix or not, as this will change user-visible behaviour.
I'd err to the side of caution and send that to net-next, but maybe
Andrew will have more insight :)
So at least, you'll have to resubmit targetting the correct tree.
Maxime
^ permalink raw reply
* Re: [PATCH net-next] net: neigh: avoid calling neigh_forced_gc on every alloc when table is full
From: Vimal Agrawal @ 2026-07-01 8:30 UTC (permalink / raw)
To: Kuniyuki Iwashima; +Cc: kuba, edumazet, netdev, vimal.agrawal
In-Reply-To: <CAAVpQUAN87UybUdiCy_A+UThmAN9haB2ie2-usW4qZoY31XEGA@mail.gmail.com>
Hi Kuniyuki,
I understand the recommendation to set gc_thresh3 = gc_thresh2 +
headroom. However, in field deployments the neighbour table size can
be unpredictable — traffic patterns, routing changes, and network
events can cause transient spikes that exceed even a well-configured
gc_thresh3.
We have reproduced this with 32k IXIA clients where we observed soft
lockups under sustained neighbour creation. This is what originally
motivated the investigation.
The current behaviour when gc_thresh3 is exceeded — calling
neigh_forced_gc() on every allocation with no rate limiting — is
unnecessarily severe. It causes lock contention proportional to the
table size on every allocation attempt, even briefly exceeding
gc_thresh3 results in significant latency impact.
The patch does not change GC semantics or thresholds. It simply
prevents repeated full table scans within a short window, which is
harmless when GC is effective and protective when it is not. This
seems like a reasonable defensive improvement regardless of how
thresholds are configured.
Thanks,
Vimal
On Tue, Jun 30, 2026 at 10:06 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> On Tue, Jun 30, 2026 at 5:01 AM Vimal Agrawal <avimalin@gmail.com> wrote:
> >
> > Hi Kuniyuki,
> >
> > You are correct that in this specific test case GC does not help since
> > all entries are active/reachable. However, this is not the only
> > scenario where entries can exceed gc_thresh3.
> >
> > In a real workload, the table can exceed gc_thresh3 with a mix of
> > active and stale entries. In that case GC does help, but should not be
> > called on every allocation attempt — once per 50ms is sufficient for
> > GC to make progress without causing lock contention.
>
> My mental model is that gc_thresh3 is the hard limit while gc_thresh2
> is the soft limit, so if the total number of entries often exceeds gc_thresh3,
> it's clearly wrong.
>
> I think you need to set gc_thresh2 to a proper baseline (it sounds like
> your current gc_thresh3 is the one) and gc_thresh3 to gc_thresh2+X
> where X covers fluctuations.
>
>
> >
> > The rate limiting also protects against the case where GC cannot
> > reclaim anything. Without it, every allocation attempt above
> > gc_thresh3 triggers a full table scan holding tbl->lock, even when GC
> > has no work to do.
> >
> > Thanks,
> > Vimal
> >
> > On Mon, Jun 29, 2026 at 11:35 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
> > >
> > > On Mon, Jun 29, 2026 at 12:57 AM Vimal Agrawal <avimalin@gmail.com> wrote:
> > > >
> > > > Hi Kuniyuki,
> > > > Thank you for the feedback.
> > > > However, the rate limiting issue exists independently of the threshold
> > > > values. If entries genuinely exceed gc_thresh3 — regardless of what it
> > > > is set to — neigh_forced_gc() is called on every allocation attempt
> > > > with no rate limiting. In my workload, most entries are
> > > > active/reachable with refcnt > 1, so the GC walk traverses the entire
> > > > table without reclaiming anything.
> > >
> > > This suggests your gc_thresh2/3 do not fit your use case.
> > >
> > > If GC does not help, there is no point in running it or rate-limiting
> > > in the first place.
> > >
> > >
> > > > Increasing gc_thresh3 would make
> > > > this worse, not better, as GC now has a larger table to scan on each
> > > > call.
> > >
> > > If you just increase gc_thresh3 slightly, then yes, it won't help.
> > >
> > >
> > > >
> > > > Regarding neigh_hash_shift: in my workload, neigh_alloc() returns
> > > > ENOBUFS before reaching do_alloc() since GC cannot reclaim any
> > > > entries. kzalloc() is never called, so neigh_hash_grow() is not
> > > > involved in the latency I observed. The pre-lock time check in
> > > > neigh_forced_gc() is a low-cost safeguard that prevents repeated full
> > > > table scans regardless of gc_thresh3 value. It does not interfere with
> > > > correct GC behaviour — if entries are still above the threshold, GC
> > > > runs normally.
> > > >
> > > >
> > > > Hi Jakub,
> > > > I tested with different threshold values, filling the table completely
> > > > with 32k reachable entries and attempting 1000 additional allocations.
> > > > Exported neigh_forced_gc so that it can be profiled
> > > > no change 10ms 50ms 100ms
> > > > max cpu usage % 44% 11.8% 2.56% 1.42%
> > > > calls > 100us (of 1000) 101 31 13 7
> > > >
> > > > At 10ms, max CPU usage is still 11.8% and 31 out of 1000 calls take
> > > > more than 100us. Given that 50ms reduces this to 2.56% and 13 calls
> > > > respectively, I would prefer 50ms as the threshold. However, I am open
> > > > to further discussion on the right value.
> > > >
> > > > Thanks,
> > > > Vimal
> > > >
> > > >
> > > > On Fri, Jun 26, 2026 at 3:17 AM Kuniyuki Iwashima <kuniyu@google.com> wrote:
> > > > >
> > > > > From: Vimal Agrawal <avimalin@gmail.com>
> > > > > Date: Thu, 25 Jun 2026 10:20:20 +0000
> > > > > > Once the neighbour table exceeds gc_thresh3, neigh_forced_gc() is called
> > > > > > on every allocation attempt with no rate limiting. In workloads with mostly
> > > > > > active/reachable entries, the GC walk traverses a large portion of the
> > > > > > neighbour table without reclaiming entries, holding tbl->lock for an
> > > > > > extended period. This causes severe lock contention and allocation
> > > > > > latencies exceeding 16ms under sustained neighbour creation.
> > > > > >
> > > > > > Add a pre-lock check in neigh_forced_gc() to skip the GC run if one was
> > > > > > performed within the last second, avoiding repeated full table scans and
> > > > > > lock acquisitions on the hot allocation path.
> > > > > >
> > > > > > Profiling of neigh_create() shows ~3 orders of magnitude latency
> > > > > > improvement with this change.
> > > > > >
> > > > > > Link:https://lore.kernel.org/netdev/CALkUMdSCpx_ywYCx_ePLdm6yioO1nQWx7sSM=AEgsq0kywHxTw@mail.gmail.com/
> > > > >
> > > > > From the thread, these look misconfigured.
> > > > >
> > > > > ---8<---
> > > > > net.ipv6.neigh.default.gc_thresh2 = 32768
> > > > > net.ipv6.neigh.default.gc_thresh3 = 32768
> > > > > ---8<---
> > > > >
> > > > > If gc_thresh3 is larger enough, gc_thresh2 will give you 5s
> > > > > rate limiting.
> > > > >
> > > > > If the number of active neigh entries constantly exceeds
> > > > > gc_thresh3, it will be the correct gc_thresh2 for you.
> > > > >
> > > > > Also, I guess you want a new kernel param for the first
> > > > > neigh_hash_alloc(), which is currently fixed for 3, which
> > > > > is too small for some hosts.
> > > > >
> > > > > 50000 entries require neigh_hash_grow() 13 times.
> > > > >
> > > > > Can you test this on your real workload, starting from
> > > > > neigh_hash_shift=16 and appropriate gc_thresh2/3 ?
> > > > >
> > > > > ---8<---
> > > > > diff --git a/net/core/neighbour.c b/net/core/neighbour.c
> > > > > index 1349c0eedb64..a75b3750eec9 100644
> > > > > --- a/net/core/neighbour.c
> > > > > +++ b/net/core/neighbour.c
> > > > > @@ -1817,6 +1817,22 @@ EXPORT_SYMBOL(neigh_parms_release);
> > > > > static struct lock_class_key neigh_table_proxy_queue_class;
> > > > >
> > > > > static struct neigh_table __rcu *neigh_tables[NEIGH_NR_TABLES] __read_mostly;
> > > > > +static __initdata unsigned long neigh_hash_shift = 3;
> > > > > +
> > > > > +static int __init neigh_set_hash_shift(char *str)
> > > > > +{
> > > > > + ssize_t ret;
> > > > > +
> > > > > + if (!str)
> > > > > + return 0;
> > > > > +
> > > > > + ret = kstrtoul(str, 0, &neigh_hash_shift);
> > > > > + if (ret)
> > > > > + return 0;
> > > > > +
> > > > > + return 1;
> > > > > +}
> > > > > +__setup("neigh_hash_shift=", neigh_set_hash_shift);
> > > > >
> > > > > void neigh_table_init(int index, struct neigh_table *tbl)
> > > > > {
> > > > > @@ -1843,7 +1859,7 @@ void neigh_table_init(int index, struct neigh_table *tbl)
> > > > > panic("cannot create neighbour proc dir entry");
> > > > > #endif
> > > > >
> > > > > - RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
> > > > > + RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(neigh_hash_shift));
> > > > >
> > > > > phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
> > > > > tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
> > > > > ---8<---
> > > > >
> > > > >
> > > > >
> > > > > > Signed-off-by: Vimal Agrawal <vimal.agrawal@sophos.com>
> > > > > > ---
> > > > > > net/core/neighbour.c | 3 +++
> > > > > > 1 file changed, 3 insertions(+)
> > > > > >
> > > > > > diff --git a/net/core/neighbour.c b/net/core/neighbour.c
> > > > > > index 1349c0eedb64..078842db3c5f 100644
> > > > > > --- a/net/core/neighbour.c
> > > > > > +++ b/net/core/neighbour.c
> > > > > > @@ -260,6 +260,9 @@ static int neigh_forced_gc(struct neigh_table *tbl)
> > > > > > int shrunk = 0;
> > > > > > int loop = 0;
> > > > > >
> > > > > > + if (!time_after(jiffies, READ_ONCE(tbl->last_flush) + HZ))
> > > > > > + return 0;
> > > > > > +
> > > > > > NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
> > > > > >
> > > > > > spin_lock_bh(&tbl->lock);
> > > > > > --
> > > > > > 2.17.1
> > > > > > v
^ permalink raw reply
* Re: [PATCH v4 1/7] dt-bindings: mtd: jedec,spi-nor: allow the SFDP to be exposed via NVMEM
From: Michael Walle @ 2026-07-01 8:34 UTC (permalink / raw)
To: Linus Walleij, Manikandan Muralidharan
Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
linux-mtd, devicetree, linux-kernel, linux-arm-kernel, netdev
In-Reply-To: <CAD++jL=FkEfpz-LW0vmPpZ28fLfGFMWo5E479Mapz55YUxKNAQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1630 bytes --]
Hi,
>> Add an optional "sfdp" child node (compatible "jedec,sfdp") that
>> describes the SFDP as a read-only NVMEM provider via nvmem.yaml, so its
>> contents (e.g. a vendor EUI-48/EUI-64) can be read through NVMEM cells.
>>
>> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
>
> I would expect it to follow nvmem conventions like this, notice
> compatibles specific-to-general with sfdp first:
> sfdp {
> /* NVMEM provided by SFDP */
> compatible = "jedec,sfdp", "nvmem-cells";
> label = "SFDP";
Isn't using label frowned upon? I wouldn't add that to the example.
> read-only;
> #address-cells = <1>;
> #size-cells = <1>;
>
> mac0: macaddr@0x00 {
> reg = <0x00 0x06>;
> };
> mac1: macaddr@0x06 {
> reg = <0x06 0x06>;
> };
> };
If I'm correct, this is the old style, see commit bd912c991d2e
("dt-bindings: nvmem: layouts: add fixed-layout"). So it should
eventually look like:
sfdp {
compatible = "jedec,sfdp";
nvmem-layout {
compatible = "microchip,sst26vf-sfdp-eui";
};
};
Which is what patch series will lead to.
Also I'm not sure if we really need to add the "nvmem-cells" here.
IIRC in MTD it was there to tell a driver to add an nvmem device to
an already existing compatible/node.
Apart from the MTD case, I've just found qcom,smem-part,yaml which
has compatible = "nvmem-cells".
-michael
> Your example should definitely be more elaborate like this,
> just an opaque sfdp node will not suffice. Maybe a separate
> example?
>
> Yours,
> Linus Walleij
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v2] ipv4: igmp: remove multicast group from hash table on device destruction
From: Yuyang Huang @ 2026-07-01 8:58 UTC (permalink / raw)
To: Ido Schimmel
Cc: Kuniyuki Iwashima, davem, dsahern, edumazet, horms,
jedrzej.jagielski, kuba, linux-kernel, netdev, pabeni,
xiyou.wangcong
In-Reply-To: <20260701081105.GA1289614@shredder>
On Wed, Jul 1, 2026 at 5:11 PM Ido Schimmel <idosch@nvidia.com> wrote:
>
> I agree, but let's do it as a separate change in net-next. The current
> one line fix is correct and fixes the root cause. Clearing the pointer
> happens to fix the problem because it relies on mc_hash only being
> accessible via dev->in_dev (vs reaching in_dev via a different path).
Acked, I can send out a separate patch for fixing this part and keep
this change as it.
^ permalink raw reply
* [PATCH net-next v3 0/3] ptp: Add driver for R-Car Gen4 gPTP timer
From: Niklas Söderlund @ 2026-07-01 9:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund
Hello,
This series is the first part cleaning up how PTP timer support is
implemented on R-Car Gen4. Currently there is partial support for it in
some of the Ethernet devices that can use it, but not all.
The partial support have been implemented by hacking the gPTP module
directly into the first Ethernet device driver that used it, RTSN for
V4H and RSWITCH for S4. This is understandable as earlier R-Car
generations had a dedicated gPTP timer for each Ethernet device, but on
Gen4 there is a single system-wide PTP timer shared by all.
The current implementation makes it impossible for other Ethernet
devices on the platform to use the PTP timer without messing around with
other Ethernet device drivers.
The effort to clean this up starts with this series which adds the
system-wide gPTP timer as its own driver and device tree node.
This series will then be followed by work to add proper PTP support to
the R-Car RAVB Gen4 driver, which currently advertises to user-space it
supports PTP but which implementation is broken and does not work.
This will in turn be followed by work to the RTSN and RSWITCH drivers
will be be switched from its current partial support by mapping the gPTP
address space directly to instead use this driver.
Having both this and RTSN/RSWITCH described and enabled (!) in device
tree will not work as they will try to use the same memory region. For
this reason this new solution will only be enabled on platforms
after all user's of the gPTP clock have moved to only use the new
centralized timer. But in the interim both devices will be described
(but not enabled) in the platforms base dtsi file.
For some platforms this is straight forward, such as V4H Sparrow Hawk,
which only have the RAVB Ethernet interface. This platform currently
have no users of the PTP timer, but still advertise it supports it. This
and the soon to be posted RAVB patches solves that.
As the RAVB patches depends on this series the device tree node for the
gPTP clock is added in this series but will be enabled and linked to
consumers in the RAVB gPTP series for platforms where it will not
conflict with RTSN and RSWITCH. And further enabled as more of this is
cleaned up.
The gPTP driver itself is heavily influence by the existing partial
support for gPTP in the RTSN and RSWITCH drivers and the Renesas BSP.
Niklas Söderlund (3):
dt-bindings: ptp: renesas,rcar-gen4-gptp: Add R-Car Gen4
ptp: Add driver for R-Car Gen4
arm64: dts: renesas: r8a779g0: Add gPTP node
.../bindings/ptp/renesas,rcar-gen4-gptp.yaml | 64 +++++
MAINTAINERS | 7 +
arch/arm64/boot/dts/renesas/r8a779g0.dtsi | 9 +
drivers/ptp/Kconfig | 12 +
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_rcar_gen4.c | 219 ++++++++++++++++++
6 files changed, 312 insertions(+)
create mode 100644 Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
create mode 100644 drivers/ptp/ptp_rcar_gen4.c
--
2.55.0
^ permalink raw reply
* [PATCH net-next v3 1/3] dt-bindings: ptp: renesas,rcar-gen4-gptp: Add R-Car Gen4
From: Niklas Söderlund @ 2026-07-01 9:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund, Krzysztof Kozlowski
In-Reply-To: <20260701090607.1108208-1-niklas.soderlund+renesas@ragnatech.se>
Add bindings for the R-Car Gen4 gPTP timer. The timer enables accurate
synchronization of the clock in the control system. The timer is
system-wide and used by different Ethernet devices on each Gen4 platform.
- On R-Car S4 it is shared between RSWITCH and RAVB.
- On R-Car V4H it is shared between RTSN and RAVB.
- On R-Car V4M it is only used by RAVB.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
* Changes since v1
- Drop 'binding for' for patch subject.
- Drop comment for renesas,rcar-gen4-gptp compatible to match other
Renesas bindings.
- Drop unused label in example.
- Rename node ptp in example.
---
.../bindings/ptp/renesas,rcar-gen4-gptp.yaml | 64 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
diff --git a/Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml b/Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
new file mode 100644
index 000000000000..3edd64d40038
--- /dev/null
+++ b/Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright (C) 2026 Renesas Electronics Corp.
+# Copyright (C) 2026 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ptp/renesas,rcar-gen4-gptp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R-Car Gen4 gPTP timer
+
+maintainers:
+ - Niklas Söderlund <niklas.soderlund@ragnatech.se>
+
+description:
+ The R-Car Gen4 gPTP timer enables accurate synchronization of the clock in
+ the control system. The timer is system-wide and used by different Ethernet
+ devices on each Gen4 platform.
+
+ - On R-Car S4 it is shared between RSWITCH and RAVB.
+ - On R-Car V4H it is shared between RTSN and RAVB.
+ - On R-Car V4M it is only used by RAVB.
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - renesas,r8a779f0-gptp # S4-8
+ - renesas,r8a779g0-gptp # V4H
+ - renesas,r8a779h0-gptp # V4M
+ - const: renesas,rcar-gen4-gptp
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+ - resets
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/r8a779g0-cpg-mssr.h>
+ #include <dt-bindings/power/r8a779g0-sysc.h>
+
+ ptp@e6449000 {
+ compatible = "renesas,r8a779g0-gptp", "renesas,rcar-gen4-gptp";
+ reg = <0xe6449000 0x500>;
+ clocks = <&cpg CPG_MOD 2723>;
+ power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
+ resets = <&cpg 2723>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..ef17128d6f3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22901,6 +22901,12 @@ S: Maintained
F: Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
F: drivers/mtd/nand/raw/renesas-nand-controller.c
+RENESAS R-CAR GEN4 GPTP DRIVER
+M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
+L: linux-renesas-soc@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
+
RENESAS R-CAR GYROADC DRIVER
M: Marek Vasut <marek.vasut+renesas@mailbox.org>
L: linux-iio@vger.kernel.org
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v3 2/3] ptp: Add driver for R-Car Gen4
From: Niklas Söderlund @ 2026-07-01 9:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund
In-Reply-To: <20260701090607.1108208-1-niklas.soderlund+renesas@ragnatech.se>
Add driver for the gPTP timer found on R-Car Gen4 devices. The timer is
system-wide and shared by different Ethernet devices on each Gen4
platform. The operation of the timer is however not completely in
depended of the systems Ethernet devices.
- On R-Car S4 is gated by the RSWITCH Ethernet module clock.
- On R-Car V4H is gated by the RTSN Ethernet module clock.
- On R-Car V4M is gated by its own module clock, the system have
neither RTSN or RSWITCH device. But the module clock is the same as
RTSN on V4H and the documentation referees to it as tsn (EtherTSN).
The gPTP device do have its own register space on all three platforms.
But on S4 and V4H it will share its clock and reset property with
RSWITCH or RTSN, respectively.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
MAINTAINERS | 1 +
drivers/ptp/Kconfig | 12 ++
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_rcar_gen4.c | 219 ++++++++++++++++++++++++++++++++++++
4 files changed, 233 insertions(+)
create mode 100644 drivers/ptp/ptp_rcar_gen4.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ef17128d6f3f..4a387623409b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22906,6 +22906,7 @@ M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
L: linux-renesas-soc@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/ptp/renesas,rcar-gen4-gptp.yaml
+F: drivers/ptp/ptp_rcar_gen4.c
RENESAS R-CAR GYROADC DRIVER
M: Marek Vasut <marek.vasut+renesas@mailbox.org>
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index b93640ca08b7..3593fd9da92a 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -263,4 +263,16 @@ config PTP_NETC_V4_TIMER
synchronization. It also supports periodic output signal (e.g. PPS)
and external trigger timestamping.
+config PTP_RCAR_GEN4
+ tristate "Renesas R-Car Gen4 PTP Driver"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on PTP_1588_CLOCK
+ help
+ This driver adds support for using the Renesas R-Car Gen4 gPTP timer
+ as a PTP clock, the clock can then be used by Gen4 Ethernet drivers
+ for PTP time synchronization.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp_rcar_gen4.
+
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index bdc47e284f14..0464a586bed2 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
obj-$(CONFIG_PTP_DFL_TOD) += ptp_dfl_tod.o
obj-$(CONFIG_PTP_S390) += ptp_s390.o
obj-$(CONFIG_PTP_NETC_V4_TIMER) += ptp_netc.o
+obj-$(CONFIG_PTP_RCAR_GEN4) += ptp_rcar_gen4.o
diff --git a/drivers/ptp/ptp_rcar_gen4.c b/drivers/ptp/ptp_rcar_gen4.c
new file mode 100644
index 000000000000..ab0be2431be8
--- /dev/null
+++ b/drivers/ptp/ptp_rcar_gen4.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Renesas R-Car Gen4 gPTP device driver
+ *
+ * Copyright (C) 2026 Renesas Electronics Corporation
+ * Copyright (C) 2026 Niklas Söderlund <niklas.soderlund@ragnatech.se>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/types.h>
+
+#define PTPTMEC_REG 0x0010
+#define PTPTMDC_REG 0x0014
+#define PTPTIVC0_REG 0x0020
+#define PTPTOVC00_REG 0x0030
+#define PTPTOVC10_REG 0x0034
+#define PTPTOVC20_REG 0x0038
+#define PTPGPTPTM00_REG 0x0050
+#define PTPGPTPTM10_REG 0x0054
+#define PTPGPTPTM20_REG 0x0058
+
+struct ptp_rcar_gen4_priv {
+ void __iomem *base;
+ struct clk *clk;
+
+ struct ptp_clock *clock;
+ struct ptp_clock_info info;
+
+ spinlock_t lock; /* Registers access. */
+ s64 default_addend;
+};
+
+#define ptp_to_priv(ptp) container_of(ptp, struct ptp_rcar_gen4_priv, info)
+
+static int ptp_rcar_gen4_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ s64 addend = priv->default_addend;
+ bool neg_adj = scaled_ppm < 0;
+ unsigned long flags;
+ s64 diff;
+
+ if (neg_adj)
+ scaled_ppm = -scaled_ppm;
+ diff = div_s64(addend * scaled_ppm_to_ppb(scaled_ppm), NSEC_PER_SEC);
+ addend = neg_adj ? addend - diff : addend + diff;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ iowrite32(addend, priv->base + PTPTIVC0_REG);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void _ptp_rcar_gen4_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+
+ lockdep_assert_held(&priv->lock);
+
+ ts->tv_nsec = ioread32(priv->base + PTPGPTPTM00_REG);
+ ts->tv_sec = ioread32(priv->base + PTPGPTPTM10_REG) |
+ ((s64)ioread32(priv->base + PTPGPTPTM20_REG) << 32);
+}
+
+static int ptp_rcar_gen4_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ _ptp_rcar_gen4_gettime(ptp, ts);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void _ptp_rcar_gen4_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+
+ lockdep_assert_held(&priv->lock);
+
+ iowrite32(1, priv->base + PTPTMDC_REG);
+ iowrite32(0, priv->base + PTPTOVC20_REG);
+ iowrite32(0, priv->base + PTPTOVC10_REG);
+ iowrite32(0, priv->base + PTPTOVC00_REG);
+ iowrite32(1, priv->base + PTPTMEC_REG);
+ iowrite32(ts->tv_sec >> 32, priv->base + PTPTOVC20_REG);
+ iowrite32(ts->tv_sec, priv->base + PTPTOVC10_REG);
+ iowrite32(ts->tv_nsec, priv->base + PTPTOVC00_REG);
+}
+
+static int ptp_rcar_gen4_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ _ptp_rcar_gen4_settime(ptp, ts);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int ptp_rcar_gen4_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct ptp_rcar_gen4_priv *priv = ptp_to_priv(ptp);
+ struct timespec64 ts;
+ unsigned long flags;
+ s64 now;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ _ptp_rcar_gen4_gettime(ptp, &ts);
+ now = ktime_to_ns(timespec64_to_ktime(ts));
+ ts = ns_to_timespec64(now + delta);
+ _ptp_rcar_gen4_settime(ptp, &ts);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static struct ptp_clock_info ptp_rcar_gen4_info = {
+ .owner = THIS_MODULE,
+ .name = "R-Car Gen4 gPTP",
+ .max_adj = 50000000,
+ .adjfine = ptp_rcar_gen4_adjfine,
+ .adjtime = ptp_rcar_gen4_adjtime,
+ .gettime64 = ptp_rcar_gen4_gettime,
+ .settime64 = ptp_rcar_gen4_settime,
+};
+
+static int ptp_rcar_gen4_probe(struct platform_device *pdev)
+{
+ struct ptp_rcar_gen4_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ spin_lock_init(&priv->lock);
+
+ priv->info = ptp_rcar_gen4_info;
+
+ /* Default timer increment in ns.
+ * bit[31:27] - integer
+ * bit[26:0] - decimal
+ * increment[ns] = perid[ns] * 2^27 => (1ns * 2^27) / rate[hz]
+ */
+ priv->default_addend =
+ div_s64(1000000000LL << 27, clk_get_rate(priv->clk));
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ iowrite32(priv->default_addend, priv->base + PTPTIVC0_REG);
+
+ priv->clock = ptp_clock_register(&priv->info, dev);
+ if (IS_ERR(priv->clock))
+ return PTR_ERR(priv->clock);
+
+ iowrite32(1, priv->base + PTPTMEC_REG);
+
+ return 0;
+}
+
+static void ptp_rcar_gen4_remove(struct platform_device *pdev)
+{
+ struct ptp_rcar_gen4_priv *priv = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ ptp_clock_unregister(priv->clock);
+
+ iowrite32(1, priv->base + PTPTMDC_REG);
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+}
+
+static const struct of_device_id ptp_rcar_gen4_of_match[] = {
+ { .compatible = "renesas,rcar-gen4-gptp", },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ptp_rcar_gen4_of_match);
+
+static struct platform_driver ptp_rcar_gen4_driver = {
+ .driver = {
+ .name = "ptp-rcar-gen4",
+ .of_match_table = ptp_rcar_gen4_of_match,
+ },
+ .probe = ptp_rcar_gen4_probe,
+ .remove = ptp_rcar_gen4_remove,
+};
+module_platform_driver(ptp_rcar_gen4_driver);
+
+MODULE_AUTHOR("Niklas Söderlund");
+MODULE_DESCRIPTION("Renesas R-Car Gen4 gPTP driver");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v3 3/3] arm64: dts: renesas: r8a779g0: Add gPTP node
From: Niklas Söderlund @ 2026-07-01 9:06 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm, Richard Cochran, Andrew Lunn,
DavidS. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-renesas-soc, devicetree, linux-kernel, netdev
Cc: Niklas Söderlund
In-Reply-To: <20260701090607.1108208-1-niklas.soderlund+renesas@ragnatech.se>
The gPTP module is shared between the RAVB and RTSN Ethernet devices on
the SoC.
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
---
* Changes since v2
- Preserve sort order by unit-address.
* Changes since v1
- Rename node ptp.
---
arch/arm64/boot/dts/renesas/r8a779g0.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
index 82a7278836e5..b9b860ef7035 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a779g0.dtsi
@@ -589,6 +589,15 @@ tmu4: timer@ffc00000 {
status = "disabled";
};
+ gptp: ptp@e6449000 {
+ compatible = "renesas,r8a779g0-gptp", "renesas,rcar-gen4-gptp";
+ reg = <0 0xe6449000 0 0x500>;
+ clocks = <&cpg CPG_MOD 2723>;
+ power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
+ resets = <&cpg 2723>;
+ status = "disabled";
+ };
+
tsn0: ethernet@e6460000 {
compatible = "renesas,r8a779g0-ethertsn", "renesas,rcar-gen4-ethertsn";
reg = <0 0xe6460000 0 0x7000>,
--
2.55.0
^ permalink raw reply related
* Re: [PATCH net-next v3 5/5] selftest: Add tests for useful handling of LSM denials on SCM_RIGHTS
From: Jori Koolstra @ 2026-07-01 9:31 UTC (permalink / raw)
To: Christian Brauner
Cc: Jakub Kicinski, Aleksa Sarai, Kuniyuki Iwashima, David S . Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, netdev, linux-fsdevel,
linux-kernel
In-Reply-To: <20260701-malen-gutmachen-stengel-2c70ad5d2971@brauner>
> Op 01-07-2026 09:38 CEST schreef Christian Brauner <brauner@kernel.org>:
>
> >
> > I just need some LSM to trigger the reject of security_file_receive()
> > and Smack was the easiest to get going. The series is totally agnostic
> > to the used LSM. I am fine with moving the tests elsewhere or porting
> > them to SELinux if that is really necessary. We could also drop them
> > altogether.
> >
> > What do you propose?
>
> I'm pretty sure the easiest will be to use a tiny bpf program to reject
> security_file_receive().
Ah. Well, that's a testament to how much there's still to learn for me.
I didn't even know that bpf could hook into LSM calls :)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox