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 Reviewed-by: Dave Marquardt --- 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 #include #include +#include +#include #include #include #include @@ -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)