LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v2 05/15] ibmveth: Add RX queue register/deregister helpers for MQ
From: Mingming Cao @ 2026-07-01 22:23 UTC (permalink / raw)
  To: netdev
  Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
	linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <20260701222327.61325-1-mmc@linux.ibm.com>

MQ RX replaces a single adapter-level register/free pair with a mixed
PHYP model: queue 0 via h_register_logical_lan*(), subordinates via
H_REG_LOGICAL_LAN_QUEUE. Subordinate registration returns queue handles
and hardware IRQ numbers that must be mapped to Linux virqs and unwound
on failure.

Add queue lifecycle helpers to isolate that control plane:

  ibmveth_register_logical_lan_queue()
  ibmveth_register_single_rx_queue()
  ibmveth_deregister_single_rx_queue()
  ibmveth_register_rx_queues()
  ibmveth_free_all_queues()
  ibmveth_dispose_subordinate_irq_mappings()

These helpers are called only when multi_queue is enabled (patch 11).
Until then open/close still use the legacy register and buffer hcall
path; legacy firmware is unchanged.

When multi_queue is enabled, queue 0 uses
h_register_logical_lan_with_handle() so all queues share the per-queue
buffer hcall path. register_rx_queues() registers with PHYP only;
interrupt delivery is enabled later from ibmveth_setup_rx_interrupts()
after request_irq(). Partial registration failure disposes subordinate virq
mappings before ibmveth_free_all_queues() clears handles;
free_all_queues() clears queue handles only — IRQ mappings are released
by dispose_subordinate_irq_mappings() or cleanup_rx_interrupts().
This commit also centralizes hcall accounting on the register/free paths.

Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
 drivers/net/ethernet/ibm/ibmveth.c | 337 ++++++++++++++++++++++++++++-
 1 file changed, 332 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 63b0184c622a..7fc11a4e1f61 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -21,6 +21,8 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/mm.h>
 #include <linux/pm.h>
 #include <linux/ethtool.h>
@@ -399,6 +401,28 @@ ibmveth_enable_irq(struct ibmveth_adapter *adapter, int queue_index)
 	return ibmveth_toggle_irq(adapter, queue_index, true);
 }
 
+/**
+ * ibmveth_dispose_subordinate_irq_mappings - Drop virq mappings for queues 1..N
+ * @adapter: ibmveth adapter structure
+ *
+ * Subordinate queues get mappings from irq_create_mapping() during PHYP
+ * registration.  Queue 0 uses netdev->irq from device tree and is left alone.
+ * Call after free_irq() when handlers were installed, or alone when open
+ * fails during register_rx_queues() before request_irq().
+ */
+static void
+ibmveth_dispose_subordinate_irq_mappings(struct ibmveth_adapter *adapter)
+{
+	int i;
+
+	for (i = 1; i < adapter->num_rx_queues; i++) {
+		if (adapter->queue_irq[i]) {
+			irq_dispose_mapping(adapter->queue_irq[i]);
+			adapter->queue_irq[i] = 0;
+		}
+	}
+}
+
 /**
  * ibmveth_setup_rx_interrupts - Register IRQs and enable NAPI
  * @adapter: ibmveth adapter structure
@@ -1082,8 +1106,8 @@ ibmveth_free_tx_resources(struct ibmveth_adapter *adapter)
 }
 
 static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
-				   union ibmveth_buf_desc rxq_desc,
-				   u64 mac_address)
+					union ibmveth_buf_desc rxq_desc,
+					u64 mac_address)
 {
 	int rc, try_again = 1;
 
@@ -1093,13 +1117,29 @@ static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
 	 * try again, but only once.
 	 */
 retry:
-	rc = h_register_logical_lan(adapter->vdev->unit_address,
-				    adapter->buffer_list_dma[0], rxq_desc.desc,
-				    adapter->filter_list_dma, mac_address);
+	/* In multi-queue mode, obtain a queue handle for queue 0 so all RX
+	 * queues can use the same per-queue buffer hypercalls.
+	 */
+	if (adapter->multi_queue) {
+		rc = h_register_logical_lan_with_handle(adapter->vdev->unit_address,
+							adapter->buffer_list_dma[0],
+							rxq_desc.desc,
+							adapter->filter_list_dma,
+							mac_address,
+							&adapter->queue_handle[0]);
+	} else {
+		rc = h_register_logical_lan(adapter->vdev->unit_address,
+					    adapter->buffer_list_dma[0],
+					    rxq_desc.desc,
+					    adapter->filter_list_dma,
+					    mac_address);
+	}
+	adapter->hcall_stats.reg_lan++;
 
 	if (rc != H_SUCCESS && try_again) {
 		do {
 			rc = h_free_logical_lan(adapter->vdev->unit_address);
+			adapter->hcall_stats.free_lan++;
 		} while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY));
 
 		try_again = 0;
@@ -1136,6 +1176,293 @@ static void __maybe_unused ibmveth_free_rx_qstats(struct ibmveth_adapter *adapte
 	adapter->rx_qstats = NULL;
 }
 
+/**
+ * ibmveth_register_logical_lan_queue - Register subordinate queue with hypervisor
+ * @adapter: ibmveth adapter structure
+ * @rxq_desc: Receive queue descriptor
+ * @queue_index: RX queue index (1..N for subordinate queues)
+ *
+ * Registers a subordinate receive queue using H_REG_LOGICAL_LAN_QUEUE.
+ * On success, stores the queue handle and virtual IRQ in the adapter.
+ * Retries once if registration fails (handles kexec case).  If IRQ mapping
+ * fails after a successful hypervisor registration, the queue is freed
+ * before returning.
+ *
+ * Return: H_SUCCESS on success, negative errno on IRQ mapping failure,
+ *         hypervisor error code otherwise
+ */
+static int
+ibmveth_register_logical_lan_queue(struct ibmveth_adapter *adapter,
+				   union ibmveth_buf_desc rxq_desc,
+				   int queue_index)
+{
+	unsigned long handle, hwirq;
+	unsigned int virq;
+	long lpar_rc;
+	int try_again = 1;
+
+retry:
+	netdev_dbg(adapter->netdev,
+		   "Attempting to register queue %d: unit_addr=0x%x buffer_list_dma=0x%llx rxq_desc=0x%llx\n",
+		   queue_index, adapter->vdev->unit_address,
+		   (unsigned long long)adapter->buffer_list_dma[queue_index],
+		   (unsigned long long)rxq_desc.desc);
+
+	lpar_rc = h_reg_logical_lan_queue(adapter->vdev->unit_address,
+					  adapter->buffer_list_dma[queue_index],
+					  rxq_desc.desc, &handle, &hwirq);
+	adapter->hcall_stats.reg_lan_queue++;
+
+	if (lpar_rc == H_SUCCESS) {
+		virq = irq_create_mapping(NULL, hwirq);
+		if (!virq) {
+			unsigned long free_rc;
+
+			netdev_err(adapter->netdev,
+				   "Failed to map IRQ for queue %d (hwirq=%lu)\n",
+				   queue_index, hwirq);
+			do {
+				free_rc = h_free_logical_lan_queue(adapter->vdev->unit_address,
+								   handle);
+			} while (H_IS_LONG_BUSY(free_rc) || (free_rc == H_BUSY));
+			adapter->hcall_stats.free_lan_queue++;
+			if (free_rc != H_SUCCESS)
+				netdev_err(adapter->netdev,
+					   "h_free_logical_lan_queue failed for queue %d after IRQ map failure: rc=0x%lx\n",
+					   queue_index, free_rc);
+			return -EINVAL;
+		}
+
+		adapter->queue_handle[queue_index] = handle;
+		adapter->queue_irq[queue_index] = virq;
+
+		netdev_dbg(adapter->netdev,
+			   "queue %d registered: handle=0x%llx irq=%u\n",
+			   queue_index, adapter->queue_handle[queue_index],
+			   adapter->queue_irq[queue_index]);
+		return H_SUCCESS;
+	}
+
+	if (lpar_rc == H_FUNCTION) {
+		if (adapter->multi_queue) {
+			netdev_info(adapter->netdev,
+				    "Multi queue mode not supported by firmware, falling back to single queue\n");
+			adapter->multi_queue = 0;
+		} else {
+			netdev_err(adapter->netdev,
+				   "Unexpected H_FUNCTION for queue %d registration (MQ mode already disabled)\n",
+				   queue_index);
+		}
+		return lpar_rc;
+	}
+
+	if (try_again) {
+		try_again = 0;
+		goto retry;
+	}
+
+	netdev_err(adapter->netdev,
+		   "h_reg_logical_lan_queue failed with %ld after retry\n",
+		   lpar_rc);
+	netdev_err(adapter->netdev,
+		   "queue %d params: unit_addr=0x%x buffer_list_dma=0x%llx rxq_desc=0x%llx\n",
+		   queue_index, adapter->vdev->unit_address,
+		   (unsigned long long)adapter->buffer_list_dma[queue_index],
+		   (unsigned long long)rxq_desc.desc);
+
+	return lpar_rc;
+}
+
+/**
+ * ibmveth_register_single_rx_queue - Register one subordinate RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to register (1..N)
+ * @mac_address: MAC address (unused; reserved for API symmetry)
+ *
+ * Builds the queue descriptor and registers with the hypervisor via
+ * ibmveth_register_logical_lan_queue().
+ *
+ * Return: 0 on success, -EINVAL if @queue_idx is invalid, -EIO on failure
+ */
+static int
+ibmveth_register_single_rx_queue(struct ibmveth_adapter *adapter,
+				 int queue_idx, u64 mac_address)
+{
+	struct net_device *netdev = adapter->netdev;
+	union ibmveth_buf_desc rxq_desc;
+	long lpar_rc;
+
+	(void)mac_address;
+
+	if (WARN_ON(queue_idx < 1 || queue_idx >= IBMVETH_MAX_RX_QUEUES))
+		return -EINVAL;
+
+	rxq_desc.fields.flags_len = IBMVETH_BUF_VALID |
+				    adapter->rx_queue[queue_idx].queue_len;
+	rxq_desc.fields.address = adapter->rx_queue[queue_idx].queue_dma;
+
+	lpar_rc = ibmveth_register_logical_lan_queue(adapter, rxq_desc,
+						     queue_idx);
+	if (lpar_rc != H_SUCCESS) {
+		netdev_err(netdev, "Failed to register queue %d: rc=0x%lx\n",
+			   queue_idx, lpar_rc);
+		return -EIO;
+	}
+
+	netdev_dbg(netdev, "Registered queue %d with handle 0x%llx\n",
+		   queue_idx, adapter->queue_handle[queue_idx]);
+
+	return 0;
+}
+
+/**
+ * ibmveth_deregister_single_rx_queue - Deregister one subordinate RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to deregister (1..N)
+ *
+ * Deregisters a single queue via H_FREE_LOGICAL_LAN_QUEUE and disposes
+ * the IRQ mapping for subordinate queues. Queue 0 is freed only through
+ * ibmveth_free_all_queues() (H_FREE_LOGICAL_LAN).
+ */
+static void __maybe_unused
+ibmveth_deregister_single_rx_queue(struct ibmveth_adapter *adapter,
+				   int queue_idx)
+{
+	unsigned long lpar_rc;
+
+	if (!adapter->queue_handle[queue_idx])
+		return;
+
+	do {
+		lpar_rc = h_free_logical_lan_queue(adapter->vdev->unit_address,
+						   adapter->queue_handle[queue_idx]);
+	} while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
+
+	adapter->hcall_stats.free_lan_queue++;
+
+	if (lpar_rc != H_SUCCESS) {
+		netdev_err(adapter->netdev,
+			   "h_free_logical_lan_queue failed for queue %d: rc=0x%lx\n",
+			   queue_idx, lpar_rc);
+	}
+
+	adapter->queue_handle[queue_idx] = 0;
+
+	if (queue_idx > 0 && adapter->queue_irq[queue_idx]) {
+		irq_dispose_mapping(adapter->queue_irq[queue_idx]);
+		adapter->queue_irq[queue_idx] = 0;
+	}
+
+	netdev_dbg(adapter->netdev, "Deregistered queue %d\n", queue_idx);
+}
+
+/**
+ * ibmveth_free_all_queues - Free all RX queues at once
+ * @adapter: ibmveth adapter structure
+ *
+ * Uses H_FREE_LOGICAL_LAN to free all queues in one hypercall.
+ * Used during interface close and registration error cleanup.
+ *
+ * Clears queue handles only; queue_irq[] is released by
+ * ibmveth_cleanup_rx_interrupts() on close, or by
+ * ibmveth_dispose_subordinate_irq_mappings() on partial register failure.
+ */
+static void ibmveth_free_all_queues(struct ibmveth_adapter *adapter)
+{
+	unsigned long lpar_rc;
+	int i;
+
+	netdev_dbg(adapter->netdev, "freeing all RX queues at once\n");
+
+	do {
+		lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
+		adapter->hcall_stats.free_lan++;
+	} while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
+
+	if (lpar_rc != H_SUCCESS) {
+		netdev_err(adapter->netdev,
+			   "h_free_logical_lan failed: %ld\n", lpar_rc);
+	}
+
+	for (i = 0; i < adapter->num_rx_queues; i++)
+		adapter->queue_handle[i] = 0;
+}
+
+/**
+ * ibmveth_register_rx_queues - Register RX queues with hypervisor
+ * @adapter: ibmveth adapter structure
+ * @mac_address: MAC address for device registration
+ *
+ * Registers queue 0 via ibmveth_register_logical_lan(), then subordinate
+ * queues 1..N when multi-queue mode is enabled.
+ *
+ * Return: 0 on success, -ENONET if queue 0 registration fails, -EIO on
+ *         subordinate queue registration failure
+ */
+static int
+ibmveth_register_rx_queues(struct ibmveth_adapter *adapter, u64 mac_address)
+{
+	struct net_device *netdev = adapter->netdev;
+	union ibmveth_buf_desc rxq_desc;
+	unsigned long lpar_rc;
+	int i, rc;
+
+	rxq_desc.fields.flags_len = IBMVETH_BUF_VALID |
+				    adapter->rx_queue[0].queue_len;
+	rxq_desc.fields.address = adapter->rx_queue[0].queue_dma;
+	adapter->queue_irq[0] = netdev->irq;
+
+	rc = ibmveth_disable_irq(adapter, 0);
+	if (rc != H_SUCCESS)
+		netdev_dbg(netdev,
+			   "Failed to disable IRQ for queue 0 before registration, rc=%d\n",
+			   rc);
+
+	lpar_rc = ibmveth_register_logical_lan(adapter, rxq_desc, mac_address);
+	if (lpar_rc != H_SUCCESS) {
+		netdev_err(netdev, "h_register_logical_lan failed: %ld\n", lpar_rc);
+		netdev_err(netdev,
+			   "buffer TCE:0x%llx filter TCE:0x%llx rxq desc:0x%llx MAC:0x%llx\n",
+			   adapter->buffer_list_dma[0],
+			   adapter->filter_list_dma,
+			   rxq_desc.desc, mac_address);
+		return -ENONET;
+	}
+
+	if (adapter->num_rx_queues == 1 || !adapter->multi_queue) {
+		netdev_dbg(netdev,
+			   "registered 1 RX queue with hypervisor (single-queue mode)\n");
+		return 0;
+	}
+
+	netdev_dbg(netdev, "Registering %d subordinate queues (1-%d)\n",
+		   adapter->num_rx_queues - 1, adapter->num_rx_queues - 1);
+
+	for (i = 1; i < adapter->num_rx_queues; i++) {
+		rc = ibmveth_register_single_rx_queue(adapter, i, mac_address);
+		if (rc) {
+			if (!adapter->queue_handle[i] || !adapter->queue_irq[i]) {
+				netdev_err(netdev,
+					   "Invalid hypervisor return for queue %d: handle=0x%llx irq=%u\n",
+					   i, adapter->queue_handle[i],
+					   adapter->queue_irq[i]);
+			}
+			goto err_unregister;
+		}
+	}
+
+	netdev_dbg(netdev,
+		   "registered %d RX queues with hypervisor (multi-queue mode)\n",
+		   adapter->num_rx_queues);
+
+	return 0;
+
+err_unregister:
+	ibmveth_dispose_subordinate_irq_mappings(adapter);
+	ibmveth_free_all_queues(adapter);
+	return rc;
+}
+
 static int ibmveth_open(struct net_device *netdev)
 {
 	struct ibmveth_adapter *adapter = netdev_priv(netdev);
-- 
2.39.3 (Apple Git-146)



^ permalink raw reply related

* [PATCH net-next v2 03/15] ibmveth: Refactor RX interrupt control for MQ RX queues
From: Mingming Cao @ 2026-07-01 22:23 UTC (permalink / raw)
  To: netdev
  Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
	linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <20260701222327.61325-1-mmc@linux.ibm.com>

Queue 0 and subordinate RX queues use different interrupt control
interfaces in PHYP:

  - queue 0: h_vio_signal() after h_register_logical_lan()
  - queue N: H_VIOCTL against the queue handle/hwirq mapping

The current code is single-queue oriented and cannot safely scale to
multiple RX queues in poll completion and open/close IRQ setup.

Introduce queue-indexed interrupt helpers:

  ibmveth_enable_irq(adapter, queue_index)
  ibmveth_disable_irq(adapter, queue_index)
  ibmveth_setup_rx_interrupts()
  ibmveth_cleanup_rx_interrupts()

These helpers centralize queue0-vs-subordinate dispatch and make IRQ
lifecycle symmetric across open/close and future resize paths.

request_irq() is wired with &adapter->napi[i] as dev_id per queue, so
interrupt ownership follows the NAPI instance that services that RX
queue.

Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
 drivers/net/ethernet/ibm/ibmveth.c | 160 +++++++++++++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 95068fb20dba..b5ae979c1f82 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -315,6 +315,166 @@ static void __maybe_unused ibmveth_cleanup_rx_resources(struct ibmveth_adapter *
 	}
 }
 
+/**
+ * ibmveth_toggle_irq - Common helper to enable/disable queue interrupts
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Index of the queue (0 for primary, 1+ for subordinate)
+ * @enable: true to enable, false to disable
+ *
+ * For queue 0 (primary), uses h_vio_signal() as it's registered via
+ * h_register_logical_lan(). For subordinate queues (1+), uses H_VIOCTL
+ * with H_ENABLE/DISABLE_VIO_INTERRUPT for per-queue interrupt control.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int
+ibmveth_toggle_irq(struct ibmveth_adapter *adapter, int queue_index, bool enable)
+{
+	unsigned long rc;
+	unsigned long irq = adapter->queue_irq[queue_index];
+	const char *action = enable ? "enable" : "disable";
+
+	if (queue_index == 0) {
+		/* Primary queue: use h_vio_signal() */
+		rc = h_vio_signal(adapter->vdev->unit_address,
+				  enable ? VIO_IRQ_ENABLE : VIO_IRQ_DISABLE);
+	} else {
+		/* Subordinate queues: use H_VIOCTL with hardware IRQ */
+		struct irq_data *irq_data = irq_get_irq_data(irq);
+		irq_hw_number_t hwirq;
+		u64 vioctl_cmd = enable ? H_ENABLE_VIO_INTERRUPT : H_DISABLE_VIO_INTERRUPT;
+
+		if (!irq_data) {
+			netdev_err(adapter->netdev,
+				   "Failed to get IRQ data for queue %d (virq=%lu)\n",
+				   queue_index, irq);
+			return -EINVAL;
+		}
+
+		hwirq = irqd_to_hwirq(irq_data);
+		rc = plpar_hcall_norets(H_VIOCTL,
+					adapter->vdev->unit_address,
+					vioctl_cmd,
+					hwirq, 0, 0);
+
+		if (rc == H_PARAMETER) {
+			/* H_PARAMETER is non-fatal when IRQ is already in the requested state. */
+			netdev_warn_once(adapter->netdev,
+					 "H_VIOCTL %s IRQ returned H_PARAMETER for queue %d (hwirq=%lu)\n",
+					 action, queue_index, hwirq);
+			return 0;
+		}
+	}
+
+	if (rc)
+		netdev_err(adapter->netdev,
+			   "Failed to %s IRQ for queue %d, rc=%ld\n",
+			   action, queue_index, rc);
+	return rc;
+}
+
+/**
+ * ibmveth_disable_irq - Disable interrupt for a specific queue
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Index of the queue (0 for primary, 1+ for subordinate)
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int
+ibmveth_disable_irq(struct ibmveth_adapter *adapter, int queue_index)
+{
+	return ibmveth_toggle_irq(adapter, queue_index, false);
+}
+
+/**
+ * ibmveth_enable_irq - Enable interrupt for a specific queue
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Index of the queue (0 for primary, 1+ for subordinate)
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int
+ibmveth_enable_irq(struct ibmveth_adapter *adapter, int queue_index)
+{
+	return ibmveth_toggle_irq(adapter, queue_index, true);
+}
+
+/**
+ * ibmveth_setup_rx_interrupts - Register IRQs and enable NAPI
+ * @adapter: ibmveth adapter structure
+ *
+ * Registers interrupt handlers for all RX queues and enables NAPI polling.
+ * On error, cleans up any successfully registered IRQs before returning.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int __maybe_unused
+ibmveth_setup_rx_interrupts(struct ibmveth_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i, rc;
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		if (!adapter->queue_irq[i]) {
+			netdev_err(netdev, "queue %d has invalid IRQ (0)\n", i);
+			rc = -EINVAL;
+			goto err_free_irqs;
+		}
+
+		rc = request_irq(adapter->queue_irq[i], ibmveth_interrupt,
+				 0, netdev->name, &adapter->napi[i]);
+		if (rc) {
+			netdev_err(netdev,
+				   "request_irq() failed for irq 0x%x queue %d: %d\n",
+				   adapter->queue_irq[i], i, rc);
+			goto err_free_irqs;
+		}
+	}
+
+	for (i = 0; i < adapter->num_rx_queues; i++)
+		napi_enable(&adapter->napi[i]);
+
+	return 0;
+
+err_free_irqs:
+	while (--i >= 0)
+		free_irq(adapter->queue_irq[i], &adapter->napi[i]);
+	return rc;
+}
+
+/**
+ * ibmveth_cleanup_rx_interrupts - Disable NAPI and free IRQs
+ * @adapter: ibmveth adapter structure
+ *
+ * Disables NAPI polling and frees interrupt handlers for all RX queues.
+ */
+static void
+ibmveth_cleanup_rx_interrupts(struct ibmveth_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < adapter->num_rx_queues; i++)
+		napi_disable(&adapter->napi[i]);
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		if (adapter->queue_irq[i])
+			free_irq(adapter->queue_irq[i], &adapter->napi[i]);
+	}
+
+	/* Dispose IRQ mappings for subordinate queues (1-15).
+	 * Queue 0 uses netdev->irq from device tree, not irq_create_mapping().
+	 */
+	for (i = 1; i < adapter->num_rx_queues; i++) {
+		if (adapter->queue_irq[i]) {
+			irq_dispose_mapping(adapter->queue_irq[i]);
+			adapter->queue_irq[i] = 0;
+		}
+	}
+
+	/* Clear queue 0 IRQ number */
+	adapter->queue_irq[0] = 0;
+}
+
 /* setup the initial settings for a buffer pool */
 static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool,
 				     u32 pool_index, u32 pool_size,
-- 
2.39.3 (Apple Git-146)



^ permalink raw reply related

* [PATCH net-next v2 04/15] ibmveth: Refactor TX resource allocation in open/close paths
From: Mingming Cao @ 2026-07-01 22:23 UTC (permalink / raw)
  To: netdev
  Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
	linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <20260701222327.61325-1-mmc@linux.ibm.com>

Same story as the RX refactor: pull TX LTB alloc out of open/close.

ibmveth_alloc_tx_resources() / ibmveth_free_tx_resources() walk
real_num_tx_queues so ethtool TX channel changes keep working. Hooked
into open/close in the next patch.

No MQ RX behaviour change — TX was already multi-queue capable via
ethtool -L. This patch only tidies the open/close path ahead of the
RX helper wiring in the next patch.

Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
 drivers/net/ethernet/ibm/ibmveth.c | 43 ++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index b5ae979c1f82..63b0184c622a 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -1038,6 +1038,49 @@ static int ibmveth_allocate_tx_ltb(struct ibmveth_adapter *adapter, int idx)
 	return 0;
 }
 
+/**
+ * ibmveth_alloc_tx_resources - Allocate TX resources for all queues
+ * @adapter: ibmveth adapter structure
+ *
+ * Allocates TX Long Term Buffers (LTBs) for all TX queues.
+ *
+ * Return: 0 on success, -ENOMEM on failure
+ */
+static int __maybe_unused
+ibmveth_alloc_tx_resources(struct ibmveth_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i;
+
+	for (i = 0; i < netdev->real_num_tx_queues; i++) {
+		if (ibmveth_allocate_tx_ltb(adapter, i))
+			goto err_free_ltbs;
+	}
+
+	return 0;
+
+err_free_ltbs:
+	while (--i >= 0)
+		ibmveth_free_tx_ltb(adapter, i);
+	return -ENOMEM;
+}
+
+/**
+ * ibmveth_free_tx_resources - Free TX resources for all queues
+ * @adapter: ibmveth adapter structure
+ *
+ * Frees TX Long Term Buffers (LTBs) for all TX queues.
+ */
+static void __maybe_unused
+ibmveth_free_tx_resources(struct ibmveth_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i;
+
+	for (i = 0; i < netdev->real_num_tx_queues; i++)
+		ibmveth_free_tx_ltb(adapter, i);
+}
+
 static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
 				   union ibmveth_buf_desc rxq_desc,
 				   u64 mac_address)
-- 
2.39.3 (Apple Git-146)



^ permalink raw reply related

* [PATCH net-next v2 02/15] ibmveth: Refactor buffer pool management for per-queue MQ RX
From: Mingming Cao @ 2026-07-01 22:23 UTC (permalink / raw)
  To: netdev
  Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
	linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <20260701222327.61325-1-mmc@linux.ibm.com>

This is the key memory-model change for MQ RX.

Legacy ibmveth uses five adapter-level RX buffer pools (512 B
through 64 KiB slots). pool_active[] enables the standard-MTU pools by
default; larger pools activate when MTU requires them. With single-queue
RX that set is shared on one completion path.

MQ requires the same pool model per queue: buffers post with
H_ADD_LOGICAL_LAN_BUFFERS_QUEUE against a queue handle and completions
return on that queue. Sharing pools across queues would mix ownership and
break queue-local replenish/drain/teardown.

Refactor around queue-local pools with static geometry (still defined at
probe on queue 0, copied to queues 1..N at alloc time):

  rx_buff_pool[queue][pool]
  ibmveth_alloc_queue_buffer_pools()
  ibmveth_free_queue_buffer_pools()
  ibmveth_alloc_buffer_pools() / ibmveth_free_buffer_pools()

Queue 0 remains the template for pool geometry (size, buff_size,
threshold, active). For queues 1..N we copy metadata from queue 0, then
allocate actual backing arrays/skbs per queue.

At the default 1500-byte MTU, pool 4 (64 KiB buffers) is not needed and
costs guest memory when allocated per queue in MQ mode. Clear
pool_active[4] so open() skips it; ibmveth_change_mtu() still enables
larger pools when MTU warrants jumbo frames.

Error handling is also made queue-safe:

  - if allocation fails in one pool, unwind only what was allocated for
    that queue, then unwind prior queues in the caller
  - free paths release pools based on real allocations
    (free_map/dma_addr/skbuff), not only pool->active

That allocation-based free check is intentional: later resize and failure
paths can leave memory allocated even when active was already cleared.
Freeing by allocation state avoids leaks and double-free corner cases.

This split keeps the per-queue pool design isolated and reviewable ahead
of the MQ datapath enable commit later in the series.

Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
 drivers/net/ethernet/ibm/ibmveth.c | 127 +++++++++++++++++++++++++++++
 drivers/net/ethernet/ibm/ibmveth.h |   2 +-
 2 files changed, 128 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index b8adc9935471..95068fb20dba 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -611,6 +611,133 @@ static void ibmveth_free_buffer_pool(struct ibmveth_adapter *adapter,
 	}
 }
 
+/**
+ * ibmveth_alloc_queue_buffer_pools - Allocate buffer pools for a single queue
+ * @adapter: ibmveth adapter structure
+ * @queue: queue index
+ *
+ * Allocates all active buffer pools for the specified queue.
+ * Pool metadata must be initialized before calling this function.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int ibmveth_alloc_queue_buffer_pools(struct ibmveth_adapter *adapter,
+					    int queue)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i;
+
+	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+		if (!adapter->rx_buff_pool[queue][i].active)
+			continue;
+
+		if (ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[queue][i])) {
+			netdev_err(netdev,
+				   "unable to allocate buffer pool %d for queue %d (size=%u, count=%u)\n",
+				   i, queue,
+				   adapter->rx_buff_pool[queue][i].buff_size,
+				   adapter->rx_buff_pool[queue][i].size);
+			adapter->rx_buff_pool[queue][i].active = 0;
+
+			/* Free pools allocated so far for this queue */
+			while (--i >= 0) {
+				if (adapter->rx_buff_pool[queue][i].active)
+					ibmveth_free_buffer_pool(adapter,
+								 &adapter->rx_buff_pool[queue][i]);
+			}
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ibmveth_free_queue_buffer_pools - Free buffer pools for a single queue
+ * @adapter: ibmveth adapter structure
+ * @queue: queue index
+ *
+ * Frees all active buffer pools for the specified queue.
+ */
+static void ibmveth_free_queue_buffer_pools(struct ibmveth_adapter *adapter,
+					    int queue)
+{
+	int i;
+
+	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+		struct ibmveth_buff_pool *pool = &adapter->rx_buff_pool[queue][i];
+
+		/* Free pool if it has allocated memory, regardless of active flag.
+		 * Pools may have memory allocated but not marked active during
+		 * queue scale-up, so we must check for actual allocations.
+		 */
+		if (pool->free_map || pool->dma_addr || pool->skbuff)
+			ibmveth_free_buffer_pool(adapter, pool);
+	}
+}
+
+/**
+ * ibmveth_alloc_buffer_pools - Allocate buffer pools for all queues
+ * @adapter: ibmveth adapter structure
+ *
+ * Initializes pool metadata for queues 1-N from queue 0 settings,
+ * then allocates buffer pools for all queues using the helper function.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int __maybe_unused ibmveth_alloc_buffer_pools(struct ibmveth_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i, q, rc;
+
+	/* Initialize pool metadata for queues 1-15 from queue 0 settings */
+	for (q = 1; q < adapter->num_rx_queues; q++) {
+		for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+			struct ibmveth_buff_pool *src = &adapter->rx_buff_pool[0][i];
+			struct ibmveth_buff_pool *dst = &adapter->rx_buff_pool[q][i];
+
+			dst->size = src->size;
+			dst->index = src->index;
+			dst->buff_size = src->buff_size;
+			dst->threshold = src->threshold;
+			dst->active = src->active;
+		}
+	}
+
+	/* Allocate actual buffers for all queues */
+	for (q = 0; q < adapter->num_rx_queues; q++) {
+		rc = ibmveth_alloc_queue_buffer_pools(adapter, q);
+		if (rc) {
+			/* Free pools for all previous queues */
+			while (--q >= 0)
+				ibmveth_free_queue_buffer_pools(adapter, q);
+			return rc;
+		}
+	}
+
+	netdev_dbg(netdev, "allocated buffer pools for %d queue(s)\n",
+		   adapter->num_rx_queues);
+	return 0;
+}
+
+/**
+ * ibmveth_free_buffer_pools - Free buffer pools for all queues
+ * @adapter: ibmveth adapter structure
+ *
+ * Frees buffer pools for all queues using the helper function.
+ */
+static void __maybe_unused ibmveth_free_buffer_pools(struct ibmveth_adapter *adapter)
+{
+	int q;
+
+	/* Free buffer pools for all queues */
+	for (q = 0; q < adapter->num_rx_queues; q++)
+		ibmveth_free_queue_buffer_pools(adapter, q);
+
+	netdev_dbg(adapter->netdev, "freed buffer pools for %d queue(s)\n",
+		   adapter->num_rx_queues);
+}
+
 /**
  * ibmveth_remove_buffer_from_pool - remove a buffer from a pool
  * @adapter: adapter instance
diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h
index f0dffe42e8fe..d2ceeccd5fbd 100644
--- a/drivers/net/ethernet/ibm/ibmveth.h
+++ b/drivers/net/ethernet/ibm/ibmveth.h
@@ -286,7 +286,7 @@ static inline long h_illan_attributes(unsigned long unit_address,
 static int pool_size[] = { 512, 1024 * 2, 1024 * 16, 1024 * 32, 1024 * 64 };
 static int pool_count[] = { 256, 512, 256, 256, 256 };
 static int pool_count_cmo[] = { 256, 512, 256, 256, 64 };
-static int pool_active[] = { 1, 1, 0, 0, 1};
+static int pool_active[] = { 1, 1, 0, 0, 0};
 
 #define IBM_VETH_INVALID_MAP ((u16)0xffff)
 
-- 
2.39.3 (Apple Git-146)



^ permalink raw reply related

* [PATCH net-next v2 01/15] ibmveth: Refactor RX resource allocation for MQ RX bring-up
From: Mingming Cao @ 2026-07-01 22:23 UTC (permalink / raw)
  To: netdev
  Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
	linuxppc-dev, maddy, mpe, Dave Marquardt
In-Reply-To: <20260701222327.61325-1-mmc@linux.ibm.com>

ibmveth_open() allocates the filter list and every RX queue inline.
That's already ~160 lines and would get ugly once we loop over
num_rx_queues, especially on error unwind.

Pull the RX bits into helpers:

  ibmveth_alloc_filter_list() / ibmveth_free_filter_list()
    — shared multicast filter list (one per adapter, not per queue)

  ibmveth_alloc_rx_queues() / ibmveth_cleanup_rx_resources()
    — per-queue buffer lists and RX rings, looping [0, num_rx_queues)

alloc_rx_queues() rolls back on failure so open() does not need nested
goto chains for every queue index.

This is the first of several helper-only patches (pools, IRQ, TX, PHYP
registration, open/close wiring, buffer submit) that reshape bring-up
ahead of MQ datapath commit later in the series.

Signed-off-by: Mingming Cao <mmc@linux.ibm.com>
Reviewed-by: Dave Marquardt <davemarq@linux.ibm.com>
---
 drivers/net/ethernet/ibm/ibmveth.c | 168 +++++++++++++++++++++++++++++
 1 file changed, 168 insertions(+)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 8f9f927bff23..b8adc9935471 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -147,6 +147,174 @@ static unsigned int ibmveth_real_max_tx_queues(void)
 	return min(n_cpu, IBMVETH_MAX_QUEUES);
 }
 
+/**
+ * ibmveth_alloc_filter_list - Allocate and map filter list
+ * @adapter: ibmveth adapter structure
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int __maybe_unused ibmveth_alloc_filter_list(struct ibmveth_adapter *adapter)
+{
+	struct device *dev = &adapter->vdev->dev;
+	struct net_device *netdev = adapter->netdev;
+
+	adapter->filter_list_addr = (void *)get_zeroed_page(GFP_KERNEL);
+	if (!adapter->filter_list_addr) {
+		netdev_err(netdev, "unable to allocate filter pages\n");
+		return -ENOMEM;
+	}
+
+	adapter->filter_list_dma = dma_map_single(dev,
+						  adapter->filter_list_addr,
+						  4096, DMA_BIDIRECTIONAL);
+	if (dma_mapping_error(dev, adapter->filter_list_dma)) {
+		netdev_err(netdev, "unable to map filter list pages\n");
+		free_page((unsigned long)adapter->filter_list_addr);
+		adapter->filter_list_addr = NULL;
+		return -ENOMEM;
+	}
+
+	netdev_dbg(netdev, "filter list @ 0x%p (DMA: 0x%llx)\n",
+		   adapter->filter_list_addr,
+		   (unsigned long long)adapter->filter_list_dma);
+
+	return 0;
+}
+
+/**
+ * ibmveth_free_filter_list - Free filter list resources
+ * @adapter: ibmveth adapter structure
+ */
+static void __maybe_unused ibmveth_free_filter_list(struct ibmveth_adapter *adapter)
+{
+	struct device *dev = &adapter->vdev->dev;
+
+	if (adapter->filter_list_dma) {
+		dma_unmap_single(dev, adapter->filter_list_dma, 4096,
+				 DMA_BIDIRECTIONAL);
+		adapter->filter_list_dma = 0;
+	}
+
+	if (adapter->filter_list_addr) {
+		free_page((unsigned long)adapter->filter_list_addr);
+		adapter->filter_list_addr = NULL;
+	}
+}
+
+/**
+ * ibmveth_alloc_rx_queues - Allocate per-queue RX resources
+ * @adapter: ibmveth adapter structure
+ * @rxq_entries: Number of entries per RX queue
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int __maybe_unused
+ibmveth_alloc_rx_queues(struct ibmveth_adapter *adapter, int rxq_entries)
+{
+	struct device *dev = &adapter->vdev->dev;
+	struct net_device *netdev = adapter->netdev;
+	int i;
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		adapter->buffer_list_addr[i] = (void *)get_zeroed_page(GFP_KERNEL);
+		if (!adapter->buffer_list_addr[i]) {
+			netdev_err(netdev, "unable to allocate buffer list for queue %d\n", i);
+			goto err_cleanup;
+		}
+
+		adapter->rx_queue[i].queue_len =
+			sizeof(struct ibmveth_rx_q_entry) * rxq_entries;
+		adapter->rx_queue[i].queue_addr =
+			dma_alloc_coherent(dev, adapter->rx_queue[i].queue_len,
+					   &adapter->rx_queue[i].queue_dma,
+					   GFP_KERNEL);
+		if (!adapter->rx_queue[i].queue_addr) {
+			netdev_err(netdev, "unable to allocate RX queue for queue %d\n", i);
+			goto err_cleanup;
+		}
+
+		adapter->buffer_list_dma[i] = dma_map_single(dev,
+							     adapter->buffer_list_addr[i],
+							     4096, DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(dev, adapter->buffer_list_dma[i])) {
+			netdev_err(netdev, "unable to map buffer list for queue %d\n", i);
+			adapter->buffer_list_dma[i] = 0;
+			goto err_cleanup;
+		}
+
+		adapter->rx_queue[i].index = 0;
+		adapter->rx_queue[i].num_slots = rxq_entries;
+		adapter->rx_queue[i].toggle = 1;
+
+		netdev_dbg(netdev, "queue %d: buffer_list @ 0x%p (DMA: 0x%llx), rx_queue @ 0x%p (DMA: 0x%llx), %llu entries\n",
+			   i, adapter->buffer_list_addr[i],
+			   (unsigned long long)adapter->buffer_list_dma[i],
+			   adapter->rx_queue[i].queue_addr,
+			   (unsigned long long)adapter->rx_queue[i].queue_dma,
+			   (unsigned long long)rxq_entries);
+	}
+
+	netdev_dbg(netdev, "allocated %d RX queue(s) with %d entries each\n",
+		   adapter->num_rx_queues, rxq_entries);
+
+	return 0;
+
+err_cleanup:
+	/* Clean up previously allocated queues */
+	for (; i >= 0; i--) {
+		if (adapter->buffer_list_dma[i]) {
+			dma_unmap_single(dev, adapter->buffer_list_dma[i],
+					 4096, DMA_BIDIRECTIONAL);
+			adapter->buffer_list_dma[i] = 0;
+		}
+		if (adapter->rx_queue[i].queue_addr) {
+			dma_free_coherent(dev, adapter->rx_queue[i].queue_len,
+					  adapter->rx_queue[i].queue_addr,
+					  adapter->rx_queue[i].queue_dma);
+			adapter->rx_queue[i].queue_addr = NULL;
+		}
+		if (adapter->buffer_list_addr[i]) {
+			free_page((unsigned long)adapter->buffer_list_addr[i]);
+			adapter->buffer_list_addr[i] = NULL;
+		}
+	}
+
+	return -ENOMEM;
+}
+
+/**
+ * ibmveth_cleanup_rx_resources - Free all RX queue resources
+ * @adapter: ibmveth adapter structure
+ */
+static void __maybe_unused ibmveth_cleanup_rx_resources(struct ibmveth_adapter *adapter)
+{
+	struct device *dev = &adapter->vdev->dev;
+	int i;
+
+	netdev_dbg(adapter->netdev, "cleaning up %d RX queue(s)\n",
+		   adapter->num_rx_queues);
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		if (adapter->buffer_list_dma[i]) {
+			dma_unmap_single(dev, adapter->buffer_list_dma[i],
+					 4096, DMA_BIDIRECTIONAL);
+			adapter->buffer_list_dma[i] = 0;
+		}
+
+		if (adapter->rx_queue[i].queue_addr) {
+			dma_free_coherent(dev, adapter->rx_queue[i].queue_len,
+					  adapter->rx_queue[i].queue_addr,
+					  adapter->rx_queue[i].queue_dma);
+			adapter->rx_queue[i].queue_addr = NULL;
+		}
+
+		if (adapter->buffer_list_addr[i]) {
+			free_page((unsigned long)adapter->buffer_list_addr[i]);
+			adapter->buffer_list_addr[i] = NULL;
+		}
+	}
+}
+
 /* setup the initial settings for a buffer pool */
 static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool,
 				     u32 pool_index, u32 pool_size,
-- 
2.39.3 (Apple Git-146)



^ permalink raw reply related

* [PATCH net-next v2 00/15] ibmveth: Add multi-queue RX support
From: Mingming Cao @ 2026-07-01 22:23 UTC (permalink / raw)
  To: netdev
  Cc: horms, bjking1, haren, ricklind, mmc, kuba, edumazet, pabeni,
	linuxppc-dev, maddy, mpe

Hi,

Power11 PHYP firmware adds Virtual Ethernet multi-queue (MQ) RX for
the ibmveth device: multiple logical-LAN RX queues, per-queue buffer
posting, and completion delivery. Guest Linux did not use that
platform support; ibmveth still registered one RX queue even when
PHYP was MQ-capable.

This series adds the ibmveth MQ client. When PHYP advertises the
capability through H_ILLAN_ATTRIBUTES, the driver registers
multiple RX queues, receives on per-queue NAPI, and exposes queue
count through ethtool. Older firmware without the bit is unchanged.
Please apply to net-next.

Background
ibmveth today registers one logical LAN, one set of buffer pools, and
one NAPI context. PHYP MQ mode gives each RX queue its own handle:
buffers are posted with H_ADD_LOGICAL_LAN_BUFFERS_QUEUE, subordinate
queues register through H_REG_LOGICAL_LAN_QUEUE, and traffic can
land on any active queue. Queue selection is firmware-defined; v1
does not program RSS or hash tables. The driver needs per-queue
pools, IRQs, and poll state to match.

Queue-aware hcalls are selected only when probe sets multi_queue
from H_ILLAN_ATTRIBUTES; legacy firmware keeps the original hcall
path unchanged through the entire series.

This splits the work so review follows the actual bring-up sequence:
  1. Hypercall definitions and MQ data structures (patches 1-2)
  2. Refactor open/close into helpers - RX, per-queue pools,
     IRQ, TX, PHYP (3-9)
  3. Turn on the MQ datapath at probe/open (10)
  4. Per-queue RX/TX stats and sysfs pool readout (11-12)
  5. Runtime RX queue resize via ethtool -L (13-14)
  6. LPAR stability fix (15)

- Helper patches (3-9) reshape ibmveth_open()/close() into
queue-aware helpers. Runtime behaviour is unchanged through that
block: num_rx_queues stays 1 and multi_queue is false until patch 10.
- Patch 10 is the switch: probe sets multi_queue from firmware, raises
num_rx_queues, registers subordinates, and replenishes every active
queue.
- Patch 15 fixes poll hangs after aggressive ethtool -L cycling and
NAPI/close deadlocks on ip link down.

Testing
Tested on ppc64le PowerVM LPAR with MQ-capable firmware:
* Aggressive ethtool -L cycling (16/1/8/11/1/3/16/8/1) with ping
* MQ path: ethtool -L under iperf3 load, link down/up during traffic
* Legacy firmware (no MQ bit): full open/close/stress on the
  refactored helper path to confirm single-queue behaviour is
  unchanged

Changes in v2
v1 resubmit as 15 patches (Patchwork limit): same code and LPAR testing;
squashed split plus checkpatch fixes in patch 15 only.
v1: https://lore.kernel.org/r/cover.1782758799.git.mmc@linux.ibm.com
Patchwork: https://patchwork.kernel.org/project/netdevbpf/list/?series=1119106

Future work
* IRQ affinity hints for subordinate queue IRQs returned by PHYP
* Summed global no_buffer drop counter across all RX queues in MQ mode

Comments and suggestions on patch split, design, and testing are
welcome.

Mingming Cao <mmc@linux.ibm.com>

Mingming Cao (15):
  ibmveth: Refactor RX resource allocation for MQ RX bring-up
  ibmveth: Refactor buffer pool management for per-queue MQ RX
  ibmveth: Refactor RX interrupt control for MQ RX queues
  ibmveth: Refactor TX resource allocation in open/close paths
  ibmveth: Add RX queue register/deregister helpers for MQ
  ibmveth: Refactor open/close into MQ-ready resource pipeline
  ibmveth: Add queue-aware RX buffer submit helper for MQ
  ibmveth: Enable multi-queue RX receive path
  ibmveth: Add per-queue RX statistics collection and reporting
  ibmveth: Add per-queue TX statistics reporting
  ibmveth: Expose per-queue buffer pool details via sysfs
  ibmveth: Add helpers for incremental MQ RX queue resize
  ibmveth: Implement incremental MQ RX queue resize
  ibmveth: Wire ethtool set_channels to MQ RX queue resize
  ibmveth: Fix MQ RX poll and shutdown hangs after queue resize

 drivers/net/ethernet/ibm/ibmveth.c | 2350 +++++++++++++++++++++++-----
 drivers/net/ethernet/ibm/ibmveth.h |   25 +-
 2 files changed, 2014 insertions(+), 361 deletions(-)



^ permalink raw reply

* Re: [PATCH] perf data convert json: Fix trace_seq memory leak in process_sample_event()
From: Namhyung Kim @ 2026-07-01 21:20 UTC (permalink / raw)
  To: Tanushree Shah
  Cc: Arnaldo Carvalho de Melo, jolsa, adrian.hunter, vmolnaro, mpetlan,
	tmricht, maddy, irogers, linux-perf-users, linuxppc-dev, atrajeev,
	hbathini, Tejas.Manhas1, Tanushree.Shah, Shivani.Nittor
In-Reply-To: <f6937d04-6346-443b-8054-6f8a801a4fd8@linux.ibm.com>

Hello,

On Thu, Jun 11, 2026 at 05:23:45PM +0530, Tanushree Shah wrote:
> Hello Arnaldo
> 
> Thanks for checking the patch. I cannot see this patch applied on
> perf-tools-next yet.

According to the other comment, I guess you're supposed to send v2.  No?

Thanks,
Namhyung



^ permalink raw reply

* Re: [PATCH 3/4] powerpc/numa: set node_possible_map from node_online_map
From: Yury Norov @ 2026-07-01 20:55 UTC (permalink / raw)
  To: Sang-Heon Jeon
  Cc: akpm, Madhavan Srinivasan, Michael Ellerman, linux-mm,
	Andreas Larsson, Christophe Leroy (CS GROUP), David S. Miller,
	John Paul Adrian Glaubitz, linux-kernel, linuxppc-dev,
	Nicholas Piggin, Rich Felker, Vlastimil Babka, Yoshinori Sato,
	Yury Norov
In-Reply-To: <20260701171851.2447626-4-ekffu200098@gmail.com>

On Thu, Jul 02, 2026 at 02:18:50AM +0900, Sang-Heon Jeon wrote:
> mem_topology_setup() intersects node_possible_map with node_online_map.
> Nothing sets node_possible_map before this, so it is NODE_MASK_ALL and
> the result is just node_online_map.
> 
> In preparation for changing node_possible_map's initial value,
> mem_topology_setup() no longer depends on it.
> 
> No functional change.
> 
> Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
> ---
>  arch/powerpc/mm/numa.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
> index f4cf3ae036de..2fdecae90a01 100644
> --- a/arch/powerpc/mm/numa.c
> +++ b/arch/powerpc/mm/numa.c
> @@ -1179,7 +1179,7 @@ void __init mem_topology_setup(void)
>  	 * that we expect to make use of for this platform's affinity
>  	 * calculations.
>  	 */
> -	nodes_and(node_possible_map, node_possible_map, node_online_map);
> +	node_possible_map = node_online_map;
>  
>  	find_possible_nodes();

And in find_possible_nodes():

         for (i = 0; i < max_nodes; i++) {
                 if (!node_possible(i))
                         node_set(i, node_possible_map);
         }

So, assuming the code reaches to that point, you unconditionally
online all the offlined nodes.

This doesn't look correct at the first glance, and definitely it's not
a "No functional change" type of things.


^ permalink raw reply

* Re: [PATCH] powerpc/64s: Clarify copy_and_flush() cache sync loop comment
From: Aditya Gupta @ 2026-07-01 18:38 UTC (permalink / raw)
  To: Nikhil Kumar Singh, linuxppc-dev
  Cc: linux-kernel, maddy, mpe, npiggin, chleroy, mahesh
In-Reply-To: <20260701182756.273019-1-nikhilks@linux.ibm.com>


On 01/07/26 23:57, Nikhil Kumar Singh wrote:
> The value loaded into r0 in copy_and_flush() represents the number of
> 8-byte words processed between cache synchronization operations.
>
> The existing comment refers to cache line size, which can make it appear
> that the value is a cache line size in bytes rather than a loop count.
> Clarify the comment to explain that the loop processes 8 words (64 bytes)
> per cache synchronization iteration, and that increasing the value would
> skip cache maintenance for intermediate cache lines.
>
> This is a comment-only change with no functional impact.
>
> Signed-off-by: Nikhil Kumar Singh <nikhilks@linux.ibm.com>
> ---
>   arch/powerpc/kernel/head_64.S | 16 ++++++++--------
>   1 file changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
> index 63432a33ec49..e21c2bce8f7e 100644
> --- a/arch/powerpc/kernel/head_64.S
> +++ b/arch/powerpc/kernel/head_64.S
> @@ -713,14 +713,14 @@ p_end: .8byte _end - copy_to_here
>   _GLOBAL(copy_and_flush)
>   	addi	r5,r5,-8
>   	addi	r6,r6,-8
> -4:	li	r0,8			/* Use the smallest common	*/
> -					/* denominator cache line	*/
> -					/* size.  This results in	*/
> -					/* extra cache line flushes	*/
> -					/* but operation is correct.	*/
> -					/* Can't get cache line size	*/
> -					/* from NACA as it is being	*/
> -					/* moved too.			*/
> +4:	li	r0,8			/* r0 is the number of 8-byte words       */
> +					/* to copy per cache sync iteration.      */
> +					/* 8 words * 8 bytes = 64 bytes. 64B is   */
> +					/* the current default cache line size.   */
> +					/* This is a loop count, not a byte       */
> +					/* count. Increasing it will skip         */
> +					/* dcbst/icbi for lines in between and    */
> +					/* leave stale instructions in icache.    */

Looks good to me, previous comment stating cache line size may make it 
look like
r0 is number of bytes being copied which isn't the case when the code is 
read.
Hence:

Reviewed-by: Aditya Gupta <adityag@linux.ibm.com>


Thanks,
- Aditya G

>   
>   	mtctr	r0			/* put # words/line in ctr	*/
>   3:	addi	r6,r6,8			/* copy a cache line		*/


^ permalink raw reply

* [PATCH v2 1/1] KVM: powerpc: Use generic xfer to guest work function
From: Vishal Chourasia @ 2026-07-01 18:30 UTC (permalink / raw)
  To: maddy
  Cc: npiggin, mpe, chleroy, sshegde, amachhiw, vaibhav, harshpb,
	gautam, linuxppc-dev, kvm, linux-kernel, Vishal Chourasia
In-Reply-To: <20260701183030.3610451-1-vishalc@linux.ibm.com>

Since commit 2cd571245b43 ("sched/fair: Add related data structure for
task based throttle") in v6.18, CFS bandwidth throttling no longer
dequeues a task directly; it queues task_work via TWA_RESUME and sets
TIF_NOTIFY_RESUME, relying on that work running before the task returns
to guest/user mode. The powerpc KVM run loops only checked for reschedule
and signals, never TIF_NOTIFY_RESUME, so the deferred throttle never ran
while a vCPU stayed in the run loop: a CPU-bound guest that rarely exits
to userspace ran far past its cpu.max quota and then appeared frozen for
minutes while the accrued throttle debt was repaid.

Use the generic infrastructure to check for and handle pending work
before transitioning into guest mode, replacing the open-coded
need_resched() and cond_resched() checks in the Book3S HV run loops and
in the common kvmppc_prepare_to_enter() used by the Book3S PR and BookE
run loops. The redundant signal_pending() recheck (and its sigpend label)
in kvmhv_run_single_vcpu() is also dropped, as
xfer_to_guest_mode_work_pending() is a superset of it.

This picks up handling for TIF_NOTIFY_RESUME, which was previously
ignored, meaning task work will now be correctly handled on every
guest re-entry.

In kvmppc_prepare_to_enter() the generic helper accounts the signal exit
(vcpu->stat.signal_exits and KVM_EXIT_INTR) but does not set the exit
type, so kvmppc_set_exit_type(SIGNAL_EXITS) is retained on the signal
path to preserve the E500 CONFIG_KVM_EXIT_TIMING histogram; it is a no-op
otherwise.

Signed-off-by: Vishal Chourasia <vishalc@linux.ibm.com>
---
 arch/powerpc/kvm/Kconfig     |  1 +
 arch/powerpc/kvm/book3s_hv.c | 64 ++++++++++++++++++++++++++++--------
 arch/powerpc/kvm/powerpc.c   | 34 ++++++++++++++-----
 3 files changed, 77 insertions(+), 22 deletions(-)

diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
index 9a0d1c1aca6c..b6bc2fc86dca 100644
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -22,6 +22,7 @@ config KVM
 	select KVM_COMMON
 	select KVM_VFIO
 	select HAVE_KVM_IRQ_BYPASS
+	select VIRT_XFER_TO_GUEST_WORK
 
 config KVM_BOOK3S_HANDLER
 	bool
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 61dbeea317f3..a1b2077561bb 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -3850,10 +3850,20 @@ static noinline void kvmppc_run_core(struct kvmppc_vcore *vc)
 	 * and return without going into the guest(s).
 	 * If the mmu_ready flag has been cleared, don't go into the
 	 * guest because that means a HPT resize operation is in progress.
+	 *
+	 * xfer_to_guest_mode_work_pending() is the IRQs-disabled recheck for
+	 * pending guest-mode work (reschedule, signals, and TIF_NOTIFY_RESUME
+	 * task_work such as the deferred CFS throttle). It is the pre-POWER9
+	 * analog of the final gate in kvmhv_run_single_vcpu(), and a superset
+	 * of the old need_resched() check: it catches work that raced in after
+	 * the drain in kvmppc_run_vcpu(), so a CPU-bound vCPU is throttled here
+	 * instead of running one more guest dispatch past its quota. IRQs are
+	 * hard-disabled just above, so the non-__ variant (which asserts that)
+	 * is the correct one.
 	 */
 	local_irq_disable();
 	hard_irq_disable();
-	if (lazy_irq_pending() || need_resched() ||
+	if (lazy_irq_pending() || xfer_to_guest_mode_work_pending() ||
 	    recheck_signals_and_mmu(&core_info)) {
 		local_irq_enable();
 		vc->vcore_state = VCORE_INACTIVE;
@@ -4824,10 +4834,24 @@ static int kvmppc_run_vcpu(struct kvm_vcpu *vcpu)
 		vc->runner = vcpu;
 		if (n_ceded == vc->n_runnable) {
 			kvmppc_vcore_blocked(vc);
-		} else if (need_resched()) {
+		} else if (__xfer_to_guest_mode_work_pending()) {
 			kvmppc_vcore_preempt(vc);
-			/* Let something else run */
-			cond_resched_lock(&vc->lock);
+			/*
+			 * Let something else run, and run pending guest-mode
+			 * work (reschedule, and TIF_NOTIFY_RESUME task_work such
+			 * as the deferred CFS throttle) before we would re-enter
+			 * the guest, so a CPU-bound vCPU is actually throttled
+			 * here instead of running past its quota. This is a
+			 * superset of the old need_resched() check. Use the raw
+			 * helper, not the kvm_ wrapper: signals (KVM_EXIT_INTR
+			 * and the signal_exits stat) are accounted by this path's
+			 * existing handling below, so going through the wrapper
+			 * here would double-count them. The helper may schedule(),
+			 * so the vcore lock is dropped around it.
+			 */
+			spin_unlock(&vc->lock);
+			xfer_to_guest_mode_handle_work();
+			spin_lock(&vc->lock);
 			if (vc->vcore_state == VCORE_PREEMPT)
 				kvmppc_vcore_end_preempt(vc);
 		} else {
@@ -4899,8 +4923,21 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit,
 		}
 	}
 
-	if (need_resched())
-		cond_resched();
+	/*
+	 * Run pending work before (re-)entering the guest, most importantly
+	 * task_work queued via TWA_RESUME (e.g. the deferred CFS bandwidth
+	 * throttle, which only sets TIF_NOTIFY_RESUME). Without this a CPU-bound
+	 * vCPU that keeps returning RESUME_GUEST never reaches an exit-to-user
+	 * point, so the throttle is never enforced and the task runs far beyond
+	 * its quota. The helper also handles reschedule and signals, replacing
+	 * the cond_resched() that was here. It may schedule(), so it runs before
+	 * preemption and IRQs are disabled, with no vcore/KVM locks held. This
+	 * is the per-reentry site shared by the bare-metal and pseries (nested)
+	 * paths, so both are covered.
+	 */
+	r = kvm_xfer_to_guest_mode_handle_work(vcpu);
+	if (r)	/* -EINTR: signal pending, exit to userspace (KVM_EXIT_INTR) */
+		return r;
 
 	kvmppc_update_vpas(vcpu);
 
@@ -4914,9 +4951,14 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit,
 
 	vcpu->arch.state = KVMPPC_VCPU_RUNNABLE;
 
-	if (signal_pending(current))
-		goto sigpend;
-	if (need_resched() || !kvm->arch.mmu_ready)
+	/*
+	 * Final IRQs-disabled check for pending guest-mode work or an MMU that
+	 * is not ready. IRQs are disabled here, so bail to the outer loop,
+	 * which re-enters and handles the pending work via
+	 * kvm_xfer_to_guest_mode_handle_work() above (exiting with -EINTR on a
+	 * signal).
+	 */
+	if (xfer_to_guest_mode_work_pending() || !kvm->arch.mmu_ready)
 		goto out;
 
 	vcpu->cpu = pcpu;
@@ -5068,10 +5110,6 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu *vcpu, u64 time_limit,
 
 	return vcpu->arch.ret;
 
- sigpend:
-	vcpu->stat.signal_exits++;
-	run->exit_reason = KVM_EXIT_INTR;
-	vcpu->arch.ret = -EINTR;
  out:
 	vcpu->cpu = -1;
 	vcpu->arch.thread_cpu = -1;
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 00302399fc37..ff1a9a8de5e0 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -84,20 +84,36 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
 	hard_irq_disable();
 
 	while (true) {
-		if (need_resched()) {
+		if (__xfer_to_guest_mode_work_pending()) {
+			/*
+			 * Handle pending guest-mode work before entering the
+			 * guest: reschedule, signals, and TIF_NOTIFY_RESUME
+			 * task_work such as the deferred CFS bandwidth throttle.
+			 * The helper must run with interrupts enabled and may
+			 * schedule(). This is a superset of the open-coded
+			 * need_resched()/signal_pending() checks it replaces. On
+			 * a pending signal it returns -EINTR after
+			 * kvm_xfer_to_guest_mode_handle_work() has set
+			 * run->exit_reason (KVM_EXIT_INTR) and bumped
+			 * vcpu->stat.signal_exits, so just return to userspace.
+			 */
 			local_irq_enable();
-			cond_resched();
+			r = kvm_xfer_to_guest_mode_handle_work(vcpu);
 			hard_irq_disable();
+			if (r) {
+				/*
+				 * -EINTR: the generic helper does not set the
+				 * exit type, so record it here for the E500
+				 * CONFIG_KVM_EXIT_TIMING histogram (a no-op
+				 * otherwise). Only the exit type is set;
+				 * signal_exits was already accounted above.
+				 */
+				kvmppc_set_exit_type(vcpu, SIGNAL_EXITS);
+				break;
+			}
 			continue;
 		}
 
-		if (signal_pending(current)) {
-			kvmppc_account_exit(vcpu, SIGNAL_EXITS);
-			vcpu->run->exit_reason = KVM_EXIT_INTR;
-			r = -EINTR;
-			break;
-		}
-
 		vcpu->mode = IN_GUEST_MODE;
 
 		/*
-- 
2.54.0



^ permalink raw reply related

* Re: [RFC] entry: Untangle the return value of syscall_enter_from_user_mode from syscall NR
From: H. Peter Anvin @ 2026-07-01 18:29 UTC (permalink / raw)
  To: Michal Suchánek, Peter Zijlstra
  Cc: Jonathan Corbet, Shuah Khan, Huacai Chen, WANG Xuerui,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev, Christian Borntraeger, Sven Schnelle,
	Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, Andrew Donnellan, Mark Rutland, Arnd Bergmann,
	Jiaxun Yang, Ryan Roberts, Greg Kroah-Hartman,
	Mukesh Kumar Chaurasiya, Shrikanth Hegde, Zong Li, Nam Cao,
	Deepak Gupta, Lukas Gerlach, Rui Qi, Kees Cook, linux-doc,
	linux-kernel, loongarch, linuxppc-dev, linux-riscv, linux-s390
In-Reply-To: <akVRcPsD_R_CE1qW@kunlun.suse.cz>

On July 1, 2026 10:42:08 AM PDT, "Michal Suchánek" <msuchanek@suse.de> wrote:
>The return value of syscall_enter_from_user_mode is used both for the
>adjusted syscall number and the indicator that a syscall should be
>skipped.
>
>As seccomp can be invoked on any syscall, including invalid ones this
>somewhat undermines seccomp.
>
>While the seccomp variants that terminate the process do not need to
>care about this for the filter that sets the syscall return value this
>disctinction is required.
>
>Pass the syscall number as a pointer to the inline entry functions, and
>use the return value exclusively for the indication that the syscall is
>already handled.
>
>This should avoid the need for the s390 PIF_SYSCALL_RET_SET which is the
>workaround for exactly this deficiency.
>
>If this is desirable the patch could be split into some series that
>adjusts the code flow where needed so that the final change is mostly
>mechanical.
>
>There is also another way to handle this problem.
>
>With x86 using bit 30 to denote compatibility syscall it sounds like
>declaring syscall number a 30bit quantity would work.
>
>Then bit 31 could be used to denote an invalid syscall that can never be
>executed, and the -1 returned from syscall_enter_from_user_mode would
>then be inherently invalid.
>
>That is so long as no architectures use syscall numbers outside of this
>range so far, and the limitation is considered fine.
>
>Signed-off-by: Michal Suchánek <msuchanek@suse.de>
>---
> Documentation/core-api/entry.rst | 12 ++++++----
> arch/loongarch/kernel/syscall.c  |  6 ++---
> arch/powerpc/kernel/syscall.c    |  3 ++-
> arch/riscv/kernel/traps.c        |  6 ++---
> arch/s390/kernel/syscall.c       |  6 ++---
> arch/x86/entry/syscall_32.c      | 39 +++++++++++++++-----------------
> arch/x86/entry/syscall_64.c      | 19 ++++++++--------
> include/linux/entry-common.h     | 38 ++++++++++++++-----------------
> 8 files changed, 63 insertions(+), 66 deletions(-)
>
>diff --git a/Documentation/core-api/entry.rst b/Documentation/core-api/entry.rst
>index 71d8eedc0549..b0bfae31fe7c 100644
>--- a/Documentation/core-api/entry.rst
>+++ b/Documentation/core-api/entry.rst
>@@ -68,12 +68,14 @@ invoked from low-level assembly code looks like this:
>   noinstr void syscall(struct pt_regs *regs, int nr)
>   {
> 	arch_syscall_enter(regs);
>-	nr = syscall_enter_from_user_mode(regs, nr);
> 
>-	instrumentation_begin();
>-	if (!invoke_syscall(regs, nr) && nr != -1)
>-	 	result_reg(regs) = __sys_ni_syscall(regs);
>-	instrumentation_end();
>+	/* Skip syscall when -1 is returned */
>+	if (!syscall_enter_from_user_mode(regs, &nr)) {
>+		instrumentation_begin();
>+		if (!invoke_syscall(regs, nr) && nr != -1)
>+			result_reg(regs) = __sys_ni_syscall(regs);
>+		instrumentation_end();
>+	}
> 
> 	syscall_exit_to_user_mode(regs);
>   }
>diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c
>index 94c1c3b5b0b5..fc18ac56b91c 100644
>--- a/arch/loongarch/kernel/syscall.c
>+++ b/arch/loongarch/kernel/syscall.c
>@@ -58,7 +58,7 @@ typedef long (*sys_call_fn)(unsigned long, unsigned long,
> 
> void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
> {
>-	unsigned long nr;
>+	unsigned long nr, ret;
> 	sys_call_fn syscall_fn;
> 
> 	nr = regs->regs[11];
>@@ -70,11 +70,11 @@ void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
> 	regs->orig_a0 = regs->regs[4];
> 	regs->regs[4] = -ENOSYS;
> 
>-	nr = syscall_enter_from_user_mode(regs, nr);
>+	ret = syscall_enter_from_user_mode(regs, &nr);
> 
> 	add_random_kstack_offset();
> 
>-	if (nr < NR_syscalls) {
>+	if (nr < NR_syscalls && !ret) {
> 		syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
> 		regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
> 					   regs->regs[7], regs->regs[8], regs->regs[9]);
>diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
>index a9da2af6efa8..45d11d518c51 100644
>--- a/arch/powerpc/kernel/syscall.c
>+++ b/arch/powerpc/kernel/syscall.c
>@@ -20,7 +20,8 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
> 	syscall_fn f;
> 
> 	add_random_kstack_offset();
>-	r0 = syscall_enter_from_user_mode(regs, r0);
>+	if (unlikely(syscall_enter_from_user_mode(regs, &r0)))
>+		return syscall_get_error(current, regs);
> 
> 	if (unlikely(r0 >= NR_syscalls)) {
> 		if (unlikely(trap_is_unsupported_scv(regs))) {
>diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
>index 8c62c771a656..9326a4a50696 100644
>--- a/arch/riscv/kernel/traps.c
>+++ b/arch/riscv/kernel/traps.c
>@@ -325,7 +325,7 @@ asmlinkage __visible __trap_section  __no_stack_protector
> void do_trap_ecall_u(struct pt_regs *regs)
> {
> 	if (user_mode(regs)) {
>-		long syscall = regs->a7;
>+		long ret, syscall = regs->a7;
> 
> 		regs->epc += 4;
> 		regs->orig_a0 = regs->a0;
>@@ -333,11 +333,11 @@ void do_trap_ecall_u(struct pt_regs *regs)
> 
> 		riscv_v_vstate_discard(regs);
> 
>-		syscall = syscall_enter_from_user_mode(regs, syscall);
>+		ret = syscall_enter_from_user_mode(regs, &syscall);
> 
> 		add_random_kstack_offset();
> 
>-		if (syscall >= 0 && syscall < NR_syscalls) {
>+		if (syscall >= 0 && syscall < NR_syscalls && !ret) {
> 			syscall = array_index_nospec(syscall, NR_syscalls);
> 			syscall_handler(regs, syscall);
> 		}
>diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c
>index 75d5a3cab14e..9e5b873c011d 100644
>--- a/arch/s390/kernel/syscall.c
>+++ b/arch/s390/kernel/syscall.c
>@@ -95,7 +95,7 @@ SYSCALL_DEFINE0(ni_syscall)
> 
> void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
> {
>-	unsigned long nr;
>+	unsigned long nr, ret;
> 
> 	enter_from_user_mode(regs);
> 	add_random_kstack_offset();
>@@ -121,7 +121,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
> 		regs->psw.addr = current->restart_block.arch_data;
> 		current->restart_block.arch_data = 1;
> 	}
>-	nr = syscall_enter_from_user_mode_work(regs, nr);
>+	ret = syscall_enter_from_user_mode_work(regs, &nr);
> 	/*
> 	 * In the s390 ptrace ABI, both the syscall number and the return value
> 	 * use gpr2. However, userspace puts the syscall number either in the
>@@ -129,7 +129,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
> 	 * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
> 	 * and if set, the syscall will be skipped.
> 	 */
>-	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
>+	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || ret))
> 		goto out;
> 	regs->gprs[2] = -ENOSYS;
> 	if (likely(nr < NR_syscalls)) {
>diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c
>index 31b9492fe851..525e99691b31 100644
>--- a/arch/x86/entry/syscall_32.c
>+++ b/arch/x86/entry/syscall_32.c
>@@ -128,7 +128,7 @@ static __always_inline bool int80_is_external(void)
>  */
> __visible noinstr void do_int80_emulation(struct pt_regs *regs)
> {
>-	int nr;
>+	long nr;
> 
> 	/* Kernel does not use INT $0x80! */
> 	if (unlikely(!user_mode(regs))) {
>@@ -168,8 +168,7 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
> 	nr = syscall_32_enter(regs);
> 
> 	local_irq_enable();
>-	nr = syscall_enter_from_user_mode_work(regs, nr);
>-	do_syscall_32_irqs_on(regs, nr);
>+	syscall_enter_from_user_mode_work(regs, &nr);
> 
> 	instrumentation_end();
> 	syscall_exit_to_user_mode(regs);
>@@ -208,7 +207,7 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
>  */
> DEFINE_FREDENTRY_RAW(int80_emulation)
> {
>-	int nr;
>+	long nr;
> 
> 	enter_from_user_mode(regs);
> 
>@@ -232,8 +231,10 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
> 	nr = syscall_32_enter(regs);
> 
> 	local_irq_enable();
>-	nr = syscall_enter_from_user_mode_work(regs, nr);
>-	do_syscall_32_irqs_on(regs, nr);
>+	if (!syscall_enter_from_user_mode_work(regs, &nr)) {
>+		nr &= GENMASK(31, 0);
>+		do_syscall_32_irqs_on(regs, nr);
>+	}
> 
> 	instrumentation_end();
> 	syscall_exit_to_user_mode(regs);
>@@ -245,20 +246,17 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
> /* Handles int $0x80 on a 32bit kernel */
> __visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
> {
>-	int nr = syscall_32_enter(regs);
>-
>-	/*
>-	 * Subtlety here: if ptrace pokes something larger than 2^31-1 into
>-	 * orig_ax, the int return value truncates it. This matches
>-	 * the semantics of syscall_get_nr().
>-	 */
>-	nr = syscall_enter_from_user_mode(regs, nr);
>-	instrumentation_begin();
>+	long nr = syscall_32_enter(regs);
> 
> 	add_random_kstack_offset();
>-	do_syscall_32_irqs_on(regs, nr);
>+	if (!syscall_enter_from_user_mode(regs, &nr)) {
>+		instrumentation_begin();
> 
>-	instrumentation_end();
>+		nr &= & GENMASK(31, 0);
>+		do_syscall_32_irqs_on(regs, nr);
>+
>+		instrumentation_end();
>+	}
> 	syscall_exit_to_user_mode(regs);
> }
> #endif /* !CONFIG_IA32_EMULATION */
>@@ -301,10 +299,9 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
> 		return false;
> 	}
> 
>-	nr = syscall_enter_from_user_mode_work(regs, nr);
>-
>-	/* Now this is just like a normal syscall. */
>-	do_syscall_32_irqs_on(regs, nr);
>+	if (!syscall_enter_from_user_mode_work(regs, &nr))
>+		/* Now this is just like a normal syscall. */
>+		do_syscall_32_irqs_on(regs, nr);
> 
> 	instrumentation_end();
> 	syscall_exit_to_user_mode(regs);
>diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c
>index 71f032504e73..3400c2f43a62 100644
>--- a/arch/x86/entry/syscall_64.c
>+++ b/arch/x86/entry/syscall_64.c
>@@ -84,19 +84,20 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
> }
> 
> /* Returns true to return using SYSRET, or false to use IRET */
>-__visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
>+__visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
> {
>-	nr = syscall_enter_from_user_mode(regs, nr);
>-
>-	instrumentation_begin();
> 	add_random_kstack_offset();
>+	if (!syscall_enter_from_user_mode(regs, &nr)) {
> 
>-	if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
>-		/* Invalid system call, but still a system call. */
>-		regs->ax = __x64_sys_ni_syscall(regs);
>-	}
>+		instrumentation_begin();
> 
>-	instrumentation_end();
>+		if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr)) {
>+			/* Invalid system call, but still a system call. */
>+			regs->ax = __x64_sys_ni_syscall(regs);
>+		}
>+
>+		instrumentation_end();
>+	}
> 	syscall_exit_to_user_mode(regs);
> 
> 	/*
>diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
>index 416a3352261f..4991071d01fe 100644
>--- a/include/linux/entry-common.h
>+++ b/include/linux/entry-common.h
>@@ -69,10 +69,8 @@ static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
> 	}
> }
> 
>-static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work)
>+static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work, unsigned long *syscall)
> {
>-	long syscall, ret = 0;
>-
> 	/*
> 	 * Handle Syscall User Dispatch.  This must comes first, since
> 	 * the ABI here can be something that doesn't make sense for
>@@ -93,27 +91,25 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
> 
> 	/* Handle ptrace */
> 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
>-		ret = arch_ptrace_report_syscall_entry(regs);
>-		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
>+		if (arch_ptrace_report_syscall_entry(regs) || (work & SYSCALL_WORK_SYSCALL_EMU))
> 			return -1L;
> 	}
> 
> 	/* Do seccomp after ptrace, to catch any tracer changes. */
> 	if (work & SYSCALL_WORK_SECCOMP) {
>-		ret = __secure_computing();
>-		if (ret == -1L)
>-			return ret;
>+		if (__secure_computing())
>+			return -1L;
> 	}
> 
> 	/* Either of the above might have changed the syscall number */
>-	syscall = syscall_get_nr(current, regs);
>+	*syscall = syscall_get_nr(current, regs);
> 
> 	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT))
>-		syscall = trace_syscall_enter(regs, syscall);
>+		*syscall = trace_syscall_enter(regs, *syscall);
> 
>-	syscall_enter_audit(regs, syscall);
>+	syscall_enter_audit(regs, *syscall);
> 
>-	return ret ? : syscall;
>+	return 0;
> }
> 
> /**
>@@ -126,12 +122,12 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
>  * enabled after invoking enter_from_user_mode(), enabling interrupts and
>  * extra architecture specific work.
>  *
>- * Returns: The original or a modified syscall number
>+ * Returns: The original or a modified syscall number as syscall
>  *
>- * If the returned syscall number is -1 then the syscall should be
>- * skipped. In this case the caller may invoke syscall_set_error() or
>- * syscall_set_return_value() first.  If neither of those are called and -1
>- * is returned, then the syscall will fail with ENOSYS.
>+ * If the returned value is -1 then the syscall should be skipped. In this case
>+ * the caller may invoke syscall_set_error() or syscall_set_return_value()
>+ * first.  If neither of those are called and -1 is returned, then the syscall
>+ * will fail with ENOSYS.
>  *
>  * It handles the following work items:
>  *
>@@ -139,14 +135,14 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
>  *     ptrace_report_syscall_entry(), __secure_computing(), trace_sys_enter()
>  *  2) Invocation of audit_syscall_entry()
>  */
>-static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
>+static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long *syscall)
> {
> 	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
> 
> 	if (work & SYSCALL_WORK_ENTER)
>-		syscall = syscall_trace_enter(regs, work);
>+		return syscall_trace_enter(regs, work, syscall);
> 
>-	return syscall;
>+	return 0;
> }
> 
> /**
>@@ -167,7 +163,7 @@ static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *re
>  * Returns: The original or a modified syscall number. See
>  * syscall_enter_from_user_mode_work() for further explanation.
>  */
>-static __always_inline long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
>+static __always_inline long syscall_enter_from_user_mode(struct pt_regs *regs, long *syscall)
> {
> 	long ret;
> 

Negative numbers most definitely not be assigned as valid system calls, not now, not ever. 

Therein lies some serious madness.

I believe setting the syscall number to -1 to skip is an ABI already in e.g. ptrace, so I doubt we can just get rid of it anyway. 

I would say as follows:

Let's formally define that: 

- valid system call numbers are positive 32-bit numbers, using the appropriate ABI convention for "int".

- bits [30:n] for some value of n are reserved for architecture-specific flags/modes. MIPS uses an offset of 2000 decimal between its syscall ABIs, which would imply n ~ 11, although I personally think that is too restrictive (MIPS could in fact use such a flag to provide an escape into a larger number space if we ever need more than 2000 system calls.)

I would suggest n = 24, at least for now. It is easier to give up additional bits later than to claw them back when already used. 

Thus: 

1. The type for a system call is int.

2. A valid system call number is always going to be positive.

3. Bits [30:24] are available for architecture ABI use. The "architecture independent" part of the system call number is therefore 24 bits wide.

4. The exact ABI is platform-specific, obviously, but as a general guideline (especially for new platforms/ABIs) should follow the rules for a platform "int" if practical. Notably, when passing a value in a register larger than 32 bits, which side of the calling interface is responsible for sign-extending a value passed in a register. If caller side, the kernel should validate, if callee side the kernel should ignore the additional bits and do the extension.

5. A negative system call number is guaranteed to return -ENOSYS (unless intercepted by seccomp, ptrace, or another mechanism under user space control.)

6. If the platform needs to algorithmically modify the system call number due to platform-specific concerns (say, the platform uses a 16-bit special purpose register for the syscall number, or it has multiple kernel entry points with different behavior), it should if at all possible transcode the system call number as necessary to match this convention in APIs that are exposed to general kernel code. 

For example, in the future I could very much see the IA32 code in the x86 kernel using bit 29 internally to indicate an ia32 system call, simplifying the is_compat implementation on x86. It should not mean that passing bit 29 to either the syscall instruction or int $0x80 will be accepted.



^ permalink raw reply

* [PATCH v2 0/1] KVM: powerpc: Use generic xfer to guest work function
From: Vishal Chourasia @ 2026-07-01 18:30 UTC (permalink / raw)
  To: maddy
  Cc: npiggin, mpe, chleroy, sshegde, amachhiw, vaibhav, harshpb,
	gautam, linuxppc-dev, kvm, linux-kernel, Vishal Chourasia

This series fixes a KVM scheduling bug on Book3S HV where a guest VM
under a cpu.max bandwidth limit can run arbitrarily past its quota and
then appear frozen for minutes afterwards. 

== Problem ==

Since commit 2cd571245b43 ("sched/fair: Add related data structure for
task based throttle"), merged in v6.18, CFS bandwidth throttling no
longer dequeues a task directly. Instead it queues a task_work item via
task_work_add(..., TWA_RESUME), sets TIF_NOTIFY_RESUME, and relies on
that work running on the return path to actually dequeue the task.

The powerpc KVM run loops only test TIF_SIGPENDING and TIF_NEED_RESCHED
before re-entering the guest; TIF_NOTIFY_RESUME is never checked. For a
CPU-bound guest that generates few KVM exits back to userspace, the vCPU
thread never returns to user mode, so the deferred throttle task_work
never runs. The guest keeps running unchecked while its
runtime_remaining goes increasingly negative, and once it finally does
exit to userspace it is legitimately throttled for minutes while the
accrued debt is repaid at the bandwidth-timer replenishment rate.

The generic xfer-to-guest-mode infrastructure (commit 935ace2fb5cc,
"entry: Provide infrastructure for work before transitioning to guest
mode") exists precisely to handle this kind of work before each guest
entry. A full trace-backed root-cause analysis was posted with v1 [2].

== Fix ==

Opt powerpc KVM into VIRT_XFER_TO_GUEST_WORK and use the generic
xfer_to_guest_mode helpers to check for and handle pending guest-mode
work (reschedule, signals, and TIF_NOTIFY_RESUME task_work such as the
deferred CFS throttle) on every guest re-entry:

- Book3S HV: both run loops — kvmhv_run_single_vcpu() for POWER9+ and
  kvmppc_run_vcpu() for pre-POWER9.
- Book3S PR and BookE: the common kvmppc_prepare_to_enter(), which
  likewise only checked need_resched()/signal_pending().

== Changes from v1 ==

- Extend the fix beyond Book3S HV to the shared powerpc KVM entry path:
  also convert the common kvmppc_prepare_to_enter() used by Book3S PR
  and BookE. (Shrikanth Shegde)
- Move "select VIRT_XFER_TO_GUEST_WORK" from KVM_BOOK3S_64_HV up to the
  common "config KVM" so every powerpc KVM variant gets the
  infrastructure.
- Drop the redundant signal_pending() recheck and its sigpend label in
  kvmhv_run_single_vcpu(); xfer_to_guest_mode_work_pending() is a
  superset of it.
- Preserve the E500 CONFIG_KVM_EXIT_TIMING histogram on the signal path
  via an explicit kvmppc_set_exit_type(SIGNAL_EXITS).

[1] https://lore.kernel.org/all/20250421102837.78515-2-sshegde@linux.ibm.com/
[2] https://lore.kernel.org/all/20260626105449.2897924-2-vishalc@linux.ibm.com/

Vishal Chourasia (1):
  KVM: powerpc: Use generic xfer to guest work function

 arch/powerpc/kvm/Kconfig     |  1 +
 arch/powerpc/kvm/book3s_hv.c | 64 ++++++++++++++++++++++++++++--------
 arch/powerpc/kvm/powerpc.c   | 34 ++++++++++++++-----
 3 files changed, 77 insertions(+), 22 deletions(-)

-- 
2.54.0



^ permalink raw reply

* [PATCH] powerpc/64s: Clarify copy_and_flush() cache sync loop comment
From: Nikhil Kumar Singh @ 2026-07-01 18:27 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: linux-kernel, maddy, mpe, npiggin, chleroy, adityag, mahesh,
	Nikhil Kumar Singh

The value loaded into r0 in copy_and_flush() represents the number of
8-byte words processed between cache synchronization operations.

The existing comment refers to cache line size, which can make it appear
that the value is a cache line size in bytes rather than a loop count.
Clarify the comment to explain that the loop processes 8 words (64 bytes)
per cache synchronization iteration, and that increasing the value would
skip cache maintenance for intermediate cache lines.

This is a comment-only change with no functional impact.

Signed-off-by: Nikhil Kumar Singh <nikhilks@linux.ibm.com>
---
 arch/powerpc/kernel/head_64.S | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index 63432a33ec49..e21c2bce8f7e 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -713,14 +713,14 @@ p_end: .8byte _end - copy_to_here
 _GLOBAL(copy_and_flush)
 	addi	r5,r5,-8
 	addi	r6,r6,-8
-4:	li	r0,8			/* Use the smallest common	*/
-					/* denominator cache line	*/
-					/* size.  This results in	*/
-					/* extra cache line flushes	*/
-					/* but operation is correct.	*/
-					/* Can't get cache line size	*/
-					/* from NACA as it is being	*/
-					/* moved too.			*/
+4:	li	r0,8			/* r0 is the number of 8-byte words       */
+					/* to copy per cache sync iteration.      */
+					/* 8 words * 8 bytes = 64 bytes. 64B is   */
+					/* the current default cache line size.   */
+					/* This is a loop count, not a byte       */
+					/* count. Increasing it will skip         */
+					/* dcbst/icbi for lines in between and    */
+					/* leave stale instructions in icache.    */
 
 	mtctr	r0			/* put # words/line in ctr	*/
 3:	addi	r6,r6,8			/* copy a cache line		*/
-- 
2.43.5



^ permalink raw reply related

* Re: [PATCH 1/1] KVM: powerpc/book3s_hv: Use generic xfer to guest work function
From: Vishal Chourasia @ 2026-07-01 18:04 UTC (permalink / raw)
  To: Shrikanth Hegde
  Cc: npiggin, mpe, chleroy, gautam, bigeasy, linuxppc-dev, kvm,
	linux-kernel, maddy
In-Reply-To: <ba65ad87-2d59-49c3-9b76-7068a9c0dac2@linux.ibm.com>


On 01/07/26 11:43, Shrikanth Hegde wrote:
> Hi Vishal,
Hi Shrikanth, Thanks for looking into it.
>
> On 6/26/26 4:23 PM, Vishal Chourasia wrote:
>> Use the generic infrastructure to check for and handle pending work
>> before transitioning into guest mode, replacing the open-coded
>> need_resched() and cond_resched() checks.
>>
>> This picks up handling for TIF_NOTIFY_RESUME, which was previously
>> ignored, meaning task work will now be correctly handled on every
>> guest re-entry.
Yes
>
> It would indeed be good if powerpc moves go generic 
> VIRT_XFER_TO_GUEST_WORK.
> It does take care of RESUME.
>
> In addition today, I doubt powerpc kvm works well for LAZY preemption.
> generic infra will take care of it too.
>
nice
>>
>> Signed-off-by: Vishal Chourasia <vishalc@linux.ibm.com>
>> ---
>>   arch/powerpc/kvm/Kconfig     |  1 +
>>   arch/powerpc/kvm/book3s_hv.c | 58 +++++++++++++++++++++++++++++++-----
>>   2 files changed, 52 insertions(+), 7 deletions(-)
>>
>> diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
>> index 9a0d1c1aca6c..36aec58c5f22 100644
>> --- a/arch/powerpc/kvm/Kconfig
>> +++ b/arch/powerpc/kvm/Kconfig
>> @@ -81,6 +81,7 @@ config KVM_BOOK3S_64_HV
>>       depends on KVM_BOOK3S_64 && PPC_POWERNV
>>       select KVM_BOOK3S_HV_POSSIBLE
>>       select KVM_BOOK3S_HV_PMU
>> +    select VIRT_XFER_TO_GUEST_WORK
>
> This takes care of HV only.
> Does PR/booke run into the same problem?
Yes, I would think so, if the KVM vCPU runs inside system.
> Does anyone out there who runs them still and care about cgroup 
> bandwidth control mechanism on it?
>
> It may be worth adding comment that PR still runs into the same issue.
>
>>       select CMA
>>       help
>>         Support running unmodified book3s_64 guest kernels in
>> diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
>> index 61dbeea317f3..b012512342e6 100644
>> --- a/arch/powerpc/kvm/book3s_hv.c
>> +++ b/arch/powerpc/kvm/book3s_hv.c
>> @@ -3850,10 +3850,20 @@ static noinline void kvmppc_run_core(struct 
>> kvmppc_vcore *vc)
>>        * and return without going into the guest(s).
>>        * If the mmu_ready flag has been cleared, don't go into the
>>        * guest because that means a HPT resize operation is in progress.
>> +     *
>> +     * xfer_to_guest_mode_work_pending() is the IRQs-disabled 
>> recheck for
>> +     * pending guest-mode work (reschedule, signals, and 
>> TIF_NOTIFY_RESUME
>> +     * task_work such as the deferred CFS throttle). It is the 
>> pre-POWER9
>> +     * analog of the final gate in kvmhv_run_single_vcpu(), and a 
>> superset
>> +     * of the old need_resched() check: it catches work that raced 
>> in after
>> +     * the drain in kvmppc_run_vcpu(), so a CPU-bound vCPU is 
>> throttled here
>> +     * instead of running one more guest dispatch past its quota. 
>> IRQs are
>> +     * hard-disabled just above, so the non-__ variant (which 
>> asserts that)
>> +     * is the correct one.
>>        */
>>       local_irq_disable();
>>       hard_irq_disable();
>> -    if (lazy_irq_pending() || need_resched() ||
>> +    if (lazy_irq_pending() || xfer_to_guest_mode_work_pending() ||
>>           recheck_signals_and_mmu(&core_info)) {
>>           local_irq_enable();
>>           vc->vcore_state = VCORE_INACTIVE;
>> @@ -4824,10 +4834,24 @@ static int kvmppc_run_vcpu(struct kvm_vcpu 
>> *vcpu)
>>           vc->runner = vcpu;
>>           if (n_ceded == vc->n_runnable) {
>>               kvmppc_vcore_blocked(vc);
>> -        } else if (need_resched()) {
>> +        } else if (__xfer_to_guest_mode_work_pending()) {
>>               kvmppc_vcore_preempt(vc);
>> -            /* Let something else run */
>> -            cond_resched_lock(&vc->lock);
>> +            /*
>> +             * Let something else run, and run pending guest-mode
>> +             * work (reschedule, and TIF_NOTIFY_RESUME task_work such
>> +             * as the deferred CFS throttle) before we would re-enter
>> +             * the guest, so a CPU-bound vCPU is actually throttled
>> +             * here instead of running past its quota. This is a
>> +             * superset of the old need_resched() check. Use the raw
>> +             * helper, not the kvm_ wrapper: signals (KVM_EXIT_INTR
>> +             * and the signal_exits stat) are accounted by this path's
>> +             * existing handling below, so going through the wrapper
>> +             * here would double-count them. The helper may schedule(),
>> +             * so the vcore lock is dropped around it.
>> +             */
>> +            spin_unlock(&vc->lock);
>> +            xfer_to_guest_mode_handle_work();
>> +            spin_lock(&vc->lock);
>>               if (vc->vcore_state == VCORE_PREEMPT)
>>                   kvmppc_vcore_end_preempt(vc);
>>           } else {
>> @@ -4899,8 +4923,21 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu 
>> *vcpu, u64 time_limit,
>>           }
>>       }
>>   -    if (need_resched())
>> -        cond_resched();
>> +    /*
>> +     * Run pending work before (re-)entering the guest, most 
>> importantly
>> +     * task_work queued via TWA_RESUME (e.g. the deferred CFS bandwidth
>> +     * throttle, which only sets TIF_NOTIFY_RESUME). Without this a 
>> CPU-bound
>> +     * vCPU that keeps returning RESUME_GUEST never reaches an 
>> exit-to-user
>> +     * point, so the throttle is never enforced and the task runs 
>> far beyond
>> +     * its quota. The helper also handles reschedule and signals, 
>> replacing
>> +     * the cond_resched() that was here. It may schedule(), so it 
>> runs before
>> +     * preemption and IRQs are disabled, with no vcore/KVM locks 
>> held. This
>> +     * is the per-reentry site shared by the bare-metal and pseries 
>> (nested)
>> +     * paths, so both are covered.
>> +     */
>> +    r = kvm_xfer_to_guest_mode_handle_work(vcpu);
>> +    if (r)    /* -EINTR: signal pending, exit to userspace 
>> (KVM_EXIT_INTR) */
>> +        return r;
>>         kvmppc_update_vpas(vcpu);
>>   @@ -4916,7 +4953,14 @@ int kvmhv_run_single_vcpu(struct kvm_vcpu 
>> *vcpu, u64 time_limit,
>>         if (signal_pending(current))
>>           goto sigpend;
>
> xfer_to_guest_mode_work_pending checks for signals too right?
>
> #define XFER_TO_GUEST_MODE_WORK                                         \
>         (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY | _TIF_SIGPENDING | \
>          _TIF_NOTIFY_SIGNAL | _TIF_NOTIFY_RESUME |                      \
>          ARCH_XFER_TO_GUEST_MODE_WORK)
Yes, I see originally we only had need_resched() check. will remove in v2
>
>> -    if (need_resched() || !kvm->arch.mmu_ready)
>> +    /*
>> +     * Re-check for pending guest-mode work with IRQs disabled, to 
>> catch
>> +     * anything (e.g. a TIF_NOTIFY_RESUME task_work such as the 
>> deferred CFS
>> +     * throttle) that raced in after the check above. Bail back to 
>> the outer
>> +     * loop, which re-enters here and runs the work. This is a 
>> superset of
>> +     * the previous need_resched() check.
>> +     */
>> +    if (xfer_to_guest_mode_work_pending() || !kvm->arch.mmu_ready)
>>           goto out;
>>         vcpu->cpu = pcpu;
>
> Copying from the discussion thread at that time,
>
> """
> on x86:
> kvm_arch_vcpu_ioctl_run
>     vcpu_run
>         for () {
>             .. run guest..
>             xfer_to_guest_mode_handle_work
>                 schedule
>         }
>
>
> on Powerpc:  ( taking book3s_hv flavour):
> kvm_arch_vcpu_ioctl_run
> kvmppc_vcpu_run_hv  *1
>     do while() {
>         kvmhv_run_single_vcpu or kvmppc_run_vcpu
>             -- checking for need_resched and signals and bails out *2
>     }
>
>
> *1 - checks for need resched and signals before entering guest
> *2 - checks for need resched and signals while running the guest
>
>
> This patch is addressing only *1 but it needs to address *2 as well 
> using generic framework.

Not sure about *2 (while running the guest) part. Here the patch checks 
twice before entering the guest.
Once when IRQs are not disabled and once after disabling IRQs.

> I think it is doable for books3s_hv atleast. (though might need rewrite)
do what exactly?
> """
>
>
> A few questions that comes to my mind.
>
> 1. I think you are doing for both *1 and *2 right? Is *1 necessary 
> still for powerpc?
yes, before entering the guest we check twice. Once when IRQs are not 
disabled and once after disabling IRQs.
> 2. There is a for loop. What is it doing? Does this still need similar 
> checks?
>
>         if (is_kvmppc_resume_guest(r) && 
> !kvmppc_vcpu_check_block(vcpu)) {
>                 kvmppc_set_timer(vcpu);
>
>                 prepare_to_rcuwait(wait);
>                 for (;;) {
>                         set_current_state(TASK_INTERRUPTIBLE);
>                         if (signal_pending(current)) {
>                                 vcpu->stat.signal_exits++;
>                                 run->exit_reason = KVM_EXIT_INTR;
>                                 vcpu->arch.ret = -EINTR;
>                                 break;
>                         }
>
>                         if (kvmppc_vcpu_check_block(vcpu))
>                                 break;
>
>                         trace_kvmppc_vcore_blocked(vcpu, 0);
>                         schedule();
>                         trace_kvmppc_vcore_blocked(vcpu, 1);
>                 }
>                 finish_rcuwait(wait);
>         }
>         vcpu->arch.ceded = 0;

IIUC, the vCPU has voluntarily went idle in the guest and it's
about to re-enter, but there's genuinely nothing to deliver yet.
So, it parks itself here until, something actually wake this vCPU.

I am not quite sure, if signal_pending() should be replaced with
xfer_*_pending() check, because vCPU is not re-entering the guest.

Adding the xfer_*_pending() check here will break the loop even
when other flags causing busy spins (cede/re-enter).

>
> PS: cc'ing me would have helped me to see the mail earlier since you 
> had referred
> to the patch I had sent.

Yes. I missed it.



^ permalink raw reply

* [RFC] entry: Untangle the return value of syscall_enter_from_user_mode from syscall NR
From: Michal Suchánek @ 2026-07-01 17:42 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Jonathan Corbet, Shuah Khan, Huacai Chen, WANG Xuerui,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev, Christian Borntraeger, Sven Schnelle,
	Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Andrew Donnellan, Mark Rutland,
	Michal Suchánek, Arnd Bergmann, Jiaxun Yang, Ryan Roberts,
	Greg Kroah-Hartman, Mukesh Kumar Chaurasiya, Shrikanth Hegde,
	Zong Li, Nam Cao, Deepak Gupta, Lukas Gerlach, Rui Qi, Kees Cook,
	linux-doc, linux-kernel, loongarch, linuxppc-dev, linux-riscv,
	linux-s390

The return value of syscall_enter_from_user_mode is used both for the
adjusted syscall number and the indicator that a syscall should be
skipped.

As seccomp can be invoked on any syscall, including invalid ones this
somewhat undermines seccomp.

While the seccomp variants that terminate the process do not need to
care about this for the filter that sets the syscall return value this
disctinction is required.

Pass the syscall number as a pointer to the inline entry functions, and
use the return value exclusively for the indication that the syscall is
already handled.

This should avoid the need for the s390 PIF_SYSCALL_RET_SET which is the
workaround for exactly this deficiency.

If this is desirable the patch could be split into some series that
adjusts the code flow where needed so that the final change is mostly
mechanical.

There is also another way to handle this problem.

With x86 using bit 30 to denote compatibility syscall it sounds like
declaring syscall number a 30bit quantity would work.

Then bit 31 could be used to denote an invalid syscall that can never be
executed, and the -1 returned from syscall_enter_from_user_mode would
then be inherently invalid.

That is so long as no architectures use syscall numbers outside of this
range so far, and the limitation is considered fine.

Signed-off-by: Michal Suchánek <msuchanek@suse.de>
---
 Documentation/core-api/entry.rst | 12 ++++++----
 arch/loongarch/kernel/syscall.c  |  6 ++---
 arch/powerpc/kernel/syscall.c    |  3 ++-
 arch/riscv/kernel/traps.c        |  6 ++---
 arch/s390/kernel/syscall.c       |  6 ++---
 arch/x86/entry/syscall_32.c      | 39 +++++++++++++++-----------------
 arch/x86/entry/syscall_64.c      | 19 ++++++++--------
 include/linux/entry-common.h     | 38 ++++++++++++++-----------------
 8 files changed, 63 insertions(+), 66 deletions(-)

diff --git a/Documentation/core-api/entry.rst b/Documentation/core-api/entry.rst
index 71d8eedc0549..b0bfae31fe7c 100644
--- a/Documentation/core-api/entry.rst
+++ b/Documentation/core-api/entry.rst
@@ -68,12 +68,14 @@ invoked from low-level assembly code looks like this:
   noinstr void syscall(struct pt_regs *regs, int nr)
   {
 	arch_syscall_enter(regs);
-	nr = syscall_enter_from_user_mode(regs, nr);
 
-	instrumentation_begin();
-	if (!invoke_syscall(regs, nr) && nr != -1)
-	 	result_reg(regs) = __sys_ni_syscall(regs);
-	instrumentation_end();
+	/* Skip syscall when -1 is returned */
+	if (!syscall_enter_from_user_mode(regs, &nr)) {
+		instrumentation_begin();
+		if (!invoke_syscall(regs, nr) && nr != -1)
+			result_reg(regs) = __sys_ni_syscall(regs);
+		instrumentation_end();
+	}
 
 	syscall_exit_to_user_mode(regs);
   }
diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c
index 94c1c3b5b0b5..fc18ac56b91c 100644
--- a/arch/loongarch/kernel/syscall.c
+++ b/arch/loongarch/kernel/syscall.c
@@ -58,7 +58,7 @@ typedef long (*sys_call_fn)(unsigned long, unsigned long,
 
 void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
 {
-	unsigned long nr;
+	unsigned long nr, ret;
 	sys_call_fn syscall_fn;
 
 	nr = regs->regs[11];
@@ -70,11 +70,11 @@ void noinstr __no_stack_protector do_syscall(struct pt_regs *regs)
 	regs->orig_a0 = regs->regs[4];
 	regs->regs[4] = -ENOSYS;
 
-	nr = syscall_enter_from_user_mode(regs, nr);
+	ret = syscall_enter_from_user_mode(regs, &nr);
 
 	add_random_kstack_offset();
 
-	if (nr < NR_syscalls) {
+	if (nr < NR_syscalls && !ret) {
 		syscall_fn = sys_call_table[array_index_nospec(nr, NR_syscalls)];
 		regs->regs[4] = syscall_fn(regs->orig_a0, regs->regs[5], regs->regs[6],
 					   regs->regs[7], regs->regs[8], regs->regs[9]);
diff --git a/arch/powerpc/kernel/syscall.c b/arch/powerpc/kernel/syscall.c
index a9da2af6efa8..45d11d518c51 100644
--- a/arch/powerpc/kernel/syscall.c
+++ b/arch/powerpc/kernel/syscall.c
@@ -20,7 +20,8 @@ notrace long system_call_exception(struct pt_regs *regs, unsigned long r0)
 	syscall_fn f;
 
 	add_random_kstack_offset();
-	r0 = syscall_enter_from_user_mode(regs, r0);
+	if (unlikely(syscall_enter_from_user_mode(regs, &r0)))
+		return syscall_get_error(current, regs);
 
 	if (unlikely(r0 >= NR_syscalls)) {
 		if (unlikely(trap_is_unsupported_scv(regs))) {
diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
index 8c62c771a656..9326a4a50696 100644
--- a/arch/riscv/kernel/traps.c
+++ b/arch/riscv/kernel/traps.c
@@ -325,7 +325,7 @@ asmlinkage __visible __trap_section  __no_stack_protector
 void do_trap_ecall_u(struct pt_regs *regs)
 {
 	if (user_mode(regs)) {
-		long syscall = regs->a7;
+		long ret, syscall = regs->a7;
 
 		regs->epc += 4;
 		regs->orig_a0 = regs->a0;
@@ -333,11 +333,11 @@ void do_trap_ecall_u(struct pt_regs *regs)
 
 		riscv_v_vstate_discard(regs);
 
-		syscall = syscall_enter_from_user_mode(regs, syscall);
+		ret = syscall_enter_from_user_mode(regs, &syscall);
 
 		add_random_kstack_offset();
 
-		if (syscall >= 0 && syscall < NR_syscalls) {
+		if (syscall >= 0 && syscall < NR_syscalls && !ret) {
 			syscall = array_index_nospec(syscall, NR_syscalls);
 			syscall_handler(regs, syscall);
 		}
diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c
index 75d5a3cab14e..9e5b873c011d 100644
--- a/arch/s390/kernel/syscall.c
+++ b/arch/s390/kernel/syscall.c
@@ -95,7 +95,7 @@ SYSCALL_DEFINE0(ni_syscall)
 
 void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
 {
-	unsigned long nr;
+	unsigned long nr, ret;
 
 	enter_from_user_mode(regs);
 	add_random_kstack_offset();
@@ -121,7 +121,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
 		regs->psw.addr = current->restart_block.arch_data;
 		current->restart_block.arch_data = 1;
 	}
-	nr = syscall_enter_from_user_mode_work(regs, nr);
+	ret = syscall_enter_from_user_mode_work(regs, &nr);
 	/*
 	 * In the s390 ptrace ABI, both the syscall number and the return value
 	 * use gpr2. However, userspace puts the syscall number either in the
@@ -129,7 +129,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
 	 * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
 	 * and if set, the syscall will be skipped.
 	 */
-	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
+	if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET) || ret))
 		goto out;
 	regs->gprs[2] = -ENOSYS;
 	if (likely(nr < NR_syscalls)) {
diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c
index 31b9492fe851..525e99691b31 100644
--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -128,7 +128,7 @@ static __always_inline bool int80_is_external(void)
  */
 __visible noinstr void do_int80_emulation(struct pt_regs *regs)
 {
-	int nr;
+	long nr;
 
 	/* Kernel does not use INT $0x80! */
 	if (unlikely(!user_mode(regs))) {
@@ -168,8 +168,7 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
 	nr = syscall_32_enter(regs);
 
 	local_irq_enable();
-	nr = syscall_enter_from_user_mode_work(regs, nr);
-	do_syscall_32_irqs_on(regs, nr);
+	syscall_enter_from_user_mode_work(regs, &nr);
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);
@@ -208,7 +207,7 @@ __visible noinstr void do_int80_emulation(struct pt_regs *regs)
  */
 DEFINE_FREDENTRY_RAW(int80_emulation)
 {
-	int nr;
+	long nr;
 
 	enter_from_user_mode(regs);
 
@@ -232,8 +231,10 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 	nr = syscall_32_enter(regs);
 
 	local_irq_enable();
-	nr = syscall_enter_from_user_mode_work(regs, nr);
-	do_syscall_32_irqs_on(regs, nr);
+	if (!syscall_enter_from_user_mode_work(regs, &nr)) {
+		nr &= GENMASK(31, 0);
+		do_syscall_32_irqs_on(regs, nr);
+	}
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);
@@ -245,20 +246,17 @@ DEFINE_FREDENTRY_RAW(int80_emulation)
 /* Handles int $0x80 on a 32bit kernel */
 __visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
 {
-	int nr = syscall_32_enter(regs);
-
-	/*
-	 * Subtlety here: if ptrace pokes something larger than 2^31-1 into
-	 * orig_ax, the int return value truncates it. This matches
-	 * the semantics of syscall_get_nr().
-	 */
-	nr = syscall_enter_from_user_mode(regs, nr);
-	instrumentation_begin();
+	long nr = syscall_32_enter(regs);
 
 	add_random_kstack_offset();
-	do_syscall_32_irqs_on(regs, nr);
+	if (!syscall_enter_from_user_mode(regs, &nr)) {
+		instrumentation_begin();
 
-	instrumentation_end();
+		nr &= & GENMASK(31, 0);
+		do_syscall_32_irqs_on(regs, nr);
+
+		instrumentation_end();
+	}
 	syscall_exit_to_user_mode(regs);
 }
 #endif /* !CONFIG_IA32_EMULATION */
@@ -301,10 +299,9 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
 		return false;
 	}
 
-	nr = syscall_enter_from_user_mode_work(regs, nr);
-
-	/* Now this is just like a normal syscall. */
-	do_syscall_32_irqs_on(regs, nr);
+	if (!syscall_enter_from_user_mode_work(regs, &nr))
+		/* Now this is just like a normal syscall. */
+		do_syscall_32_irqs_on(regs, nr);
 
 	instrumentation_end();
 	syscall_exit_to_user_mode(regs);
diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c
index 71f032504e73..3400c2f43a62 100644
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -84,19 +84,20 @@ static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
 }
 
 /* Returns true to return using SYSRET, or false to use IRET */
-__visible noinstr bool do_syscall_64(struct pt_regs *regs, int nr)
+__visible noinstr bool do_syscall_64(struct pt_regs *regs, long nr)
 {
-	nr = syscall_enter_from_user_mode(regs, nr);
-
-	instrumentation_begin();
 	add_random_kstack_offset();
+	if (!syscall_enter_from_user_mode(regs, &nr)) {
 
-	if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
-		/* Invalid system call, but still a system call. */
-		regs->ax = __x64_sys_ni_syscall(regs);
-	}
+		instrumentation_begin();
 
-	instrumentation_end();
+		if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr)) {
+			/* Invalid system call, but still a system call. */
+			regs->ax = __x64_sys_ni_syscall(regs);
+		}
+
+		instrumentation_end();
+	}
 	syscall_exit_to_user_mode(regs);
 
 	/*
diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
index 416a3352261f..4991071d01fe 100644
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -69,10 +69,8 @@ static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
 	}
 }
 
-static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work)
+static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned long work, unsigned long *syscall)
 {
-	long syscall, ret = 0;
-
 	/*
 	 * Handle Syscall User Dispatch.  This must comes first, since
 	 * the ABI here can be something that doesn't make sense for
@@ -93,27 +91,25 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
 
 	/* Handle ptrace */
 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
-		ret = arch_ptrace_report_syscall_entry(regs);
-		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
+		if (arch_ptrace_report_syscall_entry(regs) || (work & SYSCALL_WORK_SYSCALL_EMU))
 			return -1L;
 	}
 
 	/* Do seccomp after ptrace, to catch any tracer changes. */
 	if (work & SYSCALL_WORK_SECCOMP) {
-		ret = __secure_computing();
-		if (ret == -1L)
-			return ret;
+		if (__secure_computing())
+			return -1L;
 	}
 
 	/* Either of the above might have changed the syscall number */
-	syscall = syscall_get_nr(current, regs);
+	*syscall = syscall_get_nr(current, regs);
 
 	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT))
-		syscall = trace_syscall_enter(regs, syscall);
+		*syscall = trace_syscall_enter(regs, *syscall);
 
-	syscall_enter_audit(regs, syscall);
+	syscall_enter_audit(regs, *syscall);
 
-	return ret ? : syscall;
+	return 0;
 }
 
 /**
@@ -126,12 +122,12 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
  * enabled after invoking enter_from_user_mode(), enabling interrupts and
  * extra architecture specific work.
  *
- * Returns: The original or a modified syscall number
+ * Returns: The original or a modified syscall number as syscall
  *
- * If the returned syscall number is -1 then the syscall should be
- * skipped. In this case the caller may invoke syscall_set_error() or
- * syscall_set_return_value() first.  If neither of those are called and -1
- * is returned, then the syscall will fail with ENOSYS.
+ * If the returned value is -1 then the syscall should be skipped. In this case
+ * the caller may invoke syscall_set_error() or syscall_set_return_value()
+ * first.  If neither of those are called and -1 is returned, then the syscall
+ * will fail with ENOSYS.
  *
  * It handles the following work items:
  *
@@ -139,14 +135,14 @@ static __always_inline long syscall_trace_enter(struct pt_regs *regs, unsigned l
  *     ptrace_report_syscall_entry(), __secure_computing(), trace_sys_enter()
  *  2) Invocation of audit_syscall_entry()
  */
-static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
+static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *regs, long *syscall)
 {
 	unsigned long work = READ_ONCE(current_thread_info()->syscall_work);
 
 	if (work & SYSCALL_WORK_ENTER)
-		syscall = syscall_trace_enter(regs, work);
+		return syscall_trace_enter(regs, work, syscall);
 
-	return syscall;
+	return 0;
 }
 
 /**
@@ -167,7 +163,7 @@ static __always_inline long syscall_enter_from_user_mode_work(struct pt_regs *re
  * Returns: The original or a modified syscall number. See
  * syscall_enter_from_user_mode_work() for further explanation.
  */
-static __always_inline long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
+static __always_inline long syscall_enter_from_user_mode(struct pt_regs *regs, long *syscall)
 {
 	long ret;
 
-- 
2.51.0



^ permalink raw reply related

* [PATCH 3/4] powerpc/numa: set node_possible_map from node_online_map
From: Sang-Heon Jeon @ 2026-07-01 17:18 UTC (permalink / raw)
  To: akpm, Madhavan Srinivasan, Michael Ellerman
  Cc: linux-mm, Sang-Heon Jeon, Andreas Larsson,
	Christophe Leroy (CS GROUP), David S. Miller,
	John Paul Adrian Glaubitz, linux-kernel, linuxppc-dev,
	Nicholas Piggin, Rich Felker, Vlastimil Babka, Yoshinori Sato,
	Yury Norov
In-Reply-To: <20260701171851.2447626-1-ekffu200098@gmail.com>

mem_topology_setup() intersects node_possible_map with node_online_map.
Nothing sets node_possible_map before this, so it is NODE_MASK_ALL and
the result is just node_online_map.

In preparation for changing node_possible_map's initial value,
mem_topology_setup() no longer depends on it.

No functional change.

Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
---
 arch/powerpc/mm/numa.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index f4cf3ae036de..2fdecae90a01 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1179,7 +1179,7 @@ void __init mem_topology_setup(void)
 	 * that we expect to make use of for this platform's affinity
 	 * calculations.
 	 */
-	nodes_and(node_possible_map, node_possible_map, node_online_map);
+	node_possible_map = node_online_map;
 
 	find_possible_nodes();
 
-- 
2.43.0



^ permalink raw reply related

* [PATCH 0/4] treewide, mm: initialize node_possible_map like the other node maps
From: Sang-Heon Jeon @ 2026-07-01 17:18 UTC (permalink / raw)
  To: akpm, Andreas Larsson, David S. Miller, John Paul Adrian Glaubitz,
	Madhavan Srinivasan, Michael Ellerman, Rich Felker,
	Vlastimil Babka, Yoshinori Sato, Yury Norov
  Cc: linux-mm, Sang-Heon Jeon, Brendan Jackman,
	Christophe Leroy (CS GROUP), Johannes Weiner, linux-kernel,
	linuxppc-dev, linux-sh, Michal Hocko, Nicholas Piggin,
	Rasmus Villemoes, sparclinux, Suren Baghdasaryan, Zi Yan

While reviewing an earlier series [1], Andrew asked why we carefully
initialize node_possible_map at compile time, then zero it within __init
code anyway.

node_possible_map, aliased by node_states[N_POSSIBLE], is initialized to
NODE_MASK_ALL at compile time, unlike the other entries, which are
initialized with only node 0 set.

Architectures that use numa_memblks set node_possible_map from
numa_nodes_parsed, so the compile-time value is meaningless for them.

However, a few architectures that do not use numa_memblks do not set
node_possible_map on their own. Once these architectures set
node_possible_map to match their own topology, the NODE_MASK_ALL
initialization can be removed.

Patches 1-2 handle sparc64 and sh, which do not set node_possible_map
themselves. Both set node_online_map from their topology, so set
node_possible_map from node_online_map.

Patch 3 handles powerpc, which sets node_possible_map by intersecting it
with node_online_map. Nothing sets node_possible_map before this, so it is
still NODE_MASK_ALL and the intersection is just node_online_map. Once
patch 4 changes that initial value, the intersection would no longer be
node_online_map, so assign node_online_map directly.

Patch 4 initializes node_possible_map with only node 0 set, like the other
node_states[] entries. NODE_MASK_ALL is then no longer used, so remove it
and its only helper NODE_MASK_LAST_WORD.

[1] https://lore.kernel.org/all/20260624204030.3c8baa67713b6ca1d537baba@linux-foundation.org/

Sang-Heon Jeon (4):
  sparc64: set node_possible_map in bootmem_init_numa()
  sh: set node_possible_map in do_init_bootmem()
  powerpc/numa: set node_possible_map from node_online_map
  mm/page_alloc: initialize node_possible_map like the other node maps

 arch/powerpc/mm/numa.c   |  2 +-
 arch/sh/mm/init.c        |  2 ++
 arch/sparc/mm/init_64.c  |  2 ++
 include/linux/nodemask.h | 20 --------------------
 mm/page_alloc.c          |  2 +-
 5 files changed, 6 insertions(+), 22 deletions(-)

-- 
2.43.0



^ permalink raw reply

* Re: [PATCH 12/13] mm/mprotect: convert mprotect code to use vma_flags_t
From: Lance Yang @ 2026-07-01 16:09 UTC (permalink / raw)
  To: ljs
  Cc: akpm, tsbogend, maddy, mpe, maarten.lankhorst, mripard,
	tzimmermann, airlied, simona, l.stach, inki.dae, sw0312.kim,
	kyungmin.park, krzk, peter.griffin, jani.nikula, joonas.lahtinen,
	rodrigo.vivi, tursulin, robin.clark, lumag, lyude, dakr,
	tomi.valkeinen, hjc, heiko, andy.yan, thierry.reding, mperttunen,
	jonathanh, kraxel, dmitry.osipenko, zack.rusin, matthew.brost,
	thomas.hellstrom, oleksandr_andrushchenko, deller, bcrl, viro,
	brauner, muchun.song, osalvador, david, ziy, baolin.wang, liam,
	npache, ryan.roberts, dev.jain, baohua, lance.yang, hughd, vbabka,
	rppt, surenb, mhocko, jannh, pfalcato, kees, perex, tiwai,
	linux-mips, linux-kernel, linuxppc-dev, dri-devel, etnaviv,
	linux-arm-kernel, linux-samsung-soc, intel-gfx, linux-arm-msm,
	freedreno, nouveau, linux-rockchip, linux-tegra, virtualization,
	intel-xe, xen-devel, linux-fbdev, linux-aio, linux-fsdevel,
	linux-mm, linux-sound
In-Reply-To: <7ef626d8a12dc742cfc09d080be5dc09850e873a.1782760670.git.ljs@kernel.org>


On Mon, Jun 29, 2026 at 08:25:35PM +0100, Lorenzo Stoakes wrote:
>Replace use of the legacy vm_flags_t flags with vma_flags_t values
>throughout the mprotect logic.
>
>Note that we retain the legacy vm_flags_t bit shifting code in
>do_mprotect_key(), deferring a vma_flags_t approach to this for the time
>being.
>
>Additionally update comments to reflect the changes to be consistent.
>
>No functional change intended.
>
>Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
>---
> mm/mprotect.c | 16 ++++++++--------
> 1 file changed, 8 insertions(+), 8 deletions(-)
>
>diff --git a/mm/mprotect.c b/mm/mprotect.c
>index 9cbf932b028c..c9504b2a2525 100644
>--- a/mm/mprotect.c
>+++ b/mm/mprotect.c
>@@ -40,7 +40,7 @@
> 
> static bool maybe_change_pte_writable(struct vm_area_struct *vma, pte_t pte)
> {
>-	if (WARN_ON_ONCE(!(vma->vm_flags & VM_WRITE)))
>+	if (WARN_ON_ONCE(!vma_test(vma, VMA_WRITE_BIT)))
> 		return false;
> 
> 	/* Don't touch entries that are not even readable. */
>@@ -97,7 +97,7 @@ static bool can_change_shared_pte_writable(struct vm_area_struct *vma,
> bool can_change_pte_writable(struct vm_area_struct *vma, unsigned long addr,
> 			     pte_t pte)
> {
>-	if (!(vma->vm_flags & VM_SHARED))
>+	if (!vma_test(vma, VMA_SHARED_BIT))
> 		return can_change_private_pte_writable(vma, addr, pte);
> 
> 	return can_change_shared_pte_writable(vma, pte);
>@@ -194,7 +194,7 @@ static __always_inline void set_write_prot_commit_flush_ptes(struct vm_area_stru
> {
> 	bool set_write;
> 
>-	if (vma->vm_flags & VM_SHARED) {
>+	if (vma_test(vma, VMA_SHARED_BIT)) {
> 		set_write = can_change_shared_pte_writable(vma, ptent);
> 		prot_commit_flush_ptes(vma, addr, ptep, oldpte, ptent, nr_ptes,
> 				       /* idx = */ 0, set_write, tlb);
>@@ -811,8 +811,8 @@ mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb,
> 		vm_unacct_memory(nrpages);
> 
> 	/*
>-	 * Private VM_LOCKED VMA becoming writable: trigger COW to avoid major
>-	 * fault on access.
>+	 * Private VMA_LOCKED_BIT VMA becoming writable: trigger COW to avoid
>+	 * major fault on access.
> 	 */
> 	if (vma_flags_test(&new_vma_flags, VMA_WRITE_BIT) &&
> 	    vma_flags_test(&old_vma_flags, VMA_LOCKED_BIT) &&
>@@ -886,7 +886,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
> 			goto out;
> 		start = vma->vm_start;
> 		error = -EINVAL;
>-		if (!(vma->vm_flags & VM_GROWSDOWN))
>+		if (!vma_test(vma, VMA_GROWSDOWN_BIT))
> 			goto out;
> 	} else {
> 		if (vma->vm_start > start)
>@@ -894,7 +894,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
> 		if (unlikely(grows & PROT_GROWSUP)) {
> 			end = vma->vm_end;
> 			error = -EINVAL;
>-			if (!(vma->vm_flags & VM_GROWSUP))
>+			if (!vma_test(vma, VMA_GROWSUP_BIT))

IIUC, should this be

if (!vma_test_single_mask(vma, VMA_GROWSUP))

instead?

#elif defined(CONFIG_PARISC)
#define VM_GROWSUP	INIT_VM_FLAG(GROWSUP)
...
#ifndef VM_GROWSUP
#define VM_GROWSUP	VM_NONE
...

VM_GROWSUP is only defined as GROWSUP on parisc and becomes VM_NONE
elsewhere. But VMA_GROWSUP_BIT is the raw ARCH_1 bit, which is also used
for other arch-specific VMA flags:

	DECLARE_VMA_BIT_ALIAS(SAO, ARCH_1),		/* Strong Access Ordering (powerpc) */
	DECLARE_VMA_BIT_ALIAS(GROWSUP, ARCH_1),		/* parisc */
	DECLARE_VMA_BIT_ALIAS(SPARC_ADI, ARCH_1),	/* sparc64 */
	DECLARE_VMA_BIT_ALIAS(ARM64_BTI, ARCH_1),	/* arm64 */
	DECLARE_VMA_BIT_ALIAS(ARCH_CLEAR, ARCH_1),	/* sparc64, arm64 */
	DECLARE_VMA_BIT_ALIAS(MAPPED_COPY, ARCH_1),	/* !CONFIG_MMU */

Other vma_test() changes look fine to me: just fixed INIT_VM_FLAG()
masks matching their VMA_*_BIT :)

Cheers, Lance

> 				goto out;
> 		}
> 	}
>@@ -918,7 +918,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len,
> 		}
> 
> 		/* Does the application expect PROT_READ to imply PROT_EXEC */
>-		if (rier && (vma->vm_flags & VM_MAYEXEC))
>+		if (rier && vma_test(vma, VMA_MAYEXEC_BIT))
> 			prot |= PROT_EXEC;
> 
> 		/*
>-- 
>2.54.0
>
>


^ permalink raw reply

* Re: [PATCH v2 02/19] driver core: platform: provide platform_device_set_of_node()
From: Manuel Ebner @ 2026-07-01 15:05 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-kernel, netdev, linux-arm-msm, linux-sound, driver-core,
	devicetree, linuxppc-dev, linux-i2c, iommu, linux-pm, imx,
	linux-arm-kernel, intel-xe, dri-devel, linux-usb, linux-mips,
	platform-driver-x86, Bartosz Golaszewski, Lee Jones,
	Thierry Reding, Sebastian Hesselbarth, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Srinivas Kandagatla, Greg Kroah-Hartman, Vinod Koul,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring, Saravana Kannan,
	Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Andi Shyti, Andy Shevchenko,
	Joerg Roedel, Will Deacon, Robin Murphy, Doug Berger,
	Florian Fainelli, Broadcom internal kernel review list,
	Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Matthew Brost, Thomas Hellström, Rodrigo Vivi,
	David Airlie, Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu,
	Philipp Zabel, Maximilian Luz, Hans de Goede, Ilpo Järvinen,
	Krzysztof Kozlowski, Benjamin Herrenschmidt
In-Reply-To: <CAMRc=MdQURjypSn+QjSDJfEiOMC8bbEZmZLgjpt=EquAM1Q1pw@mail.gmail.com>

On Tue, 2026-06-30 at 09:22 -0400, Bartosz Golaszewski wrote:
> On Tue, 30 Jun 2026 13:37:54 +0200, Manuel Ebner <manuelebner@mailbox.org> said:
> > On Mon, 2026-06-29 at 11:12 +0200, Bartosz Golaszewski wrote:
> > > [...]
> > > 
> > > +/**
> > > + * platform_device_set_of_node - assign an OF node to device
> > > + * @pdev: platform device to add the node for
> > > + * @np: new device node
> > > + *
> > > + * Assign an OF node to this platform device. Internally keep track of the
> > > + * reference count. Devices created with platform_device_alloc() must use this
> > > + * function instead of assigning the node manually.
> > 
> > Doesn't it make sense to add a remark to the kernel doc of platform_device_alloc()?
> > 
> > Thanks
> >  Manuel
> > 
> > >  [...]
> > 
> 
> Sure, will do in the next iteration.

then you can add
Reviewed-by: Manuel Ebner <manuelebner@mailbox.org>

 Manuel
> 
> Bart


^ permalink raw reply

* Re: [PATCH v2 1/9] time: Respect COMPAT_32BIT_TIME for old time type functions
From: Arnd Bergmann @ 2026-07-01 13:11 UTC (permalink / raw)
  To: Thomas Weißschuh
  Cc: Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Russell King, Catalin Marinas,
	Will Deacon, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy, Thomas Bogendoerfer,
	Vincenzo Frascino, John Stultz, Stephen Boyd, David S . Miller,
	Andreas Larsson, linux-kernel, linux-arm-kernel, linuxppc-dev,
	linux-mips, linux-api, sparclinux
In-Reply-To: <20260701102912-ea8f3291-7bba-407b-9a7d-7c367a4c9398@linutronix.de>

On Wed, Jul 1, 2026, at 10:40, Thomas Weißschuh wrote:
> On Tue, Jun 30, 2026 at 03:00:37PM +0200, Arnd Bergmann wrote:
>> On Tue, Jun 30, 2026, at 09:38, Thomas Weißschuh wrote:
>> > The "old" time types use 32-bit seconds which are not y2038-safe.
>> > Respect COMPAT_32BIT_TIME for functions using those types.
>> > time(), stime() and gettimeofday() are disabled completely.
>> 
>> Looks good, yes
>
> Sashiko found an issue [0], which I think is valid. I'll change that for v3.

Ok

>> > settimeofday() is kept as it is required to do the initial timewarping
>> > after boot. However the 'tv' argument will be rejected.
>> 
>> Not sure about this part, did we already discuss this last time?
>
> This is my interpretation of [1].

Indeed, we did.

>> I can see how keeping the timewarping functionality is the easy way
>> out, but completely disabling the settimeofday syscall the same
>> way we do on new architectures seems so much more consistent.
>
> Shouldn't we then do this completely? Irrespective of COMPAT_32BIT_TIME?
> And then remove all of the timewarping and kernel timezone bits.

I don't think we can simply kill the timewarping code since that
likely has users on architectures including on x86-64.

COMPAT_32BIT_TIME=n is somewhat special because this is an
intentional (and optional) ABI break already and requires
updated userspace that avoids the time32 syscalls. Having the
timewarp still in settimeofday() or dropping it entirely is not
that different for userspace.

I was slightly worried about whether returning -EINVAL or -ENOSYS
is the better option here, but I think your choice is the correct
one after seeing that this is what glibc does other invalid
cases, and that in all of codesearch.debian.net, I could not
find a single caller that would care about the difference.

> It would be nice however if this series, and my other ones blocked behind it,
> are not blocked on that larger rework.

Sure.

I think you can go ahead with this version. I would prefer
to not have any settimeofday() for the COMPAT_32BIT_TIME=n
case, but if nobody else has a strong opinion on the matter,
let's do it your way.

Reviewed-by: Arnd Bergmann <arnd@arndb.de>


^ permalink raw reply

* [PATCH] Connecting the SB600's i8259 controller rather in pasemi's pci.c than in pasemi's setup.c.
From: Christian Zigotzky @ 2026-07-01 12:59 UTC (permalink / raw)
  To: Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
	Christophe Leroy (CS GROUP), Krzysztof Kozlowski,
	Christian Zigotzky,
	open list:LINUX FOR POWERPC (32-BIT AND 64-BIT), open list

Hello,

Here is the email without HTML (Sorry):

The Nemo board [1] doesn’t boot without this patch. Darren explained it really well:

Originally we initialised the PCI-e ports in setup arch, this is quite early, and it seems uses some kernel functions that were not recommended (They changed a whole lot of different platforms at the same time for the same reason)

After this we added the ISA bridge, then the kernel would init the IRQ contollers.

The patch that broke booting on the X1000 moved the pas_pci_init to a node in the machine description, where it called later in the boot sequence. Unfortunately this is after we've tried to add the i8259 contoller from the pas_init_IRQ. Since our ISA bridge can't be found until we've connected the PCI-e ports the system tries to write to registers that aren't yet mapped - result a kernel panic, but before console I/O has been initialised so it appears to be a hang. We had a similar problem when they were introducing Radix support.

My patch changes our code so that it works with the new kernel code in place. Basically I moved the code that adds the i8259 cascade to after we've scanned for the ISA bridge where I know it will work.

Hopefully this makes sense, shout out if it doesn't.

Regards
Darren

[1] https://en.wikipedia.org/wiki/AmigaOne_X1000

Signed-off-by: Christian Zigotzky <chzigotzky@xenosoft.de>
---
 arch/powerpc/platforms/pasemi/pci.c   | 7 +++++++
 arch/powerpc/platforms/pasemi/setup.c | 7 ++++---
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c
index 2df955274652..7208c325bfc5 100644
--- a/arch/powerpc/platforms/pasemi/pci.c
+++ b/arch/powerpc/platforms/pasemi/pci.c
@@ -25,6 +25,8 @@
 
 #define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
 
+extern void nemo_init_IRQ(void);
+
 static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
 {
 	/* Device 0 Function 0 is special: It's config space spans function 1 as
@@ -265,6 +267,11 @@ static int __init pas_add_bridge(struct device_node *dev)
 	 */
 	isa_bridge_find_early(hose);
 
+	/*
+	 * ISA brigde is now active, add the i8259 cascade (if needed)
+	 */
+	nemo_init_IRQ();
+
 	return 0;
 }
 
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c
index d03b41336901..eec74611be46 100644
--- a/arch/powerpc/platforms/pasemi/setup.c
+++ b/arch/powerpc/platforms/pasemi/setup.c
@@ -214,10 +214,12 @@ static void sb600_8259_cascade(struct irq_desc *desc)
 	chip->irq_eoi(&desc->irq_data);
 }
 
-static void __init nemo_init_IRQ(struct mpic *mpic)
+void nemo_init_IRQ(void)
 {
 	struct device_node *np;
 	int gpio_virq;
+        struct mpic *mpic;
+
 	/* Connect the SB600's legacy i8259 controller */
 	np = of_find_node_by_path("/pxp@0,e0000000");
 	i8259_init(np, 0);
@@ -228,6 +230,7 @@ static void __init nemo_init_IRQ(struct mpic *mpic)
 	irq_set_chained_handler(gpio_virq, sb600_8259_cascade);
 	mpic_unmask_irq(irq_get_irq_data(gpio_virq));
 
+	mpic = irq_get_chip_data(gpio_virq);
 	irq_set_default_domain(mpic->irqhost);
 }
 
@@ -298,8 +301,6 @@ static __init void pas_init_IRQ(void)
 		mpic_unmask_irq(irq_get_irq_data(nmi_virq));
 	}
 
-	nemo_init_IRQ(mpic);
-
 	of_node_put(mpic_node);
 	of_node_put(root);
 }
-- 
2.55.0.windows.1



^ permalink raw reply related

* [PATCH 6/6] selftests/powerpc/tm: fix spelling errors in comments
From: Wang Yan @ 2026-07-01 12:35 UTC (permalink / raw)
  To: Shuah Khan, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy (CS GROUP), linuxppc-dev,
	linux-kselftest, linux-kernel
  Cc: Wang Yan
In-Reply-To: <20260701123520.271580-1-wangyan01@kylinos.cn>

Fix three spelling mistakes in powerpc TM selftest comments:
  - "delievery" -> "delivery" (powerpc/tm/tm-signal-stack.c)
  - "sucess" -> "success" (powerpc/tm/tm-tar.c)
  - "becuase" -> "because" (powerpc/tm/tm-signal-msr-resv.c)

Signed-off-by: Wang Yan <wangyan01@kylinos.cn>
---
 tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c | 2 +-
 tools/testing/selftests/powerpc/tm/tm-signal-stack.c    | 2 +-
 tools/testing/selftests/powerpc/tm/tm-tar.c             | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c b/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c
index 4a61e9bd12b4..8aee18819603 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-msr-resv.c
@@ -42,7 +42,7 @@ void signal_usr1(int signum, siginfo_t *info, void *uc)
 #else
 	ucp->uc_mcontext.uc_regs->gregs[PT_MSR] |= (7ULL);
 #endif
-	/* Should segv on return becuase of invalid context */
+	/* Should segv on return because of invalid context */
 	segv_expected = 1;
 }
 
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-stack.c b/tools/testing/selftests/powerpc/tm/tm-signal-stack.c
index 68807aac8dd3..7b323679437d 100644
--- a/tools/testing/selftests/powerpc/tm/tm-signal-stack.c
+++ b/tools/testing/selftests/powerpc/tm/tm-signal-stack.c
@@ -52,7 +52,7 @@ int tm_signal_stack()
 
 	/*
 	 * The flow here is:
-	 * 1) register a signal handler (so signal delievery occurs)
+	 * 1) register a signal handler (so signal delivery occurs)
 	 * 2) make stack pointer (r1) = NULL
 	 * 3) start transaction
 	 * 4) cause segv
diff --git a/tools/testing/selftests/powerpc/tm/tm-tar.c b/tools/testing/selftests/powerpc/tm/tm-tar.c
index f2a9137f3c1e..ea420caa3961 100644
--- a/tools/testing/selftests/powerpc/tm/tm-tar.c
+++ b/tools/testing/selftests/powerpc/tm/tm-tar.c
@@ -50,7 +50,7 @@ int test_tar(void)
 			"bne	2b;"
 			"tend.;"
 
-			/* Transaction sucess! TAR should be 3 */
+			/* Transaction success! TAR should be 3 */
 			"mfspr  7, %[tar];"
 			"ori	%[res], 7, 4;"  // res = 3|4 = 7
 			"b	4f;"
-- 
2.25.1



^ permalink raw reply related

* Re: [PATCH v2 2/9] vdso/gettimeofday: Validate system call existence for time() and gettimeofday()
From: Philippe Mathieu-Daudé @ 2026-07-01  7:47 UTC (permalink / raw)
  To: Thomas Weißschuh, Andy Lutomirski, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Russell King, Catalin Marinas, Will Deacon, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
	Thomas Bogendoerfer, Vincenzo Frascino, John Stultz, Stephen Boyd,
	David S. Miller, Andreas Larsson
  Cc: linux-kernel, linux-arm-kernel, linuxppc-dev, linux-mips,
	Arnd Bergmann, linux-api, sparclinux
In-Reply-To: <20260630-vdso-compat_32bit_time-v2-2-520d194640dd@linutronix.de>

On 30/6/26 09:38, Thomas WeiÃschuh wrote:
> Not all architectures have the system calls for time() and
> gettimeofday(). When the system call is missing, the vDSO function
> should also not be present.
> 
> Validate that.
> 
> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> ---
>   lib/vdso/gettimeofday.c | 10 ++++++++++
>   1 file changed, 10 insertions(+)

Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>


^ permalink raw reply

* Re: [PATCH v2 9/9] vdso/gettimeofday: Verify COMPAT_32BIT_TIME interactions
From: Philippe Mathieu-Daudé @ 2026-07-01  7:51 UTC (permalink / raw)
  To: Thomas Weißschuh, Andy Lutomirski, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Russell King, Catalin Marinas, Will Deacon, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
	Thomas Bogendoerfer, Vincenzo Frascino, John Stultz, Stephen Boyd,
	David S. Miller, Andreas Larsson
  Cc: linux-kernel, linux-arm-kernel, linuxppc-dev, linux-mips,
	Arnd Bergmann, linux-api, sparclinux
In-Reply-To: <20260630-vdso-compat_32bit_time-v2-9-520d194640dd@linutronix.de>

On 30/6/26 09:38, Thomas WeiÃschuh wrote:
> If CONFIG_COMPAT_32BIT_TIME is disabled then the vDSO should not
> provide any 32-bit time related functionality.
> 
> Add some build-time validations to make sure the architecture-specific
> glue satisfies this requirement.
> 
> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> ---
>   lib/vdso/gettimeofday.c | 10 ++++++++++
>   1 file changed, 10 insertions(+)

Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>


^ permalink raw reply

* Re: [PATCH v2 8/9] sparc: vdso: Respect COMPAT_32BIT_TIME
From: Philippe Mathieu-Daudé @ 2026-07-01  7:50 UTC (permalink / raw)
  To: Thomas Weißschuh, Andy Lutomirski, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Russell King, Catalin Marinas, Will Deacon, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
	Thomas Bogendoerfer, Vincenzo Frascino, John Stultz, Stephen Boyd,
	David S. Miller, Andreas Larsson
  Cc: linux-kernel, linux-arm-kernel, linuxppc-dev, linux-mips,
	Arnd Bergmann, linux-api, sparclinux
In-Reply-To: <20260630-vdso-compat_32bit_time-v2-8-520d194640dd@linutronix.de>

On 30/6/26 09:38, Thomas WeiÃschuh wrote:
> If CONFIG_COMPAT_32BIT_TIME is disabled then the vDSO should not
> provide any 32-bit time related functionality. This is the intended
> effect of the kconfig option and also the fallback system calls would
> also not be implemented.
> 
> Currently the kconfig option does not affect the gettimeofday() syscall,
> so also keep that in the vDSO.
> 
> Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
> ---
>   arch/sparc/vdso/vclock_gettime.c    | 4 ++++
>   arch/sparc/vdso/vdso32/vdso32.lds.S | 6 ++++--
>   2 files changed, 8 insertions(+), 2 deletions(-)

Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox