* [PATCH net-next v6 12/12] net: airoha: Better handle MIB for GDM with multiple port attached
From: Lorenzo Bianconi @ 2026-05-11 10:49 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Brown Huang
In-Reply-To: <20260511-airoha-eth-multi-serdes-v6-0-c899462c4f75@kernel.org>
From: Christian Marangi <ansuelsmth@gmail.com>
In the context of a GDM that can have multiple port attached (GDM3/4)
the HW counter (MIB) are global for the GDM port. This cause duplicated
stats reported to the kernel for the related interface.
The SoC supports a split MIB feature where each counter is tracked based
on the relevant HW channel (NBQ) to account for this scenario and
provide a way to select the related counter on accessing the MIB
registers.
Enable this feature for GDM3 and GDM4 and configure the relevant HW
channel before updating the HW stats to report correct HW counter to the
kernel for the related interface.
Also move the stats struct from port to dev since HW counter are
now specific to the network interface instead of the GDM port.
Co-developed-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Brown Huang <Brown.huang@airoha.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 173 ++++++++++++++++---------------
drivers/net/ethernet/airoha/airoha_eth.h | 7 +-
2 files changed, 93 insertions(+), 87 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 533ffe20f833..27ccc636d800 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1621,143 +1621,146 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
- spin_lock(&port->stats.lock);
- u64_stats_update_begin(&port->stats.syncp);
+ spin_lock(&port->stats_lock);
+ u64_stats_update_begin(&dev->stats.syncp);
+
+ /* Read relevant MIB for GDM with multiple port attached */
+ if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
+ airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_TX_MIB_ID_MASK | FE_RX_MIB_ID_MASK,
+ FIELD_PREP(FE_TX_MIB_ID_MASK, dev->nbq) |
+ FIELD_PREP(FE_RX_MIB_ID_MASK, dev->nbq));
/* TX */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
- port->stats.tx_ok_pkts += ((u64)val << 32);
+ dev->stats.tx_ok_pkts = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
- port->stats.tx_ok_pkts += val;
+ dev->stats.tx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
- port->stats.tx_ok_bytes += ((u64)val << 32);
+ dev->stats.tx_ok_bytes = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
- port->stats.tx_ok_bytes += val;
+ dev->stats.tx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
- port->stats.tx_drops += val;
+ dev->stats.tx_drops = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
- port->stats.tx_broadcast += val;
+ dev->stats.tx_broadcast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
- port->stats.tx_multicast += val;
+ dev->stats.tx_multicast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
- port->stats.tx_len[i] += val;
+ dev->stats.tx_len[i] = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
- port->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
- port->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] = val;
/* RX */
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
- port->stats.rx_ok_pkts += ((u64)val << 32);
+ dev->stats.rx_ok_pkts = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
- port->stats.rx_ok_pkts += val;
+ dev->stats.rx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
- port->stats.rx_ok_bytes += ((u64)val << 32);
+ dev->stats.rx_ok_bytes = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
- port->stats.rx_ok_bytes += val;
+ dev->stats.rx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
- port->stats.rx_drops += val;
+ dev->stats.rx_drops = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
- port->stats.rx_broadcast += val;
+ dev->stats.rx_broadcast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
- port->stats.rx_multicast += val;
+ dev->stats.rx_multicast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
- port->stats.rx_errors += val;
+ dev->stats.rx_errors = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
- port->stats.rx_crc_error += val;
+ dev->stats.rx_crc_error = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
- port->stats.rx_over_errors += val;
+ dev->stats.rx_over_errors = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
- port->stats.rx_fragment += val;
+ dev->stats.rx_fragment = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
- port->stats.rx_jabber += val;
+ dev->stats.rx_jabber = val;
i = 0;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
- port->stats.rx_len[i] += val;
+ dev->stats.rx_len[i] = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
- port->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
- port->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
- port->stats.rx_len[i++] += val;
-
- /* reset mib counters */
- airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(port->id),
- FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
+ dev->stats.rx_len[i++] = val;
- u64_stats_update_end(&port->stats.syncp);
- spin_unlock(&port->stats.lock);
+ u64_stats_update_end(&dev->stats.syncp);
+ spin_unlock(&port->stats_lock);
}
static int airoha_dev_open(struct net_device *netdev)
@@ -1984,6 +1987,11 @@ static int airoha_dev_init(struct net_device *netdev)
case AIROHA_GDM4_IDX: {
struct airoha_eth *eth = dev->eth;
+ /* Enable split MIB for GDM with multiple port attached */
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(port->id),
+ FE_GDM_TX_MIB_SPLIT_EN_MASK |
+ FE_GDM_RX_MIB_SPLIT_EN_MASK);
+
/* GDM2 supports a single net_device */
if (eth->ports[1] && eth->ports[1]->devs[0])
break;
@@ -2022,23 +2030,22 @@ static void airoha_dev_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *storage)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- storage->rx_packets = port->stats.rx_ok_pkts;
- storage->tx_packets = port->stats.tx_ok_pkts;
- storage->rx_bytes = port->stats.rx_ok_bytes;
- storage->tx_bytes = port->stats.tx_ok_bytes;
- storage->multicast = port->stats.rx_multicast;
- storage->rx_errors = port->stats.rx_errors;
- storage->rx_dropped = port->stats.rx_drops;
- storage->tx_dropped = port->stats.tx_drops;
- storage->rx_crc_errors = port->stats.rx_crc_error;
- storage->rx_over_errors = port->stats.rx_over_errors;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ storage->rx_packets = dev->stats.rx_ok_pkts;
+ storage->tx_packets = dev->stats.tx_ok_pkts;
+ storage->rx_bytes = dev->stats.rx_ok_bytes;
+ storage->tx_bytes = dev->stats.tx_ok_bytes;
+ storage->multicast = dev->stats.rx_multicast;
+ storage->rx_errors = dev->stats.rx_errors;
+ storage->rx_dropped = dev->stats.rx_drops;
+ storage->tx_dropped = dev->stats.tx_drops;
+ storage->rx_crc_errors = dev->stats.rx_crc_error;
+ storage->rx_over_errors = dev->stats.rx_over_errors;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
@@ -2297,20 +2304,19 @@ static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
struct ethtool_eth_mac_stats *stats)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
unsigned int start;
airoha_update_hw_stats(dev);
do {
- start = u64_stats_fetch_begin(&port->stats.syncp);
- stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
- stats->OctetsTransmittedOK = port->stats.tx_ok_bytes;
- stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
- stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
- stats->FramesReceivedOK = port->stats.rx_ok_pkts;
- stats->OctetsReceivedOK = port->stats.rx_ok_bytes;
- stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
+ stats->FramesTransmittedOK = dev->stats.tx_ok_pkts;
+ stats->OctetsTransmittedOK = dev->stats.tx_ok_bytes;
+ stats->MulticastFramesXmittedOK = dev->stats.tx_multicast;
+ stats->BroadcastFramesXmittedOK = dev->stats.tx_broadcast;
+ stats->FramesReceivedOK = dev->stats.rx_ok_pkts;
+ stats->OctetsReceivedOK = dev->stats.rx_ok_bytes;
+ stats->BroadcastFramesReceivedOK = dev->stats.rx_broadcast;
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static const struct ethtool_rmon_hist_range airoha_ethtool_rmon_ranges[] = {
@@ -2330,8 +2336,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
const struct ethtool_rmon_hist_range **ranges)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_gdm_port *port = dev->port;
- struct airoha_hw_stats *hw_stats = &port->stats;
+ struct airoha_hw_stats *hw_stats = &dev->stats;
unsigned int start;
BUILD_BUG_ON(ARRAY_SIZE(airoha_ethtool_rmon_ranges) !=
@@ -2344,7 +2349,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
do {
int i;
- start = u64_stats_fetch_begin(&port->stats.syncp);
+ start = u64_stats_fetch_begin(&dev->stats.syncp);
stats->fragments = hw_stats->rx_fragment;
stats->jabbers = hw_stats->rx_jabber;
for (i = 0; i < ARRAY_SIZE(airoha_ethtool_rmon_ranges) - 1;
@@ -2352,7 +2357,7 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
stats->hist[i] = hw_stats->rx_len[i];
stats->hist_tx[i] = hw_stats->tx_len[i];
}
- } while (u64_stats_fetch_retry(&port->stats.syncp, start));
+ } while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
@@ -3189,6 +3194,7 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
+ u64_stats_init(&dev->stats.syncp);
dev->dev = netdev;
dev->port = port;
dev->eth = eth;
@@ -3229,9 +3235,8 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
if (!port)
return -ENOMEM;
- u64_stats_init(&port->stats.syncp);
- spin_lock_init(&port->stats.lock);
port->id = id;
+ spin_lock_init(&port->stats_lock);
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index 364ca76eb3a6..1e9cd899b7cb 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -215,8 +215,6 @@ struct airoha_tx_irq_queue {
};
struct airoha_hw_stats {
- /* protect concurrent hw_stats accesses */
- spinlock_t lock;
struct u64_stats_sync syncp;
/* get_stats64 */
@@ -555,6 +553,8 @@ struct airoha_gdm_dev {
u32 flags;
int nbq;
+
+ struct airoha_hw_stats stats;
};
struct airoha_gdm_port {
@@ -562,7 +562,8 @@ struct airoha_gdm_port {
int id;
int users;
- struct airoha_hw_stats stats;
+ /* protect concurrent hw_stats accesses */
+ spinlock_t stats_lock;
struct metadata_dst *dsa_meta[AIROHA_MAX_DSA_PORTS];
};
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v6 11/12] net: airoha: Support multiple LAN/WAN interfaces for hw MAC address configuration
From: Lorenzo Bianconi @ 2026-05-11 10:49 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Lorenzo Bianconi
Cc: Christian Marangi, Benjamin Larsson, linux-arm-kernel,
linux-mediatek, netdev, devicetree, Madhur Agrawal
In-Reply-To: <20260511-airoha-eth-multi-serdes-v6-0-c899462c4f75@kernel.org>
The EN7581 and AN7583 SoCs provide registers to configure hardware LAN/WAN
MAC addresses, used to determine whether received traffic is destined for
this host or should be forwarded to another device.
The SoC hardware design assumes all interfaces configured as LAN (or WAN)
share a common upper MAC address, which is programmed into the
REG_FE_{LAN,WAN}_MAC_H register. The lower bytes of 'local' addresses can
be expressed as a range via the REG_FE_MAC_LMIN and REG_FE_MAC_LMAX
registers.
Previously, only a single interface was considered when programming these
registers. Extend the logic to derive the correct minimum and maximum
values for REG_FE_MAC_LMIN/REG_FE_MAC_LMAX when two or more interfaces are
configured as LAN or WAN.
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 75 +++++++++++++++++++++++++++-----
drivers/net/ethernet/airoha/airoha_eth.h | 2 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 4 +-
3 files changed, 66 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 16c0ff9999da..533ffe20f833 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -71,20 +71,67 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
}
-static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
+static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
{
struct airoha_eth *eth = dev->eth;
- u32 val, reg;
+ u8 ref_addr[ETH_ALEN] = {};
+ u32 reg, val, lmin, lmax;
+ int i;
+
+ lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5];
+ lmax = lmin;
+
+ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
+ struct airoha_gdm_port *port = eth->ports[i];
+ int j;
+
+ if (!port)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
+ struct airoha_gdm_dev *iter_dev;
+ struct net_device *netdev;
+
+ iter_dev = port->devs[j];
+ if (!iter_dev || iter_dev == dev)
+ continue;
+
+ if (airoha_is_lan_gdm_dev(iter_dev) !=
+ airoha_is_lan_gdm_dev(dev))
+ continue;
+
+ netdev = iter_dev->dev;
+ if (netdev->reg_state != NETREG_REGISTERED)
+ continue;
+
+ ether_addr_copy(ref_addr, netdev->dev_addr);
+ val = (netdev->dev_addr[3] << 16) |
+ (netdev->dev_addr[4] << 8) | netdev->dev_addr[5];
+ if (val < lmin)
+ lmin = val;
+ if (val > lmax)
+ lmax = val;
+ }
+ }
+
+ if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
+ /* According to the HW design, hw mac address MS bits
+ * must be the same for each net_device with the same
+ * LAN/WAN configuration.
+ */
+ return -EINVAL;
+ }
reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
airoha_fe_wr(eth, reg, val);
- val = (addr[3] << 16) | (addr[4] << 8) | addr[5];
- airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
- airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
+ airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin);
+ airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax);
- airoha_ppe_init_upd_mem(dev);
+ airoha_ppe_init_upd_mem(dev, addr);
+
+ return 0;
}
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
@@ -1814,13 +1861,18 @@ static int airoha_dev_stop(struct net_device *netdev)
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct sockaddr *addr = p;
int err;
- err = eth_mac_addr(netdev, p);
+ err = eth_prepare_mac_addr_change(netdev, p);
if (err)
return err;
- airoha_set_macaddr(dev, netdev->dev_addr);
+ err = airoha_set_macaddr(dev, addr->sa_data);
+ if (err)
+ return err;
+
+ eth_commit_mac_addr_change(netdev, p);
return 0;
}
@@ -1925,6 +1977,7 @@ static int airoha_dev_init(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
+ int err;
switch (port->id) {
case AIROHA_GDM3_IDX:
@@ -1949,12 +2002,12 @@ static int airoha_dev_init(struct net_device *netdev)
}
airoha_dev_set_qdma(dev);
- airoha_set_macaddr(dev, netdev->dev_addr);
+ err = airoha_set_macaddr(dev, netdev->dev_addr);
+ if (err)
+ return err;
if (!airoha_is_lan_gdm_dev(dev) &&
(port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
- int err;
-
err = airoha_set_gdm2_loopback(dev);
if (err) {
dev->flags &= ~AIROHA_PRIV_F_WAN;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index a2241520f2e2..364ca76eb3a6 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -684,7 +684,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr);
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 194cd50b2c74..531ce33528b9 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -1482,12 +1482,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
}
-void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
+void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr)
{
struct airoha_gdm_port *port = dev->port;
- struct net_device *netdev = dev->dev;
struct airoha_eth *eth = dev->eth;
- const u8 *addr = netdev->dev_addr;
u32 val;
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v3 0/3] pmdomain: core: add support for domain hierarchies in DT
From: Ulf Hansson @ 2026-05-11 10:56 UTC (permalink / raw)
To: Kevin Hilman (TI)
Cc: Rob Herring, Geert Uytterhoeven, linux-pm, devicetree,
linux-kernel, arm-scmi, linux-arm-kernel
In-Reply-To: <20260420-topic-lpm-pmdomain-child-ids-v3-0-c2c40bef238c@baylibre.com>
On Tue, 21 Apr 2026 at 01:51, Kevin Hilman (TI) <khilman@baylibre.com> wrote:
>
> Currently, PM domains can only support hierarchy for simple
> providers (e.g. ones with #power-domain-cells = 0).
>
> Add support for oncell providers as well by adding a new property
> `power-domains-child-ids` to describe the parent/child relationship.
>
> Also adds the first user of the new API: the Arm SCMI PM domain driver.
>
> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
> ---
> Changes in v3:
> - cleanup return codes for add_child_ids()
> - < 0: error
> - zero means no child domains present
> - > 0: number of child domains added
> - arm_scmi: adopt to new return codes (only <0 is an error)
> - Link to v2: https://patch.msgid.link/20260410-topic-lpm-pmdomain-child-ids-v2-0-83396e4b5f8b@baylibre.com
The v3 series applied for next, thanks!
Kind regards
Uffe
>
> Changes compared to initial RFC[2]
> - dropped RFC
> - rewrote the parse/add function to use iterators/helpers from of.h
> - add a remove function for cleanup
> - use child domain language instead of subdomain
>
> This idea was previously discussed on the arm-scmi mailing list[1]
> where this approach was proposed by Ulf, and then an initial RFC[2]
> implementation was made. From there, it was suggested by Rob[3] to
> use a nexus node map instead, which led to several more versions
> attempting to implement that, culminating in v5[4], where Rob and
> Geert then had second thoughts about the power-domain-map approach.
>
> Therefore, I've gone back to the approach in the initial RFC[2] to use
> the child-ids approach.
>
> [1] https://lore.kernel.org/arm-scmi/CAPDyKFo_P129sVirHHYjOQT+QUmpymcRJme9obzKJeRgO7B-1A@mail.gmail.com/
> [2] https://lore.kernel.org/all/20250528-pmdomain-hierarchy-onecell-v1-1-851780700c68@baylibre.com/
> [3] https://lore.kernel.org/all/20250528203532.GA704342-robh@kernel.org/
> [4] https://lore.kernel.org/r/20260122-pmdomain-hierarchy-onecell-v5-0-76855ec856bd@baylibre.com
>
> Changes in v2:
> - dt-bindings: fix warinings from make dt_binding_check
> - scmi_pm_domain: switch to dev_err()
> - pmdomain: core: fix locking around add/remove domains
> - pmdomain: error unwind if any children fail to be added
> - pmdomain: fix node reference leak
> - pmdomain: ensure power-domains and child-ids properties are same
> length before iterating
> - Link to v1: https://patch.msgid.link/20260310-topic-lpm-pmdomain-child-ids-v1-0-5361687a18ff@baylibre.com
>
> ---
> Kevin Hilman (TI) (3):
> dt-bindings: power: Add power-domains-child-ids property
> pmdomain: core: add support for power-domains-child-ids
> pmdomain: arm_scmi: add support for domain hierarchies
>
> Documentation/devicetree/bindings/power/power-domain.yaml | 34 ++++++++++++++++++++++++++++++++++
> drivers/pmdomain/arm/scmi_pm_domain.c | 14 +++++++++++++-
> drivers/pmdomain/core.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/pm_domain.h | 16 ++++++++++++++++
> 4 files changed, 230 insertions(+), 1 deletion(-)
> ---
> base-commit: f7b88edb52c8dd01b7e576390d658ae6eef0e134
> change-id: 20260310-topic-lpm-pmdomain-child-ids-e3d57ae57040
>
> Best regards,
> --
> Kevin Hilman (TI) <khilman@baylibre.com>
>
^ permalink raw reply
* Re: [PATCH v4 00/15] SCMI Clock rates discovery rework
From: Cristian Marussi @ 2026-05-11 11:00 UTC (permalink / raw)
To: Sudeep Holla
Cc: Cristian Marussi, Geert Uytterhoeven, linux-kernel,
linux-arm-kernel, arm-scmi, linux-clk, linux-renesas-soc,
philip.radford, james.quinlan, f.fainelli, vincent.guittot,
etienne.carriere, peng.fan, michal.simek, geert+renesas,
kuninori.morimoto.gx, marek.vasut+renesas
In-Reply-To: <20260511-origami-ape-of-inspiration-7b55f3@sudeepholla>
On Mon, May 11, 2026 at 09:09:40AM +0100, Sudeep Holla wrote:
> On Fri, May 08, 2026 at 07:25:49PM +0200, Geert Uytterhoeven wrote:
> > Hi Cristian,
> >
> > On Fri, 8 May 2026 at 17:33, Cristian Marussi <cristian.marussi@arm.com> wrote:
> > > it was a known limitation, in the SCMI Clock protocol support, the lack of
> > > dynamic allocation around per-clock rates discovery: fixed size statically
> > > per-clock rates arrays did not scale and was increasingly a waste of memory
> > > (see [1]).
> >
> > [...]
> >
> > > v3 -->v4
> > > - Rebased on v7.1-rc2
> > > - Removed unused info.rate_discrete [Geert]
> > > - Made dev_dbg() more meaningful by printing tot_rates [Geert]
> > > - Fixed build bisectability by renaming properly to iter_response_bound_cleanup()
> >
> > Thanks for the update!
> >
> > I believe you still have a possible runtime bisectability issue
> > between "[PATCH v4 04/15] firmware: arm_scmi: Simplify clock
> > rates exposed interface" and "[PATCH v4 05/15] clk: scmi: Use new
> > simplified per-clock rate properties": 04/15 removes the last setter
> > of scmi_clock_info.rate_discrete, before 05/15 removes the last getter.
> >
>
> I have fixed this up by adding some initialisation in 04/15 and removing it
> in 06/15. Cristian, if possible can you check if the functionality will
> remain intact after 05/15 ?
LGTM.
Thanks,
Cristian
^ permalink raw reply
* Re: [RFC PATCH] KVM: arm64: vgic: Skip vCPU trylock for pre-init register access
From: Yao Yuan @ 2026-05-11 11:03 UTC (permalink / raw)
To: David Woodhouse
Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Sascha Bischoff,
Paolo Bonzini, Jonathan Cameron, Eric Auger,
Raghavendra Rao Ananta, David Woodhouse, Maxim Levitsky,
linux-arm-kernel, kvmarm, kvm
In-Reply-To: <6564c8b967948e30a8d3f35b6ef5de79dd5feeb7.camel@infradead.org>
On Sun, May 10, 2026 at 11:11:43PM +0800, David Woodhouse wrote:
> From: David Woodhouse <dwmw@amazon.co.uk>
>
> When creating a VM, userspace sets pre-init distributor registers such
> as GICD_IIDR before the VGIC is initialized.
>
> Strictly speaking there's no reason this couldn't be done at VM creation
> time before any vCPUs exist at all, but the design of the userspace API
> does require vCPU0 to have been created, as the system-wide registers
> are effectively accessed via vCPU0.
>
> So a VMM can't just set the IIDR at startup before spawning vCPU threads;
> it has to be done once the vCPUs are being created.
>
> However, kvm_trylock_all_vcpus() makes it unreliable by causing spurious
> -EBUSY failures if any vCPU cannot be locked. So userspace is forced to
> create the vCPUs (well, at least vCPU0), and is then forced to have a
> full synchronization point and quiesce them all in order to reliably set
> the IIDR.
>
> To slightly reduce the pain of all this, skip the trylock when the VGIC
> is not yet initialized. There is no need to lock the vCPUs if they can't
> be accessing it anyway.
>
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
> Other options would include making it possible to set the IIDR before
> creating any vCPUs, either by creating a new device-level attribute or
> special-casing it not to require vCPU0 for DIST_REGS that aren't really
> associated to a vCPU.
>
> Deprecating kvm_trylock_all_vcpus() and killing every user of it with
> fire would also be a good option, perhaps... :)
>
> arch/arm64/kvm/vgic/vgic-kvm-device.c | 38 ++++++++++++++++++++-------
> 1 file changed, 29 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c
> index a96c77dccf35..e17ea9f07f5f 100644
> --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c
> +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c
> @@ -540,7 +540,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
> struct vgic_reg_attr reg_attr;
> gpa_t addr;
> struct kvm_vcpu *vcpu;
> - bool uaccess;
> + bool uaccess, vcpus_locked = false;
> u32 val;
> int ret;
>
> @@ -566,18 +566,37 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
> return -EFAULT;
> }
>
> + if (!vgic_initialized(dev->kvm) && !reg_allowed_pre_init(attr))
> + return -EBUSY;
> +
> mutex_lock(&dev->kvm->lock);
>
> - if (kvm_trylock_all_vcpus(dev->kvm)) {
> - mutex_unlock(&dev->kvm->lock);
> - return -EBUSY;
> + /*
> + * Pre-init registers (e.g. GICD_IIDR) don't need vCPU quiescence
> + * since the VGIC isn't live yet. Skip the trylock to avoid spurious
> + * -EBUSY when vCPU threads happen to be running.
> + */
> + if (vgic_initialized(dev->kvm)) {
> + if (kvm_trylock_all_vcpus(dev->kvm)) {
> + mutex_unlock(&dev->kvm->lock);
> + return -EBUSY;
> + }
> + vcpus_locked = true;
> }
> -
> mutex_lock(&dev->kvm->arch.config_lock);
>
> - if (!(vgic_initialized(dev->kvm) || reg_allowed_pre_init(attr))) {
> - ret = -EBUSY;
> - goto out;
Hi David,
> + /*
> + * If the VGIC becomes initialized between the above check and taking
> + * config_lock, drop config_lock to lock the VCPUS.
> + */
> + if (vgic_initialized(dev->kvm) && !vcpus_locked) {
> + mutex_unlock(&dev->kvm->arch.config_lock);
> + if (kvm_trylock_all_vcpus(dev->kvm)) {
> + mutex_unlock(&dev->kvm->lock);
> + return -EBUSY;
> + }
> + mutex_lock(&dev->kvm->arch.config_lock);
> + vcpus_locked = true;
Question: Why not do vgic_initialized(dev->kvm) and
reg_allowed_pre_init(attr) checking after take config_lock,
and depends on the checking result to decide trylocak all
vcpus or not ?
> }
>
> switch (attr->group) {
> @@ -612,7 +631,8 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
>
> out:
> mutex_unlock(&dev->kvm->arch.config_lock);
> - kvm_unlock_all_vcpus(dev->kvm);
> + if (vcpus_locked)
> + kvm_unlock_all_vcpus(dev->kvm);
> mutex_unlock(&dev->kvm->lock);
>
> if (!ret && uaccess && !is_write) {
> --
> 2.43.0
>
>
^ permalink raw reply
* [PATCH v12 00/28] CoreSight: Refactor power management for CoreSight path
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan, Mike Leach
This series focuses on CoreSight path power management. The changes can
be divided into four parts for review:
Patches 01 - 10: Preparison for CPU PM:
Fix source disabling on idr_alloc failure.
Fix helper enable failure handling.
Refactor CPU ID stored in csdev.
Move CPU lock to sysfs layer.
Move per-CPU source pointer from etm-perf to core layer.
Refactor etm-perf to retrieve source via per-CPU's event
data for lockless and get source reference during AUX
setup.
Patches 11 - 13: Refactor CPU idle flow managed in the CoreSight core
layer.
Patches 14 - 23: Refactor path enable / disable with range, control path
during CPU idle.
Patches 24 - 25: Support the sink (TRBE) control during CPU idle.
Patches 26 - 28: Move CPU hotplug into the core layer, and fix sysfs
mode for hotplug.
This series is rebased on the coresight-next branch and has been verified
on Juno-r2 (ETM + ETR) and FVP RevC (ETE + TRBE). Built successfully
for armv7 (ARCH=arm).
---
Changes in v12:
- Added comments on coresight_{get|put)_percpu_source_ref (Suzuki).
- Refined failure handling in path enable (Suzuki).
- Added coresight_is_software_source() helper (Suzuki).
- Reordered taking ref on csdev and its parent in patch 07.
- Define the enum mode with bit flags.
- Minor improvements on commit logs.
- Rebased on lastest coresight-next.
- Link to v11: https://lore.kernel.org/r/20260501-arm_coresight_path_power_management_improvement-v11-0-fc7fb9d5af1c@arm.com
Changes in v11:
- Moved per-CPU source pointer from etm-perf to core (Suzuki).
- Added grabbing/ungrabbing csdev for device reference (Suzuki).
- Minor refine for error handling and logs in CPU PM (James).
- Refactored etm-perf with fetching path/source from event data (Suzuki).
- Fixed Helper error handling (sashiko).
- Added Jie's test tag (thanks!).
- Minor improvement for comments and commit logs.
- Link to v10: https://lore.kernel.org/r/20260405-arm_coresight_path_power_management_improvement-v10-0-13e94754a8be@arm.com
Changes in v10:
- Removed redundant checks in ETMv4 PM callbacks (sashiko).
- Added a new const structure etm4_cs_pm_ops (sashiko).
- Used fine-grained spinlock on sysfs_active_config (sashiko).
- Blocked notification after failures in save / restore to avoid lockups.
- Changed Change CPUHP_AP_ARM_CORESIGHT_STARTING to
CPUHP_AP_ARM_CORESIGHT_ONLINE so that the CPU hotplug callback runs in
the thread context (sashiko).
- Link to v9: https://lore.kernel.org/r/20260401-arm_coresight_path_power_management_improvement-v9-0-091d73e44072@arm.com
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
Jie Gan (1):
coresight: Fix source not disabled on idr_alloc_u32 failure
Leo Yan (26):
coresight: Handle helper enable failure properly
coresight: Extract device init into coresight_init_device()
coresight: Populate CPU ID into coresight_device
coresight: Remove .cpu_id() callback from source ops
coresight: Take hotplug lock in enable_source_store() for Sysfs mode
coresight: perf: Retrieve path and source from event data
coresight: Take a reference on csdev
coresight: Move per-CPU source pointer to core layer
coresight: Take per-CPU source reference during AUX setup
coresight: Register CPU PM notifier in core layer
coresight: etm4x: Hook CPU PM callbacks
coresight: etm4x: Remove redundant checks in PM save and restore
coresight: syscfg: Use IRQ-safe spinlock to protect active variables
coresight: Disable source helpers in coresight_disable_path()
coresight: Control path with range
coresight: Use helpers to fetch first and last nodes
coresight: Introduce coresight_enable_source() helper
coresight: Save active path for system tracers
coresight: etm4x: Set active path on target CPU
coresight: etm3x: Set active path on target CPU
coresight: sysfs: Use source's path pointer for path control
coresight: Control path during CPU idle
coresight: Add PM callbacks for sink device
coresight: sysfs: Increment refcount only for software source
coresight: Move CPU hotplug callbacks to core layer
coresight: sysfs: Validate CPU online status for per-CPU sources
Yabin Cui (1):
coresight: trbe: Save and restore state across CPU low power state
drivers/hwtracing/coresight/coresight-catu.c | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 548 ++++++++++++++++++---
drivers/hwtracing/coresight/coresight-cti-core.c | 9 +-
drivers/hwtracing/coresight/coresight-etm-perf.c | 285 ++++++-----
drivers/hwtracing/coresight/coresight-etm3x-core.c | 73 +--
drivers/hwtracing/coresight/coresight-etm4x-core.c | 166 ++-----
drivers/hwtracing/coresight/coresight-priv.h | 6 +
drivers/hwtracing/coresight/coresight-syscfg.c | 38 +-
drivers/hwtracing/coresight/coresight-syscfg.h | 2 +
drivers/hwtracing/coresight/coresight-sysfs.c | 131 ++---
drivers/hwtracing/coresight/coresight-trbe.c | 61 ++-
include/linux/coresight.h | 27 +-
include/linux/cpuhotplug.h | 2 +-
13 files changed, 870 insertions(+), 480 deletions(-)
---
base-commit: 0ec0a8785d21f63db520bd9d2a67c55e855d36a8
change-id: 20251104-arm_coresight_path_power_management_improvement-dab4966f8280
Best regards,
--
Leo Yan <leo.yan@arm.com>
^ permalink raw reply
* [PATCH v12 01/28] coresight: Fix source not disabled on idr_alloc_u32 failure
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
From: Jie Gan <jie.gan@oss.qualcomm.com>
In coresight_enable_sysfs(), for non-CPU sources (SOFTWARE, TPDM,
OTHERS), the source device is enabled via coresight_enable_source_sysfs()
before idr_alloc_u32() maps the path. If idr_alloc_u32() fails, the
original code jumped directly to err_source, which only calls
coresight_disable_path() and coresight_release_path(). The source device
was left enabled with an incremented refcnt but no path tracked for it,
leaving the device in an inconsistent state.
Disable the source before jumping to err_source so the enable and path
operations are fully unwound.
Fixes: 5c0016d7b343 ("coresight: core: Use IDR for non-cpu bound sources' paths.")
Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
---
drivers/hwtracing/coresight/coresight-sysfs.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index d2a6ed8bcc74d64dccc735463f14790b4e80d101..a5c08fab97a1b5d14d961e9b8ec6ef2d67b85944 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -244,8 +244,10 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
- if (ret)
+ if (ret) {
+ coresight_disable_source_sysfs(csdev, NULL);
goto err_source;
+ }
break;
default:
/* We can't be here */
--
2.34.1
^ permalink raw reply related
* [PATCH v12 02/28] coresight: Handle helper enable failure properly
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
If a helper fails to be enabled, unwind any helpers that were already
enabled earlier in the loop. This avoids leaving partially enabled
helpers behind.
Fixes: 6148652807ba ("coresight: Enable and disable helper devices adjacent to the path")
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 46f247f73cf64a97b9353b84ba5b76b991676f5f..ed17c36ad46ac4270abdfdf2a1a742faa6273097 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -499,10 +499,19 @@ static int coresight_enable_helpers(struct coresight_device *csdev,
ret = coresight_enable_helper(helper, mode, path);
if (ret)
- return ret;
+ goto err;
}
return 0;
+
+err:
+ while (i--) {
+ helper = csdev->pdata->out_conns[i]->dest_dev;
+ if (helper && coresight_is_helper(helper))
+ coresight_disable_helper(helper, path);
+ }
+
+ return ret;
}
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
--
2.34.1
^ permalink raw reply related
* [PATCH v12 03/28] coresight: Extract device init into coresight_init_device()
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
This commit extracts the allocation and initialization of the coresight
device structure into a separate function to make future extensions
easier.
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index ed17c36ad46ac4270abdfdf2a1a742faa6273097..48ea882bd10870a6be253ecf0b58860d7a4a0bb0 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1331,20 +1331,16 @@ void coresight_release_platform_data(struct device *dev,
devm_kfree(dev, pdata);
}
-struct coresight_device *coresight_register(struct coresight_desc *desc)
+static struct coresight_device *
+coresight_init_device(struct coresight_desc *desc)
{
- int ret;
struct coresight_device *csdev;
- bool registered = false;
csdev = kzalloc_obj(*csdev);
- if (!csdev) {
- ret = -ENOMEM;
- goto err_out;
- }
+ if (!csdev)
+ return ERR_PTR(-ENOMEM);
csdev->pdata = desc->pdata;
-
csdev->type = desc->type;
csdev->subtype = desc->subtype;
csdev->ops = desc->ops;
@@ -1357,6 +1353,21 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
csdev->dev.release = coresight_device_release;
csdev->dev.bus = &coresight_bustype;
+ return csdev;
+}
+
+struct coresight_device *coresight_register(struct coresight_desc *desc)
+{
+ int ret;
+ struct coresight_device *csdev;
+ bool registered = false;
+
+ csdev = coresight_init_device(desc);
+ if (IS_ERR(csdev)) {
+ ret = PTR_ERR(csdev);
+ goto err_out;
+ }
+
if (csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) {
raw_spin_lock_init(&csdev->perf_sink_id_map.lock);
--
2.34.1
^ permalink raw reply related
* [PATCH v12 05/28] coresight: Remove .cpu_id() callback from source ops
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
The CPU ID can be fetched directly from the coresight_device structure,
so the .cpu_id() callback is no longer needed.
Remove the .cpu_id() callback from source ops and update callers
accordingly.
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 8 ++++----
drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +-
drivers/hwtracing/coresight/coresight-etm3x-core.c | 8 --------
drivers/hwtracing/coresight/coresight-etm4x-core.c | 8 --------
drivers/hwtracing/coresight/coresight-sysfs.c | 4 ++--
include/linux/coresight.h | 3 ---
6 files changed, 7 insertions(+), 26 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index f8c0c2b05888403318372dc76278910c8a5c7e15..703cc4e349f7703a4b38ba897909e03dc9c617a6 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -796,7 +796,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
goto out;
if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) &&
- sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) {
+ sink == per_cpu(csdev_sink, csdev->cpu)) {
if (_coresight_build_path(sink, source, sink, path) == 0) {
found = true;
goto out;
@@ -1023,7 +1023,7 @@ coresight_find_default_sink(struct coresight_device *csdev)
/* look for a default sink if we have not found for this device */
if (!csdev->def_sink) {
if (coresight_is_percpu_source(csdev))
- csdev->def_sink = per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev));
+ csdev->def_sink = per_cpu(csdev_sink, csdev->cpu);
if (!csdev->def_sink)
csdev->def_sink = coresight_find_sink(csdev, &depth);
}
@@ -1761,10 +1761,10 @@ int coresight_etm_get_trace_id(struct coresight_device *csdev, enum cs_mode mode
{
int cpu, trace_id;
- if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE || !source_ops(csdev)->cpu_id)
+ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
return -EINVAL;
- cpu = source_ops(csdev)->cpu_id(csdev);
+ cpu = csdev->cpu;
switch (mode) {
case CS_MODE_SYSFS:
trace_id = coresight_trace_id_get_cpu_id(cpu);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index f85dedf89a3f9e85d568ca4c320fa6fa6d9059ff..cae6738e9906c35d291e4b0f3feae37306b95c06 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -824,7 +824,7 @@ static void etm_addr_filters_sync(struct perf_event *event)
int etm_perf_symlink(struct coresight_device *csdev, bool link)
{
char entry[sizeof("cpu9999999")];
- int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev);
+ int ret = 0, cpu = csdev->cpu;
struct device *pmu_dev = etm_pmu.dev;
struct device *cs_dev = &csdev->dev;
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c
index eb665db1a37d9970f7f55395c0aa23b98a7f3118..ab47f69e923fb191b48b82367dce465c79b3a93d 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c
@@ -466,13 +466,6 @@ static void etm_enable_sysfs_smp_call(void *info)
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
-static int etm_cpu_id(struct coresight_device *csdev)
-{
- struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
- return drvdata->cpu;
-}
-
void etm_release_trace_id(struct etm_drvdata *drvdata)
{
coresight_trace_id_put_cpu_id(drvdata->cpu);
@@ -684,7 +677,6 @@ static void etm_disable(struct coresight_device *csdev,
}
static const struct coresight_ops_source etm_source_ops = {
- .cpu_id = etm_cpu_id,
.enable = etm_enable,
.disable = etm_disable,
};
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 8f270bfc74723f1fc1cead2da1725ab6b50f910e..b7312570a7ae1f0355c01b6f7a54ea2dd8891097 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -231,13 +231,6 @@ static void etm4_cs_unlock(struct etmv4_drvdata *drvdata,
CS_UNLOCK(csa->base);
}
-static int etm4_cpu_id(struct coresight_device *csdev)
-{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
- return drvdata->cpu;
-}
-
void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
{
coresight_trace_id_put_cpu_id(drvdata->cpu);
@@ -1205,7 +1198,6 @@ static void etm4_pause_perf(struct coresight_device *csdev)
}
static const struct coresight_ops_source etm4_source_ops = {
- .cpu_id = etm4_cpu_id,
.enable = etm4_enable,
.disable = etm4_disable,
.resume_perf = etm4_resume_perf,
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index a5c08fab97a1b5d14d961e9b8ec6ef2d67b85944..3087de9e1c12a66ea15a73f92564d623d75d68ec 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -232,7 +232,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
- cpu = source_ops(csdev)->cpu_id(csdev);
+ cpu = csdev->cpu;
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
@@ -284,7 +284,7 @@ void coresight_disable_sysfs(struct coresight_device *csdev)
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
- cpu = source_ops(csdev)->cpu_id(csdev);
+ cpu = csdev->cpu;
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 687190ca11ddeaa83193caa3903a480bac3060d1..e9c20ceb9016fa3db256b8c1147c1fd2027b7b0d 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -395,15 +395,12 @@ struct coresight_ops_link {
/**
* struct coresight_ops_source - basic operations for a source
* Operations available for sources.
- * @cpu_id: returns the value of the CPU number this component
- * is associated to.
* @enable: enables tracing for a source.
* @disable: disables tracing for a source.
* @resume_perf: resumes tracing for a source in perf session.
* @pause_perf: pauses tracing for a source in perf session.
*/
struct coresight_ops_source {
- int (*cpu_id)(struct coresight_device *csdev);
int (*enable)(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode, struct coresight_path *path);
void (*disable)(struct coresight_device *csdev,
--
2.34.1
^ permalink raw reply related
* [PATCH v12 07/28] coresight: perf: Retrieve path and source from event data
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
ETM perf callbacks currently use the per-CPU csdev_src pointer, which
can race with updates during device registration and unregistration.
The AUX setup already builds and stores the path in the event data.
Use this path to retrieve the source instead of csdev_src to avoid
the race.
Export coresight_get_source() and add etm_event_get_ctxt_path() to
retrieve the context's path and its source with READ_ONCE() /
WRITE_ONCE() accessors. Give the comments to explain why this
approach is safe when pause or resume callbacks preempt the disable
callback (e.g. via NMI).
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 2 +-
drivers/hwtracing/coresight/coresight-etm-perf.c | 114 ++++++++++++++---------
drivers/hwtracing/coresight/coresight-priv.h | 1 +
3 files changed, 74 insertions(+), 43 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 703cc4e349f7703a4b38ba897909e03dc9c617a6..5519d323f21f38d719b9030c7d77a9a61948ba1d 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -82,7 +82,7 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
}
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
-static struct coresight_device *coresight_get_source(struct coresight_path *path)
+struct coresight_device *coresight_get_source(struct coresight_path *path)
{
struct coresight_device *csdev;
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index cae6738e9906c35d291e4b0f3feae37306b95c06..206a983239a0b1983b2b78c5e78a5fd988d308ce 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -315,6 +315,35 @@ static bool sinks_compatible(struct coresight_device *a,
(sink_ops(a) == sink_ops(b));
}
+/*
+ * This helper is used for fetching the path pointer via the ctxt.
+ *
+ * Perf event callbacks run on the same CPU in atomic context, but AUX pause
+ * and resume may run in NMI context and preempt other callbacks. Since the
+ * event stop callback clears ctxt->event_data before the data is released,
+ * AUX pause/resume will either observe a NULL pointer and stop fetching the
+ * path pointer, or safely access event_data and the path, as the data has
+ * not yet been freed.
+ */
+static struct coresight_path *etm_event_get_ctxt_path(struct etm_ctxt *ctxt)
+{
+ struct etm_event_data *event_data;
+ struct coresight_path *path;
+
+ if (!ctxt)
+ return NULL;
+
+ event_data = READ_ONCE(ctxt->event_data);
+ if (!event_data)
+ return NULL;
+
+ path = etm_event_cpu_path(event_data, smp_processor_id());
+ if (!path)
+ return NULL;
+
+ return path;
+}
+
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
@@ -464,13 +493,23 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
goto out;
}
-static int etm_event_resume(struct coresight_device *csdev,
- struct etm_ctxt *ctxt)
+static int etm_event_resume(struct coresight_path *path)
{
- if (!ctxt->event_data)
+ struct coresight_device *source;
+ int ret;
+
+ if (!path)
return 0;
- return coresight_resume_source(csdev);
+ source = coresight_get_source(path);
+ if (!source)
+ return 0;
+
+ ret = coresight_resume_source(source);
+ if (ret < 0)
+ dev_err(&source->dev, "Failed to resume ETM event.\n");
+
+ return ret;
}
static void etm_event_start(struct perf_event *event, int flags)
@@ -479,23 +518,19 @@ static void etm_event_start(struct perf_event *event, int flags)
struct etm_event_data *event_data;
struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt);
struct perf_output_handle *handle = &ctxt->handle;
- struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+ struct coresight_device *source, *sink;
struct coresight_path *path;
u64 hw_id;
- if (!csdev)
- goto fail;
-
if (flags & PERF_EF_RESUME) {
- if (etm_event_resume(csdev, ctxt) < 0) {
- dev_err(&csdev->dev, "Failed to resume ETM event.\n");
+ path = etm_event_get_ctxt_path(ctxt);
+ if (etm_event_resume(path) < 0)
goto fail;
- }
return;
}
/* Have we messed up our tracking ? */
- if (WARN_ON(ctxt->event_data))
+ if (WARN_ON(READ_ONCE(ctxt->event_data)))
goto fail;
/*
@@ -523,9 +558,10 @@ static void etm_event_start(struct perf_event *event, int flags)
path = etm_event_cpu_path(event_data, cpu);
path->handle = handle;
- /* We need a sink, no need to continue without one */
+ /* We need source and sink, no need to continue if any is not set */
+ source = coresight_get_source(path);
sink = coresight_get_sink(path);
- if (WARN_ON_ONCE(!sink))
+ if (WARN_ON_ONCE(!source || !sink))
goto fail_end_stop;
/* Nothing will happen without a path */
@@ -533,7 +569,7 @@ static void etm_event_start(struct perf_event *event, int flags)
goto fail_end_stop;
/* Finally enable the tracer */
- if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, path))
+ if (source_ops(source)->enable(source, event, CS_MODE_PERF, path))
goto fail_disable_path;
/*
@@ -557,7 +593,7 @@ static void etm_event_start(struct perf_event *event, int flags)
/* Tell the perf core the event is alive */
event->hw.state = 0;
/* Save the event_data for this ETM */
- ctxt->event_data = event_data;
+ WRITE_ONCE(ctxt->event_data, event_data);
return;
fail_disable_path:
@@ -577,27 +613,26 @@ static void etm_event_start(struct perf_event *event, int flags)
return;
}
-static void etm_event_pause(struct perf_event *event,
- struct coresight_device *csdev,
+static void etm_event_pause(struct coresight_path *path,
+ struct perf_event *event,
struct etm_ctxt *ctxt)
{
- int cpu = smp_processor_id();
- struct coresight_device *sink;
struct perf_output_handle *handle = &ctxt->handle;
- struct coresight_path *path;
+ struct coresight_device *source, *sink;
+ struct etm_event_data *event_data;
unsigned long size;
- if (!ctxt->event_data)
+ if (!path)
return;
- /* Stop tracer */
- coresight_pause_source(csdev);
-
- path = etm_event_cpu_path(ctxt->event_data, cpu);
+ source = coresight_get_source(path);
sink = coresight_get_sink(path);
- if (WARN_ON_ONCE(!sink))
+ if (WARN_ON_ONCE(!source || !sink))
return;
+ /* Stop tracer */
+ coresight_pause_source(source);
+
/*
* The per CPU sink has own interrupt handling, it might have
* race condition with updating buffer on AUX trace pause if
@@ -613,8 +648,9 @@ static void etm_event_pause(struct perf_event *event,
if (!sink_ops(sink)->update_buffer)
return;
+ event_data = READ_ONCE(ctxt->event_data);
size = sink_ops(sink)->update_buffer(sink, handle,
- ctxt->event_data->snk_config);
+ event_data->snk_config);
if (READ_ONCE(handle->event)) {
if (!size)
return;
@@ -630,14 +666,14 @@ static void etm_event_stop(struct perf_event *event, int mode)
{
int cpu = smp_processor_id();
unsigned long size;
- struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
+ struct coresight_device *source, *sink;
struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt);
struct perf_output_handle *handle = &ctxt->handle;
+ struct coresight_path *path = etm_event_get_ctxt_path(ctxt);
struct etm_event_data *event_data;
- struct coresight_path *path;
if (mode & PERF_EF_PAUSE)
- return etm_event_pause(event, csdev, ctxt);
+ return etm_event_pause(path, event, ctxt);
/*
* If we still have access to the event_data via handle,
@@ -647,9 +683,9 @@ static void etm_event_stop(struct perf_event *event, int mode)
WARN_ON(perf_get_aux(handle) != ctxt->event_data))
return;
- event_data = ctxt->event_data;
+ event_data = READ_ONCE(ctxt->event_data);
/* Clear the event_data as this ETM is stopping the trace. */
- ctxt->event_data = NULL;
+ WRITE_ONCE(ctxt->event_data, NULL);
if (event->hw.state == PERF_HES_STOPPED)
return;
@@ -671,19 +707,13 @@ static void etm_event_stop(struct perf_event *event, int mode)
return;
}
- if (!csdev)
- return;
-
- path = etm_event_cpu_path(event_data, cpu);
- if (!path)
- return;
-
+ source = coresight_get_source(path);
sink = coresight_get_sink(path);
- if (!sink)
+ if (!source || !sink)
return;
/* stop tracer */
- coresight_disable_source(csdev, event);
+ coresight_disable_source(source, event);
/* tell the core */
event->hw.state = PERF_HES_STOPPED;
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 1ea882dffd703b2873e41b4ce0c2564d2ce9bbad..a0cd1a0ad2a182f762cc9721fd93725f2d4ef688 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -248,6 +248,7 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
struct coresight_device *coresight_get_percpu_sink(int cpu);
+struct coresight_device *coresight_get_source(struct coresight_path *path);
void coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_pause_source(struct coresight_device *csdev);
int coresight_resume_source(struct coresight_device *csdev);
--
2.34.1
^ permalink raw reply related
* [PATCH v12 08/28] coresight: Take a reference on csdev
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
coresight_get_ref() currently pins the provider module and takes a
reference on the parent device, but it does not pin &csdev->dev. Take a
reference on &csdev->dev when grabbing a CoreSight device and drop it in
coresight_put_ref().
Reorder the sequence to follow child-to-parent dependencies: first take
a reference on csdev, then on the parent device and grab the driver
module. Once the data and module are pinned, take a PM runtime
reference to power on the hardware.
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 36 +++++++++++++++++++++-------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 5519d323f21f38d719b9030c7d77a9a61948ba1d..a3a03356c4e3729c530cd274f071500c4ae8d490 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -651,15 +651,29 @@ struct coresight_device *coresight_get_sink_by_id(u32 id)
*/
static bool coresight_get_ref(struct coresight_device *csdev)
{
- struct device *dev = csdev->dev.parent;
+ struct device *dev = &csdev->dev;
+ struct device *parent = csdev->dev.parent;
+ struct device_driver *drv;
- /* Make sure the driver can't be removed */
- if (!try_module_get(dev->driver->owner))
- return false;
- /* Make sure the device can't go away */
+ /* Make sure csdev can't go away */
get_device(dev);
- pm_runtime_get_sync(dev);
+
+ /* Make sure parent device can't go away */
+ get_device(parent);
+
+ /* Make sure the driver can't be removed */
+ drv = parent->driver;
+ if (!drv || !try_module_get(drv->owner))
+ goto err_module;
+
+ /* Make sure the device is powered on */
+ pm_runtime_get_sync(parent);
return true;
+
+err_module:
+ put_device(parent);
+ put_device(dev);
+ return false;
}
/**
@@ -670,11 +684,15 @@ static bool coresight_get_ref(struct coresight_device *csdev)
*/
static void coresight_put_ref(struct coresight_device *csdev)
{
- struct device *dev = csdev->dev.parent;
+ struct device *dev = &csdev->dev;
+ struct device *parent = csdev->dev.parent;
+ struct device_driver *drv = parent->driver;
- pm_runtime_put(dev);
+ pm_runtime_put(parent);
+ if (drv)
+ module_put(drv->owner);
+ put_device(parent);
put_device(dev);
- module_put(dev->driver->owner);
}
/*
--
2.34.1
^ permalink raw reply related
* [PATCH v12 11/28] coresight: Register CPU PM notifier in core layer
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
The current implementation only saves and restores the context for ETM
sources while ignoring the context of links. However, if funnels or
replicators on a linked path resides in a CPU or cluster power domain,
the hardware context for the link will be lost after resuming from low
power states.
To support context management for links during CPU low power modes, a
better way is to implement CPU PM callbacks in the Arm CoreSight core
layer. As the core layer has sufficient information for linked paths,
from tracers to links, which can be used for power management.
As a first step, this patch registers CPU PM notifier in the core layer.
If a source device provides callbacks for saving and restoring context,
these callbacks will be invoked in CPU suspend and resume.
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 93 ++++++++++++++++++++++++++++
include/linux/coresight.h | 2 +
2 files changed, 95 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index b8d9a457458018a776c716341966824eb5080143..c525ecaad7efc40150d2d8ef3a6decddb68aff69 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -6,6 +6,7 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/build_bug.h>
+#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -1738,6 +1739,91 @@ static void coresight_release_device_list(void)
}
}
+static struct coresight_device *coresight_cpu_get_active_source(void)
+{
+ struct coresight_device *source;
+ bool is_active = false;
+
+ source = coresight_get_percpu_source_ref(smp_processor_id());
+ if (!source)
+ return NULL;
+
+ if (coresight_get_mode(source) != CS_MODE_DISABLED)
+ is_active = true;
+
+ coresight_put_percpu_source_ref(source);
+
+ /*
+ * It is expected to run in atomic context, so it cannot be preempted
+ * to disable the source. Here returns the active source pointer
+ * without concern that its state may change. Since the build path has
+ * taken a reference on the component, the source can be safely used
+ * by the caller.
+ */
+ return is_active ? source : NULL;
+}
+
+static int coresight_pm_is_needed(struct coresight_device *csdev)
+{
+ if (!csdev)
+ return 0;
+
+ /* pm_save_disable() and pm_restore_enable() must be paired */
+ if (coresight_ops(csdev)->pm_save_disable &&
+ coresight_ops(csdev)->pm_restore_enable)
+ return 1;
+
+ return 0;
+}
+
+static int coresight_pm_device_save(struct coresight_device *csdev)
+{
+ return coresight_ops(csdev)->pm_save_disable(csdev);
+}
+
+static void coresight_pm_device_restore(struct coresight_device *csdev)
+{
+ coresight_ops(csdev)->pm_restore_enable(csdev);
+}
+
+static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
+ void *v)
+{
+ struct coresight_device *csdev = coresight_cpu_get_active_source();
+
+ if (!coresight_pm_is_needed(csdev))
+ return NOTIFY_DONE;
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (coresight_pm_device_save(csdev))
+ return NOTIFY_BAD;
+ break;
+ case CPU_PM_EXIT:
+ case CPU_PM_ENTER_FAILED:
+ coresight_pm_device_restore(csdev);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block coresight_cpu_pm_nb = {
+ .notifier_call = coresight_cpu_pm_notify,
+};
+
+static int __init coresight_pm_setup(void)
+{
+ return cpu_pm_register_notifier(&coresight_cpu_pm_nb);
+}
+
+static void coresight_pm_cleanup(void)
+{
+ cpu_pm_unregister_notifier(&coresight_cpu_pm_nb);
+}
+
const struct bus_type coresight_bustype = {
.name = "coresight",
};
@@ -1792,9 +1878,15 @@ static int __init coresight_init(void)
/* initialise the coresight syscfg API */
ret = cscfg_init();
+ if (ret)
+ goto exit_notifier;
+
+ ret = coresight_pm_setup();
if (!ret)
return 0;
+ cscfg_exit();
+exit_notifier:
atomic_notifier_chain_unregister(&panic_notifier_list,
&coresight_notifier);
exit_perf:
@@ -1806,6 +1898,7 @@ static int __init coresight_init(void)
static void __exit coresight_exit(void)
{
+ coresight_pm_cleanup();
cscfg_exit();
atomic_notifier_chain_unregister(&panic_notifier_list,
&coresight_notifier);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index e9c20ceb9016fa3db256b8c1147c1fd2027b7b0d..5f9d7ea9f5941ab01eb6a084ca558a9417c7727f 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -438,6 +438,8 @@ struct coresight_ops_panic {
struct coresight_ops {
int (*trace_id)(struct coresight_device *csdev, enum cs_mode mode,
struct coresight_device *sink);
+ int (*pm_save_disable)(struct coresight_device *csdev);
+ void (*pm_restore_enable)(struct coresight_device *csdev);
const struct coresight_ops_sink *sink_ops;
const struct coresight_ops_link *link_ops;
const struct coresight_ops_source *source_ops;
--
2.34.1
^ permalink raw reply related
* [PATCH v12 09/28] coresight: Move per-CPU source pointer to core layer
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Move the per-CPU source pointer from ETM perf to the core layer, as this
will be used for not only perf session and also for CPU PM notifiers.
Provides coresight_{set|clear|get}_percpu_source() helpers to access the
per-CPU source pointer. Add a raw spinlock to protect exclusive access.
Device registration and unregistration call the set and clear helpers
for init and teardown the pointers.
Update callers to invoke coresight_get_percpu_source() for retrieving
the pointer.
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 38 ++++++++++++++++++++++++
drivers/hwtracing/coresight/coresight-etm-perf.c | 14 +++------
drivers/hwtracing/coresight/coresight-priv.h | 1 +
3 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index a3a03356c4e3729c530cd274f071500c4ae8d490..c2a290903cbf416651e7ec7057a1ee0b36bb6a8b 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -35,6 +35,9 @@
DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
+static DEFINE_RAW_SPINLOCK(coresight_dev_lock);
+static DEFINE_PER_CPU(struct coresight_device *, csdev_source);
+
/**
* struct coresight_node - elements of a path, from source to sink
* @csdev: Address of an element.
@@ -82,6 +85,39 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
}
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
+static void coresight_set_percpu_source(struct coresight_device *csdev)
+{
+ if (!csdev || !coresight_is_percpu_source(csdev))
+ return;
+
+ guard(raw_spinlock_irqsave)(&coresight_dev_lock);
+
+ /* Expect no device to be set yet */
+ WARN_ON(per_cpu(csdev_source, csdev->cpu));
+ per_cpu(csdev_source, csdev->cpu) = csdev;
+}
+
+static void coresight_clear_percpu_source(struct coresight_device *csdev)
+{
+ if (!csdev || !coresight_is_percpu_source(csdev))
+ return;
+
+ guard(raw_spinlock_irqsave)(&coresight_dev_lock);
+
+ /* The per-CPU pointer should contain the same csdev */
+ WARN_ON(per_cpu(csdev_source, csdev->cpu) != csdev);
+ per_cpu(csdev_source, csdev->cpu) = NULL;
+}
+
+struct coresight_device *coresight_get_percpu_source(int cpu)
+{
+ if (WARN_ON(cpu < 0))
+ return NULL;
+
+ guard(raw_spinlock_irqsave)(&coresight_dev_lock);
+ return per_cpu(csdev_source, cpu);
+}
+
struct coresight_device *coresight_get_source(struct coresight_path *path)
{
struct coresight_device *csdev;
@@ -1449,6 +1485,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
if (ret)
goto out_unlock;
+ coresight_set_percpu_source(csdev);
mutex_unlock(&coresight_mutex);
if (cti_assoc_ops && cti_assoc_ops->add)
@@ -1478,6 +1515,7 @@ void coresight_unregister(struct coresight_device *csdev)
cti_assoc_ops->remove(csdev);
mutex_lock(&coresight_mutex);
+ coresight_clear_percpu_source(csdev);
etm_perf_del_symlink_sink(csdev);
coresight_remove_conns(csdev);
coresight_clear_default_sink(csdev);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 206a983239a0b1983b2b78c5e78a5fd988d308ce..b9891c8ac078d99fc2180c858dc56b1e7dbec2d9 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -49,7 +49,6 @@ struct etm_ctxt {
};
static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt);
-static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
GEN_PMU_FORMAT_ATTR(cycacc);
GEN_PMU_FORMAT_ATTR(timestamp);
@@ -385,7 +384,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
struct coresight_path *path;
struct coresight_device *csdev;
- csdev = per_cpu(csdev_src, cpu);
+ csdev = coresight_get_percpu_source(cpu);
/*
* If there is no ETM associated with this CPU clear it from
* the mask and continue with the rest. If ever we try to trace
@@ -863,17 +862,12 @@ int etm_perf_symlink(struct coresight_device *csdev, bool link)
if (!etm_perf_up)
return -EPROBE_DEFER;
- if (link) {
+ if (link)
ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry);
- if (ret)
- return ret;
- per_cpu(csdev_src, cpu) = csdev;
- } else {
+ else
sysfs_remove_link(&pmu_dev->kobj, entry);
- per_cpu(csdev_src, cpu) = NULL;
- }
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(etm_perf_symlink);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index a0cd1a0ad2a182f762cc9721fd93725f2d4ef688..7ce79fa36232bb1b0af768423777bab27cacee95 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -249,6 +249,7 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
struct coresight_device *coresight_get_percpu_sink(int cpu);
struct coresight_device *coresight_get_source(struct coresight_path *path);
+struct coresight_device *coresight_get_percpu_source(int cpu);
void coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_pause_source(struct coresight_device *csdev);
int coresight_resume_source(struct coresight_device *csdev);
--
2.34.1
^ permalink raw reply related
* [PATCH v12 14/28] coresight: syscfg: Use IRQ-safe spinlock to protect active variables
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
cscfg_config_sysfs_get_active_cfg() will be used from idle flows, while
sleeping locks are not allowed. Introduce sysfs_store_lock to replace
the mutex to protect sysfs_active_config and sysfs_active_preset
accesses, with IRQ-safe locking to avoid lockdep complaint.
Refactor cscfg_config_sysfs_activate() to use spinlock for
sysfs_active_config and activate/deactivate config.
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-syscfg.c | 38 ++++++++++++++------------
drivers/hwtracing/coresight/coresight-syscfg.h | 2 ++
2 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
index d7f5037953d6ba7fb7f83a8012a1abc5ffd0a147..2bfdd7b45e49c8bec88428545069577431083bda 100644
--- a/drivers/hwtracing/coresight/coresight-syscfg.c
+++ b/drivers/hwtracing/coresight/coresight-syscfg.c
@@ -953,39 +953,41 @@ int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool acti
unsigned long cfg_hash;
int err = 0;
- mutex_lock(&cscfg_mutex);
+ guard(mutex)(&cscfg_mutex);
cfg_hash = (unsigned long)config_desc->event_ea->var;
if (activate) {
/* cannot be a current active value to activate this */
- if (cscfg_mgr->sysfs_active_config) {
- err = -EBUSY;
- goto exit_unlock;
- }
- err = _cscfg_activate_config(cfg_hash);
- if (!err)
+ if (cscfg_mgr->sysfs_active_config)
+ return -EBUSY;
+
+ scoped_guard(raw_spinlock_irqsave, &cscfg_mgr->sysfs_store_lock) {
+ err = _cscfg_activate_config(cfg_hash);
+ if (err)
+ return err;
+
cscfg_mgr->sysfs_active_config = cfg_hash;
+ }
} else {
- /* disable if matching current value */
- if (cscfg_mgr->sysfs_active_config == cfg_hash) {
+ if (cscfg_mgr->sysfs_active_config != cfg_hash)
+ return -EINVAL;
+
+ scoped_guard(raw_spinlock_irqsave, &cscfg_mgr->sysfs_store_lock) {
+ /* disable if matching current value */
_cscfg_deactivate_config(cfg_hash);
cscfg_mgr->sysfs_active_config = 0;
- } else
- err = -EINVAL;
+ }
}
-exit_unlock:
- mutex_unlock(&cscfg_mutex);
- return err;
+ return 0;
}
/* set the sysfs preset value */
void cscfg_config_sysfs_set_preset(int preset)
{
- mutex_lock(&cscfg_mutex);
+ guard(raw_spinlock_irqsave)(&cscfg_mgr->sysfs_store_lock);
cscfg_mgr->sysfs_active_preset = preset;
- mutex_unlock(&cscfg_mutex);
}
/*
@@ -994,10 +996,9 @@ void cscfg_config_sysfs_set_preset(int preset)
*/
void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset)
{
- mutex_lock(&cscfg_mutex);
+ guard(raw_spinlock_irqsave)(&cscfg_mgr->sysfs_store_lock);
*preset = cscfg_mgr->sysfs_active_preset;
*cfg_hash = cscfg_mgr->sysfs_active_config;
- mutex_unlock(&cscfg_mutex);
}
EXPORT_SYMBOL_GPL(cscfg_config_sysfs_get_active_cfg);
@@ -1201,6 +1202,7 @@ static int cscfg_create_device(void)
INIT_LIST_HEAD(&cscfg_mgr->load_order_list);
atomic_set(&cscfg_mgr->sys_active_cnt, 0);
cscfg_mgr->load_state = CSCFG_NONE;
+ raw_spin_lock_init(&cscfg_mgr->sysfs_store_lock);
/* setup the device */
dev = cscfg_device();
diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h
index 66e2db890d8203853a0c3c907b48aa66dd8014e6..658e93c3705f1cb3ba3523d0bc27ac704697dd70 100644
--- a/drivers/hwtracing/coresight/coresight-syscfg.h
+++ b/drivers/hwtracing/coresight/coresight-syscfg.h
@@ -42,6 +42,7 @@ enum cscfg_load_ops {
* @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs.
* @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs.
* @load_state: A multi-stage load/unload operation is in progress.
+ * @sysfs_store_lock: Exclusive access sysfs stored variables.
*/
struct cscfg_manager {
struct device dev;
@@ -54,6 +55,7 @@ struct cscfg_manager {
u32 sysfs_active_config;
int sysfs_active_preset;
enum cscfg_load_ops load_state;
+ raw_spinlock_t sysfs_store_lock;
};
/* get reference to dev in cscfg_manager */
--
2.34.1
^ permalink raw reply related
* [PATCH v12 10/28] coresight: Take per-CPU source reference during AUX setup
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
etm_setup_aux() fetches the per-CPU source pointer while preparing perf
AUX trace paths. This can race with CoreSight device unregistration, the
ETM device may has been released while the AUX setup still use it,
leading to use-after-free.
Move per-CPU path construction into etm_event_build_path() and use
the coresight_{get|put}_percpu_source_ref() pairs to take and drop
the device references, this ensures the device is safe to access during
path construction.
Update comments accordingly. Document a PREEMPT_RT corner case: the
put_device() may release resources while coresight_dev_lock (a raw
spinlock) is held, and the release may attempt to acquire a spinlock
that becomes sleepable under PREEMPT_RT. This will be fixed in the
future.
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 38 +++++-
drivers/hwtracing/coresight/coresight-etm-perf.c | 157 +++++++++++++----------
drivers/hwtracing/coresight/coresight-priv.h | 3 +-
3 files changed, 129 insertions(+), 69 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index c2a290903cbf416651e7ec7057a1ee0b36bb6a8b..b8d9a457458018a776c716341966824eb5080143 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -109,13 +109,47 @@ static void coresight_clear_percpu_source(struct coresight_device *csdev)
per_cpu(csdev_source, csdev->cpu) = NULL;
}
-struct coresight_device *coresight_get_percpu_source(int cpu)
+struct coresight_device *coresight_get_percpu_source_ref(int cpu)
{
+ struct coresight_device *csdev;
+
if (WARN_ON(cpu < 0))
return NULL;
guard(raw_spinlock_irqsave)(&coresight_dev_lock);
- return per_cpu(csdev_source, cpu);
+
+ csdev = per_cpu(csdev_source, cpu);
+ if (!csdev)
+ return NULL;
+
+ /*
+ * Holding a reference to the csdev->dev ensures that the
+ * coresight_device is live for the caller. The path building
+ * logic can safely either build a path to the sink or fail
+ * if the device is being unregistered (if there was a race).
+ * The caller can skip the "source" device, if no path could
+ * be built.
+ */
+ get_device(&csdev->dev);
+
+ return csdev;
+}
+
+void coresight_put_percpu_source_ref(struct coresight_device *csdev)
+{
+ if (!csdev || !coresight_is_percpu_source(csdev))
+ return;
+
+ guard(raw_spinlock_irqsave)(&coresight_dev_lock);
+
+ /*
+ * TODO: coresight_device_release() is invoked to release resources when
+ * the device's refcount reaches zero. It then calls free_percpu(),
+ * which acquires pcpu_lock — a sleepable lock when PREEMPT_RT is
+ * enabled. Since the raw spinlock coresight_dev_lock is held, this can
+ * lead to a potential "scheduling while atomic" issue.
+ */
+ put_device(&csdev->dev);
}
struct coresight_device *coresight_get_source(struct coresight_path *path)
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index b9891c8ac078d99fc2180c858dc56b1e7dbec2d9..e416d2717a99aa3b573f8e53adec19518851d635 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -343,6 +343,85 @@ static struct coresight_path *etm_event_get_ctxt_path(struct etm_ctxt *ctxt)
return path;
}
+static struct coresight_path *
+etm_event_build_path(struct perf_event *event, int cpu,
+ struct coresight_device *user_sink,
+ struct coresight_device *match_sink)
+{
+ struct coresight_path *path = NULL;
+ struct coresight_device *source, *sink;
+
+ source = coresight_get_percpu_source_ref(cpu);
+
+ /*
+ * If there is no ETM associated with this CPU or ever we try to trace
+ * on this CPU, we handle it accordingly.
+ */
+ if (!source)
+ return NULL;
+
+ /*
+ * If AUX pause feature is enabled but the ETM driver does not
+ * support the operations, skip for this source.
+ */
+ if (event->attr.aux_start_paused &&
+ (!source_ops(source)->pause_perf ||
+ !source_ops(source)->resume_perf)) {
+ dev_err_once(&source->dev, "AUX pause is not supported.\n");
+ goto out;
+ }
+
+ /* If sink has been specified by user, directly use it */
+ if (user_sink) {
+ sink = user_sink;
+ } else {
+ /*
+ * No sink provided - look for a default sink for all the ETMs,
+ * where this event can be scheduled.
+ *
+ * We allocate the sink specific buffers only once for this
+ * event. If the ETMs have different default sink devices, we
+ * can only use a single "type" of sink as the event can carry
+ * only one sink specific buffer. Thus we have to make sure
+ * that the sinks are of the same type and driven by the same
+ * driver, as the one we allocate the buffer for. We don't
+ * trace on a CPU if the sink is not compatible.
+ */
+
+ /* Find the default sink for this ETM */
+ sink = coresight_find_default_sink(source);
+ if (!sink)
+ goto out;
+
+ /* Check if this sink compatible with the last sink */
+ if (match_sink && !sinks_compatible(match_sink, sink))
+ goto out;
+ }
+
+ /*
+ * Building a path doesn't enable it, it simply builds a
+ * list of devices from source to sink that can be
+ * referenced later when the path is actually needed.
+ */
+ path = coresight_build_path(source, sink);
+ if (IS_ERR(path))
+ goto out;
+
+ /* ensure we can allocate a trace ID for this CPU */
+ coresight_path_assign_trace_id(path, CS_MODE_PERF);
+ if (!IS_VALID_CS_TRACE_ID(path->trace_id)) {
+ coresight_release_path(path);
+ path = NULL;
+ goto out;
+ }
+
+ coresight_trace_id_perf_start(&sink->perf_sink_id_map);
+
+out:
+ coresight_put_percpu_source_ref(source);
+ return IS_ERR_OR_NULL(path) ? NULL : path;
+}
+
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
@@ -350,7 +429,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
int cpu = event->cpu;
cpumask_t *mask;
struct coresight_device *sink = NULL;
- struct coresight_device *user_sink = NULL, *last_sink = NULL;
+ struct coresight_device *user_sink = NULL;
struct etm_event_data *event_data = NULL;
event_data = alloc_event_data(cpu);
@@ -382,80 +461,26 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
*/
for_each_cpu(cpu, mask) {
struct coresight_path *path;
- struct coresight_device *csdev;
- csdev = coresight_get_percpu_source(cpu);
- /*
- * If there is no ETM associated with this CPU clear it from
- * the mask and continue with the rest. If ever we try to trace
- * on this CPU, we handle it accordingly.
- */
- if (!csdev) {
+ path = etm_event_build_path(event, cpu, user_sink, sink);
+ if (!path) {
+ /*
+ * Failed to create a path for the CPU, clear it from
+ * the mask and continue to next one.
+ */
cpumask_clear_cpu(cpu, mask);
continue;
}
- /*
- * If AUX pause feature is enabled but the ETM driver does not
- * support the operations, clear this CPU from the mask and
- * continue to next one.
- */
- if (event->attr.aux_start_paused &&
- (!source_ops(csdev)->pause_perf || !source_ops(csdev)->resume_perf)) {
- dev_err_once(&csdev->dev, "AUX pause is not supported.\n");
- cpumask_clear_cpu(cpu, mask);
- continue;
- }
/*
- * No sink provided - look for a default sink for all the ETMs,
- * where this event can be scheduled.
- * We allocate the sink specific buffers only once for this
- * event. If the ETMs have different default sink devices, we
- * can only use a single "type" of sink as the event can carry
- * only one sink specific buffer. Thus we have to make sure
- * that the sinks are of the same type and driven by the same
- * driver, as the one we allocate the buffer for. As such
- * we choose the first sink and check if the remaining ETMs
- * have a compatible default sink. We don't trace on a CPU
- * if the sink is not compatible.
- */
- if (!user_sink) {
- /* Find the default sink for this ETM */
- sink = coresight_find_default_sink(csdev);
- if (!sink) {
- cpumask_clear_cpu(cpu, mask);
- continue;
- }
-
- /* Check if this sink compatible with the last sink */
- if (last_sink && !sinks_compatible(last_sink, sink)) {
- cpumask_clear_cpu(cpu, mask);
- continue;
- }
- last_sink = sink;
- }
-
- /*
- * Building a path doesn't enable it, it simply builds a
- * list of devices from source to sink that can be
- * referenced later when the path is actually needed.
+ * The first found sink is saved here and passed to
+ * etm_event_build_path() to check whether the remaining ETMs
+ * have a compatible default sink.
*/
- path = coresight_build_path(csdev, sink);
- if (IS_ERR(path)) {
- cpumask_clear_cpu(cpu, mask);
- continue;
- }
-
- /* ensure we can allocate a trace ID for this CPU */
- coresight_path_assign_trace_id(path, CS_MODE_PERF);
- if (!IS_VALID_CS_TRACE_ID(path->trace_id)) {
- cpumask_clear_cpu(cpu, mask);
- coresight_release_path(path);
- continue;
- }
+ if (!user_sink && !sink)
+ sink = coresight_get_sink(path);
- coresight_trace_id_perf_start(&sink->perf_sink_id_map);
*etm_event_cpu_path_ptr(event_data, cpu) = path;
}
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 7ce79fa36232bb1b0af768423777bab27cacee95..a1aab67e23db7fdea5139100312b3eb7cd31df51 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -249,7 +249,8 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
struct coresight_device *coresight_get_percpu_sink(int cpu);
struct coresight_device *coresight_get_source(struct coresight_path *path);
-struct coresight_device *coresight_get_percpu_source(int cpu);
+struct coresight_device *coresight_get_percpu_source_ref(int cpu);
+void coresight_put_percpu_source_ref(struct coresight_device *csdev);
void coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_pause_source(struct coresight_device *csdev);
int coresight_resume_source(struct coresight_device *csdev);
--
2.34.1
^ permalink raw reply related
* [PATCH v12 15/28] coresight: Disable source helpers in coresight_disable_path()
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
coresight_enable_path() enables helpers attached to every device in
the path, including those bound to the source. However,
coresight_disable_path() skips the source node, so source helpers had
to be disabled separately in coresight_disable_source().
Move source helper disabling into coresight_disable_path() instead.
Make coresight_disable_path_from() start from the passed node nd, so
it can also disable helpers on the source. Update the comments
accordingly.
As coresight_disable_path_from() now changes its semantics from
"start beyond nd" to "start from nd", update the failure handling in
coresight_enable_path(). If enabling a node fails, iterate to the
previous node (the last successfully enabled one) and pass it to
coresight_disable_path_from() for rollback.
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 27 ++++++++-------------------
1 file changed, 8 insertions(+), 19 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index c525ecaad7efc40150d2d8ef3a6decddb68aff69..856036ad838b3508bf39a2ef40e68673925bca5b 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -456,19 +456,12 @@ static void coresight_disable_helpers(struct coresight_device *csdev,
}
/*
- * Helper function to call source_ops(csdev)->disable and also disable the
- * helpers.
- *
- * There is an imbalance between coresight_enable_path() and
- * coresight_disable_path(). Enabling also enables the source's helpers as part
- * of the path, but disabling always skips the first item in the path (which is
- * the source), so sources and their helpers don't get disabled as part of that
- * function and we need the extra step here.
+ * coresight_disable_source() only disables the source, but do nothing for
+ * the associated helpers, which are controlled as part of the path.
*/
void coresight_disable_source(struct coresight_device *csdev, void *data)
{
source_ops(csdev)->disable(csdev, data);
- coresight_disable_helpers(csdev, NULL);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
@@ -495,9 +488,9 @@ int coresight_resume_source(struct coresight_device *csdev)
EXPORT_SYMBOL_GPL(coresight_resume_source);
/*
- * coresight_disable_path_from : Disable components in the given path beyond
- * @nd in the list. If @nd is NULL, all the components, except the SOURCE are
- * disabled.
+ * coresight_disable_path_from : Disable components in the given path starting
+ * from @nd in the list. If @nd is NULL, all the components, except the SOURCE
+ * are disabled.
*/
static void coresight_disable_path_from(struct coresight_path *path,
struct coresight_node *nd)
@@ -508,7 +501,7 @@ static void coresight_disable_path_from(struct coresight_path *path,
if (!nd)
nd = list_first_entry(&path->path_list, struct coresight_node, link);
- list_for_each_entry_continue(nd, &path->path_list, link) {
+ list_for_each_entry_from(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@@ -528,12 +521,6 @@ static void coresight_disable_path_from(struct coresight_path *path,
coresight_disable_sink(csdev);
break;
case CORESIGHT_DEV_TYPE_SOURCE:
- /*
- * We skip the first node in the path assuming that it
- * is the source. So we don't expect a source device in
- * the middle of a path.
- */
- WARN_ON(1);
break;
case CORESIGHT_DEV_TYPE_LINK:
parent = list_prev_entry(nd, link)->csdev;
@@ -648,6 +635,8 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
err_disable_helpers:
coresight_disable_helpers(csdev, path);
err_disable_path:
+ /* Fetch the previous node, the last successfully enabled one */
+ nd = list_next_entry(nd, link);
coresight_disable_path_from(path, nd);
goto out;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v12 13/28] coresight: etm4x: Remove redundant checks in PM save and restore
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan, Mike Leach
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
ETMv4 driver save/restore callbacks still re-check conditions that are
already validated by the core layer before the callbacks are invoked.
Remove the duplicated checks and fold the two-level functions into
direct callback implementations. The obsolete WARN_ON() checks are
removed as well.
Reviewed-by: Mike Leach <mike.leach@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-etm4x-core.c | 41 +++-------------------
1 file changed, 4 insertions(+), 37 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 5cd70f0f3710a5ec6406a89c034d30230f782b13..fef1270439e9d770fc0459e9ab8cab7cb40827ee 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -1853,17 +1853,14 @@ static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata)
return !!drvdata->save_state;
}
-static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
+static int etm4_cpu_save(struct coresight_device *csdev)
{
int i, ret = 0;
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_save_state *state;
- struct coresight_device *csdev = drvdata->csdev;
struct csdev_access *csa;
struct device *etm_dev;
- if (WARN_ON(!csdev))
- return -ENODEV;
-
etm_dev = &csdev->dev;
csa = &csdev->access;
@@ -1995,32 +1992,13 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
return ret;
}
-static int etm4_cpu_save(struct coresight_device *csdev)
-{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- int ret = 0;
-
- if (!etm4_pm_save_needed(drvdata))
- return 0;
-
- /*
- * Save and restore the ETM Trace registers only if
- * the ETM is active.
- */
- if (coresight_get_mode(drvdata->csdev))
- ret = __etm4_cpu_save(drvdata);
- return ret;
-}
-
-static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
+static void etm4_cpu_restore(struct coresight_device *csdev)
{
int i;
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_save_state *state = drvdata->save_state;
struct csdev_access *csa = &drvdata->csdev->access;
- if (WARN_ON(!drvdata->csdev))
- return;
-
etm4_cs_unlock(drvdata, csa);
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
@@ -2113,17 +2091,6 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4_cs_lock(drvdata, csa);
}
-static void etm4_cpu_restore(struct coresight_device *csdev)
-{
- struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
-
- if (!etm4_pm_save_needed(drvdata))
- return;
-
- if (coresight_get_mode(drvdata->csdev))
- __etm4_cpu_restore(drvdata);
-}
-
static const struct coresight_ops etm4_cs_ops = {
.trace_id = coresight_etm_get_trace_id,
.source_ops = &etm4_source_ops,
--
2.34.1
^ permalink raw reply related
* [PATCH v12 17/28] coresight: Use helpers to fetch first and last nodes
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Replace open code with coresight_path_first_node() and
coresight_path_last_node() for fetching the nodes.
Check that the node is not NULL before accessing csdev field.
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 0c82869b21cb734ab3ae96123c53c78071281cd6..2effbde0133b79c81ba72dd9b1ed6510523370d1 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -174,11 +174,16 @@ void coresight_put_percpu_source_ref(struct coresight_device *csdev)
struct coresight_device *coresight_get_source(struct coresight_path *path)
{
struct coresight_device *csdev;
+ struct coresight_node *nd;
if (!path)
return NULL;
- csdev = list_first_entry(&path->path_list, struct coresight_node, link)->csdev;
+ nd = coresight_path_first_node(path);
+ if (!nd)
+ return NULL;
+
+ csdev = nd->csdev;
if (!coresight_is_device_source(csdev))
return NULL;
@@ -711,11 +716,16 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
struct coresight_device *coresight_get_sink(struct coresight_path *path)
{
struct coresight_device *csdev;
+ struct coresight_node *nd;
if (!path)
return NULL;
- csdev = list_last_entry(&path->path_list, struct coresight_node, link)->csdev;
+ nd = coresight_path_last_node(path);
+ if (!nd)
+ return NULL;
+
+ csdev = nd->csdev;
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
return NULL;
--
2.34.1
^ permalink raw reply related
* [PATCH v12 18/28] coresight: Introduce coresight_enable_source() helper
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Introduce the coresight_enable_source() helper for enabling source
device.
Add validation to ensure the device is a source before proceeding with
further operations.
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 18 ++++++++++++++++--
drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +-
drivers/hwtracing/coresight/coresight-priv.h | 3 +++
drivers/hwtracing/coresight/coresight-sysfs.c | 2 +-
4 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 2effbde0133b79c81ba72dd9b1ed6510523370d1..ff882769cab2bb20bc2cb768e86f111f8a09cc8d 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -479,11 +479,25 @@ static void coresight_disable_helpers(struct coresight_device *csdev,
}
/*
- * coresight_disable_source() only disables the source, but do nothing for
- * the associated helpers, which are controlled as part of the path.
+ * coresight_enable_source() and coresight_disable_source() only enable and
+ * disable the source, but do nothing for the associated helpers, which are
+ * controlled as part of the path.
*/
+int coresight_enable_source(struct coresight_device *csdev,
+ struct perf_event *event, enum cs_mode mode,
+ struct coresight_path *path)
+{
+ if (!coresight_is_device_source(csdev))
+ return -EINVAL;
+
+ return source_ops(csdev)->enable(csdev, event, mode, path);
+}
+
void coresight_disable_source(struct coresight_device *csdev, void *data)
{
+ if (!coresight_is_device_source(csdev))
+ return;
+
source_ops(csdev)->disable(csdev, data);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index e416d2717a99aa3b573f8e53adec19518851d635..7919b0c225f7704194fb736901f51dd5fa21521d 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -593,7 +593,7 @@ static void etm_event_start(struct perf_event *event, int flags)
goto fail_end_stop;
/* Finally enable the tracer */
- if (source_ops(source)->enable(source, event, CS_MODE_PERF, path))
+ if (coresight_enable_source(source, event, CS_MODE_PERF, path))
goto fail_disable_path;
/*
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index a1aab67e23db7fdea5139100312b3eb7cd31df51..456c53ba5d4c87bf33490383eb59b6a1710bbdf7 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -251,6 +251,9 @@ struct coresight_device *coresight_get_percpu_sink(int cpu);
struct coresight_device *coresight_get_source(struct coresight_path *path);
struct coresight_device *coresight_get_percpu_source_ref(int cpu);
void coresight_put_percpu_source_ref(struct coresight_device *csdev);
+int coresight_enable_source(struct coresight_device *csdev,
+ struct perf_event *event, enum cs_mode mode,
+ struct coresight_path *path);
void coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_pause_source(struct coresight_device *csdev);
int coresight_resume_source(struct coresight_device *csdev);
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index b24b0d08a462cbe992cf26e03b47693b2cf5f8e9..7ef3f13ebac55b920458b6cd202bdb533796ba2d 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -66,7 +66,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev,
*/
lockdep_assert_held(&coresight_mutex);
if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
- ret = source_ops(csdev)->enable(csdev, NULL, mode, path);
+ ret = coresight_enable_source(csdev, NULL, mode, path);
if (ret)
return ret;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v12 16/28] coresight: Control path with range
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
CPU PM notifiers need to control only part of a path instead of always
operating on the full path.
Add internal enable and disable helpers that take an inclusive node
range [from, to], validate that the requested nodes are ordered before
using them.
Update the existed coresight_{enable|disable}_path() interfaces as
full-path wrappers by passing the first and last path nodes. The helpers
coresight_path_{first|last}_node() are provided for conveniently
fetching the first and last nodes on the path.
In coresight_enable_path_from_to(), if a failure occurs at the last node
in the range, no device is actually enabled in this case, bail out
directly.
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 95 ++++++++++++++++++++++++----
1 file changed, 81 insertions(+), 14 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 856036ad838b3508bf39a2ef40e68673925bca5b..0c82869b21cb734ab3ae96123c53c78071281cd6 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -62,6 +62,24 @@ static LIST_HEAD(coresight_dev_idx_list);
static const struct cti_assoc_op *cti_assoc_ops;
+static struct coresight_node *
+coresight_path_first_node(struct coresight_path *path)
+{
+ if (list_empty(&path->path_list))
+ return NULL;
+
+ return list_first_entry(&path->path_list, struct coresight_node, link);
+}
+
+static struct coresight_node *
+coresight_path_last_node(struct coresight_path *path)
+{
+ if (list_empty(&path->path_list))
+ return NULL;
+
+ return list_last_entry(&path->path_list, struct coresight_node, link);
+}
+
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@@ -488,19 +506,41 @@ int coresight_resume_source(struct coresight_device *csdev)
EXPORT_SYMBOL_GPL(coresight_resume_source);
/*
- * coresight_disable_path_from : Disable components in the given path starting
- * from @nd in the list. If @nd is NULL, all the components, except the SOURCE
- * are disabled.
+ * Callers must fetch nodes from the path and pass @from and @to to the path
+ * enable/disable functions. Walk the path from @from to locate @to. If @to
+ * is found, it indicates @from and @to are in order. Otherwise, they are out
+ * of order.
*/
-static void coresight_disable_path_from(struct coresight_path *path,
- struct coresight_node *nd)
+static bool coresight_path_nodes_in_order(struct coresight_path *path,
+ struct coresight_node *from,
+ struct coresight_node *to)
+{
+ struct coresight_node *nd;
+
+ if (WARN_ON_ONCE(!from || !to))
+ return false;
+
+ nd = from;
+ list_for_each_entry_from(nd, &path->path_list, link) {
+ if (nd == to)
+ return true;
+ }
+
+ return false;
+}
+
+static void coresight_disable_path_from_to(struct coresight_path *path,
+ struct coresight_node *from,
+ struct coresight_node *to)
{
u32 type;
struct coresight_device *csdev, *parent, *child;
+ struct coresight_node *nd;
- if (!nd)
- nd = list_first_entry(&path->path_list, struct coresight_node, link);
+ if (!coresight_path_nodes_in_order(path, from, to))
+ return;
+ nd = from;
list_for_each_entry_from(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@@ -534,12 +574,18 @@ static void coresight_disable_path_from(struct coresight_path *path,
/* Disable all helpers adjacent along the path last */
coresight_disable_helpers(csdev, path);
+
+ /* Iterate up to and including @to */
+ if (nd == to)
+ break;
}
}
void coresight_disable_path(struct coresight_path *path)
{
- coresight_disable_path_from(path, NULL);
+ coresight_disable_path_from_to(path,
+ coresight_path_first_node(path),
+ coresight_path_last_node(path));
}
EXPORT_SYMBOL_GPL(coresight_disable_path);
@@ -572,16 +618,21 @@ static int coresight_enable_helpers(struct coresight_device *csdev,
return ret;
}
-int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
+static int coresight_enable_path_from_to(struct coresight_path *path,
+ enum cs_mode mode,
+ struct coresight_node *from,
+ struct coresight_node *to)
{
int ret = 0;
u32 type;
struct coresight_node *nd;
struct coresight_device *csdev, *parent, *child;
- struct coresight_device *source;
- source = coresight_get_source(path);
- list_for_each_entry_reverse(nd, &path->path_list, link) {
+ if (!coresight_path_nodes_in_order(path, from, to))
+ return -EINVAL;
+
+ nd = to;
+ list_for_each_entry_from_reverse(nd, &path->path_list, link) {
csdev = nd->csdev;
type = csdev->type;
@@ -620,7 +671,8 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
case CORESIGHT_DEV_TYPE_LINK:
parent = list_prev_entry(nd, link)->csdev;
child = list_next_entry(nd, link)->csdev;
- ret = coresight_enable_link(csdev, parent, child, source);
+ ret = coresight_enable_link(csdev, parent, child,
+ coresight_get_source(path));
if (ret)
goto err_disable_helpers;
break;
@@ -628,6 +680,10 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
ret = -EINVAL;
goto err_disable_helpers;
}
+
+ /* Iterate down to and including @from */
+ if (nd == from)
+ break;
}
out:
@@ -635,12 +691,23 @@ int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
err_disable_helpers:
coresight_disable_helpers(csdev, path);
err_disable_path:
+ /* No device is actually enabled */
+ if (nd == to)
+ goto out;
+
/* Fetch the previous node, the last successfully enabled one */
nd = list_next_entry(nd, link);
- coresight_disable_path_from(path, nd);
+ coresight_disable_path_from_to(path, nd, to);
goto out;
}
+int coresight_enable_path(struct coresight_path *path, enum cs_mode mode)
+{
+ return coresight_enable_path_from_to(path, mode,
+ coresight_path_first_node(path),
+ coresight_path_last_node(path));
+}
+
struct coresight_device *coresight_get_sink(struct coresight_path *path)
{
struct coresight_device *csdev;
--
2.34.1
^ permalink raw reply related
* [PATCH v12 19/28] coresight: Save active path for system tracers
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
This commit only set the path pointer for system tracers (e.g. STM) in
coresight_{enable|disable}_source().
Later changes will set the path pointer locally for per-CPU sources.
This is because the mode and path pointer must be set together, so that
they are observed atomically by the CPU PM notifier.
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 23 ++++++++++++++++++++++-
include/linux/coresight.h | 2 ++
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index ff882769cab2bb20bc2cb768e86f111f8a09cc8d..f07f6f28b9162911cdc673a454702f3ac4dc29ad 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -487,10 +487,28 @@ int coresight_enable_source(struct coresight_device *csdev,
struct perf_event *event, enum cs_mode mode,
struct coresight_path *path)
{
+ int ret;
+
if (!coresight_is_device_source(csdev))
return -EINVAL;
- return source_ops(csdev)->enable(csdev, event, mode, path);
+ ret = source_ops(csdev)->enable(csdev, event, mode, path);
+ if (ret)
+ return ret;
+
+ /*
+ * Update the path pointer until after the source is enabled to avoid
+ * races where multiple paths attempt to enable the same source.
+ *
+ * Do not set the path pointer here for per-CPU sources; set it locally
+ * on the CPU instead. Otherwise, there is a window where the path is
+ * enabled but the pointer is not yet set, causing CPU PM notifiers to
+ * miss PM operations due to reading a NULL pointer.
+ */
+ if (!coresight_is_percpu_source(csdev))
+ csdev->path = path;
+
+ return 0;
}
void coresight_disable_source(struct coresight_device *csdev, void *data)
@@ -498,6 +516,9 @@ void coresight_disable_source(struct coresight_device *csdev, void *data)
if (!coresight_is_device_source(csdev))
return;
+ if (!coresight_is_percpu_source(csdev))
+ csdev->path = NULL;
+
source_ops(csdev)->disable(csdev, data);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 5f9d7ea9f5941ab01eb6a084ca558a9417c7727f..58d474b269806d32cad6ed87da96550b06f1f30f 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -257,6 +257,7 @@ struct coresight_trace_id_map {
* by @coresight_ops.
* @access: Device i/o access abstraction for this device.
* @dev: The device entity associated to this component.
+ * @path: Activated path pointer (only used for source device).
* @mode: The device mode, i.e sysFS, Perf or disabled. This is actually
* an 'enum cs_mode' but stored in an atomic type. Access is always
* through atomic APIs, ensuring SMP-safe synchronisation between
@@ -291,6 +292,7 @@ struct coresight_device {
const struct coresight_ops *ops;
struct csdev_access access;
struct device dev;
+ struct coresight_path *path;
atomic_t mode;
int refcnt;
int cpu;
--
2.34.1
^ permalink raw reply related
* [PATCH v12 23/28] coresight: Control path during CPU idle
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Extend the CPU PM flow to control the path: disable from source up to
the node before the sink, then re-enable the same range on restore.
To avoid latency, control it up to the node before the sink.
Track per-CPU PM restore failures using percpu_pm_failed. Once a CPU
hits a restore failure, set the percpu_pm_failed and return NOTIFY_BAD
on subsequent notifications to avoid repeating half-completed
transitions.
Setting percpu_pm_failed permanently blocks CPU PM on that CPU. Such
failures are typically seen during development; disabling PM operations
simplifies the implementation, and a warning highlights the issue.
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Tested-by: James Clark <james.clark@linaro.org>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 90 +++++++++++++++++++++++-----
1 file changed, 75 insertions(+), 15 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index f07f6f28b9162911cdc673a454702f3ac4dc29ad..674fc375ff44e732405563af7be9dc8fae118e41 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -38,6 +38,7 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
static DEFINE_RAW_SPINLOCK(coresight_dev_lock);
static DEFINE_PER_CPU(struct coresight_device *, csdev_source);
+static DEFINE_PER_CPU(bool, percpu_pm_failed);
/**
* struct coresight_node - elements of a path, from source to sink
@@ -1840,7 +1841,7 @@ static void coresight_release_device_list(void)
}
}
-static struct coresight_device *coresight_cpu_get_active_source(void)
+static struct coresight_path *coresight_cpu_get_active_path(void)
{
struct coresight_device *source;
bool is_active = false;
@@ -1856,22 +1857,32 @@ static struct coresight_device *coresight_cpu_get_active_source(void)
/*
* It is expected to run in atomic context, so it cannot be preempted
- * to disable the source. Here returns the active source pointer
- * without concern that its state may change. Since the build path has
- * taken a reference on the component, the source can be safely used
- * by the caller.
+ * to disable the path. Here returns the active path pointer without
+ * concern that its state may change. Since the build path has taken
+ * a reference on the component, the path can be safely used by the
+ * caller.
*/
- return is_active ? source : NULL;
+ return is_active ? source->path : NULL;
}
-static int coresight_pm_is_needed(struct coresight_device *csdev)
+/* Return: 1 if PM is required, 0 if skip, or a negative error */
+static int coresight_pm_is_needed(struct coresight_path *path)
{
- if (!csdev)
+ struct coresight_device *source;
+
+ if (this_cpu_read(percpu_pm_failed))
+ return -EIO;
+
+ if (!path)
+ return 0;
+
+ source = coresight_get_source(path);
+ if (!source)
return 0;
/* pm_save_disable() and pm_restore_enable() must be paired */
- if (coresight_ops(csdev)->pm_save_disable &&
- coresight_ops(csdev)->pm_restore_enable)
+ if (coresight_ops(source)->pm_save_disable &&
+ coresight_ops(source)->pm_restore_enable)
return 1;
return 0;
@@ -1887,22 +1898,71 @@ static void coresight_pm_device_restore(struct coresight_device *csdev)
coresight_ops(csdev)->pm_restore_enable(csdev);
}
+static int coresight_pm_save(struct coresight_path *path)
+{
+ struct coresight_device *source = coresight_get_source(path);
+ struct coresight_node *from, *to;
+ int ret;
+
+ ret = coresight_pm_device_save(source);
+ if (ret)
+ return ret;
+
+ from = coresight_path_first_node(path);
+ /* Disable up to the node before sink */
+ to = list_prev_entry(coresight_path_last_node(path), link);
+ coresight_disable_path_from_to(path, from, to);
+
+ return 0;
+}
+
+static void coresight_pm_restore(struct coresight_path *path)
+{
+ struct coresight_device *source = coresight_get_source(path);
+ struct coresight_node *from, *to;
+ int ret;
+
+ from = coresight_path_first_node(path);
+ /* Enable up to the node before sink */
+ to = list_prev_entry(coresight_path_last_node(path), link);
+ ret = coresight_enable_path_from_to(path, coresight_get_mode(source),
+ from, to);
+ if (ret)
+ goto path_failed;
+
+ coresight_pm_device_restore(source);
+ return;
+
+path_failed:
+ pr_err("Failed in coresight PM restore on CPU%d: %d\n",
+ smp_processor_id(), ret);
+
+ /*
+ * Once PM fails on a CPU, set percpu_pm_failed and leave it set until
+ * reboot. This prevents repeated partial transitions during idle
+ * entry and exit.
+ */
+ this_cpu_write(percpu_pm_failed, true);
+}
+
static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
void *v)
{
- struct coresight_device *csdev = coresight_cpu_get_active_source();
+ struct coresight_path *path = coresight_cpu_get_active_path();
+ int ret;
- if (!coresight_pm_is_needed(csdev))
- return NOTIFY_DONE;
+ ret = coresight_pm_is_needed(path);
+ if (ret <= 0)
+ return ret ? NOTIFY_BAD : NOTIFY_DONE;
switch (cmd) {
case CPU_PM_ENTER:
- if (coresight_pm_device_save(csdev))
+ if (coresight_pm_save(path))
return NOTIFY_BAD;
break;
case CPU_PM_EXIT:
case CPU_PM_ENTER_FAILED:
- coresight_pm_device_restore(csdev);
+ coresight_pm_restore(path);
break;
default:
return NOTIFY_DONE;
--
2.34.1
^ permalink raw reply related
* [PATCH v12 21/28] coresight: etm3x: Set active path on target CPU
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Set the path pointer on the target CPU during ETM enable and disable.
This ensures the device mode and path pointer are updated together and
observed atomically by the CPU PM notifier.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-etm3x-core.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c
index aeeb284abdbe4b6a0960da45baa1138e203f3e3c..c6fe8b25b855a4119110fee4162f55c0154c3d05 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c
@@ -441,6 +441,7 @@ static int etm_enable_hw(struct etm_drvdata *drvdata)
struct etm_enable_arg {
struct etm_drvdata *drvdata;
+ struct coresight_path *path;
int rc;
};
@@ -462,8 +463,12 @@ static void etm_enable_sysfs_smp_call(void *info)
arg->rc = etm_enable_hw(arg->drvdata);
/* The tracer didn't start */
- if (arg->rc)
+ if (arg->rc) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
+ return;
+ }
+
+ csdev->path = arg->path;
}
void etm_release_trace_id(struct etm_drvdata *drvdata)
@@ -492,10 +497,13 @@ static int etm_enable_perf(struct coresight_device *csdev,
ret = etm_enable_hw(drvdata);
/* Failed to start tracer; roll back to DISABLED mode */
- if (ret)
+ if (ret) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
+ return ret;
+ }
- return ret;
+ csdev->path = path;
+ return 0;
}
static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
@@ -514,6 +522,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_pat
*/
if (cpu_online(drvdata->cpu)) {
arg.drvdata = drvdata;
+ arg.path = path;
ret = smp_call_function_single(drvdata->cpu,
etm_enable_sysfs_smp_call, &arg, 1);
if (!ret)
@@ -583,6 +592,7 @@ static void etm_disable_sysfs_smp_call(void *info)
etm_disable_hw(drvdata);
+ drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
}
@@ -607,6 +617,7 @@ static void etm_disable_perf(struct coresight_device *csdev)
CS_LOCK(drvdata->csa.base);
+ drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
/*
--
2.34.1
^ permalink raw reply related
* [PATCH v12 25/28] coresight: trbe: Save and restore state across CPU low power state
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
From: Yabin Cui <yabinc@google.com>
TRBE context can be lost when a CPU enters low power states. If a trace
source is restored while TRBE is not, tracing may run without an active
sink, which can lead to hangs on some devices (e.g., Pixel 9).
The save and restore flows are described in the section K5.5 "Context
switching" of Arm ARM (ARM DDI 0487 L.a). This commit adds save and
restore callbacks with following the software usages defined in the
architecture manual.
During the restore flow, since TRBLIMITR_EL1.E resets to 0 on a warm
reset, the trace buffer unit is disabled when idle resume, it is safe to
restore base/pointer/status registers first and program TRBLIMITR_EL1
last.
Signed-off-by: Yabin Cui <yabinc@google.com>
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Co-developed-by: Leo Yan <leo.yan@arm.com>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-trbe.c | 59 +++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
index 14e35b9660d76e47619cc6026b94929b3bb3e02b..c7cbca45f2debd4047b93283ea9fe5dd9e1f2ebf 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.c
+++ b/drivers/hwtracing/coresight/coresight-trbe.c
@@ -116,6 +116,20 @@ static int trbe_errata_cpucaps[] = {
*/
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
+/*
+ * struct trbe_save_state: Register values representing TRBE state
+ * @trblimitr - Trace Buffer Limit Address Register value
+ * @trbbaser - Trace Buffer Base Register value
+ * @trbptr - Trace Buffer Write Pointer Register value
+ * @trbsr - Trace Buffer Status Register value
+ */
+struct trbe_save_state {
+ u64 trblimitr;
+ u64 trbbaser;
+ u64 trbptr;
+ u64 trbsr;
+};
+
/*
* struct trbe_cpudata: TRBE instance specific data
* @trbe_flag - TRBE dirty/access flag support
@@ -134,6 +148,7 @@ struct trbe_cpudata {
enum cs_mode mode;
struct trbe_buf *buf;
struct trbe_drvdata *drvdata;
+ struct trbe_save_state save_state;
DECLARE_BITMAP(errata, TRBE_ERRATA_MAX);
};
@@ -1189,6 +1204,46 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
return IRQ_HANDLED;
}
+static int arm_trbe_save(struct coresight_device *csdev)
+{
+ struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
+ struct trbe_save_state *state = &cpudata->save_state;
+
+ state->trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
+
+ /* Disable the unit, ensure the writes to memory are complete */
+ if (state->trblimitr & TRBLIMITR_EL1_E)
+ trbe_drain_and_disable_local(cpudata);
+
+ state->trbbaser = read_sysreg_s(SYS_TRBBASER_EL1);
+ state->trbptr = read_sysreg_s(SYS_TRBPTR_EL1);
+ state->trbsr = read_sysreg_s(SYS_TRBSR_EL1);
+ return 0;
+}
+
+static void arm_trbe_restore(struct coresight_device *csdev)
+{
+ struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
+ struct trbe_save_state *state = &cpudata->save_state;
+
+ write_sysreg_s(state->trbbaser, SYS_TRBBASER_EL1);
+ write_sysreg_s(state->trbptr, SYS_TRBPTR_EL1);
+ write_sysreg_s(state->trbsr, SYS_TRBSR_EL1);
+
+ if (!(state->trblimitr & TRBLIMITR_EL1_E)) {
+ write_sysreg_s(state->trblimitr, SYS_TRBLIMITR_EL1);
+ } else {
+ /*
+ * The section K5.5 Context switching, Arm ARM (ARM DDI 0487
+ * L.a), S_PKLXF requires a Context synchronization event to
+ * guarantee the Trace Buffer Unit will observe the new values
+ * of the system registers.
+ */
+ isb();
+ set_trbe_enabled(cpudata, state->trblimitr);
+ }
+}
+
static const struct coresight_ops_sink arm_trbe_sink_ops = {
.enable = arm_trbe_enable,
.disable = arm_trbe_disable,
@@ -1198,7 +1253,9 @@ static const struct coresight_ops_sink arm_trbe_sink_ops = {
};
static const struct coresight_ops arm_trbe_cs_ops = {
- .sink_ops = &arm_trbe_sink_ops,
+ .pm_save_disable = arm_trbe_save,
+ .pm_restore_enable = arm_trbe_restore,
+ .sink_ops = &arm_trbe_sink_ops,
};
static ssize_t align_show(struct device *dev, struct device_attribute *attr, char *buf)
--
2.34.1
^ permalink raw reply related
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