From: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
To: intel-wired-lan@lists.osuosl.org
Cc: netdev@vger.kernel.org,
Michal Swiatkowski <michal.swiatkowski@linux.intel.com>,
Aleksandr Loktionov <aleksandr.loktionov@intel.com>,
Larysa Zaremba <larysa.zaremba@intel.com>
Subject: [PATCH iwl-next v2 05/10] libie, idpf: move irq code to libie
Date: Fri, 11 Sep 2026 14:49:16 +0200 [thread overview]
Message-ID: <20260911124921.2881348-6-michal.swiatkowski@linux.intel.com> (raw)
In-Reply-To: <20260911124921.2881348-1-michal.swiatkowski@linux.intel.com>
Management of irqs is the same in idpf and ixd driver. Move the common
code to separate lib to reuse it in ixd.
libie_irq keeps the allocated interrupts in an xarray split into two
index ranges: LIBIE_IRQ_STATIC for the vectors that must exist for the
device to be functional at all (mailbox, the minimum per default vport,
RDMA), and LIBIE_IRQ_DYNAMIC for the rest.
libie_irq_init() allocates the static range with pci_alloc_irq_vectors()
at probe. When the platform supports post-enable MSI-X allocation,
libie_irq_alloc() backs the dynamic range with pci_msix_alloc_irq_at()
on demand, so vectors are only claimed once a vport actually needs them;
when it does not, the whole range is allocated up front and
LIBIE_IRQ_DYNAMIC is transparently served from the static pool.
libie_irq_reserve() hands out an index without an MSI-X vector behind it,
for queues that are mapped to a vector but never raise an interrupt
(the idpf NOIRQ vector used by XDP send queues).
Move getting irq slot before allocating q_vectors array. Alloc only
q_vectors for which there is an available interrupt.
Allocating more lead to the situation where there are unused q_vector
which can be confused.
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Michal Swiatkowski <michal.swiatkowski@linux.intel.com>
---
drivers/net/ethernet/intel/idpf/Kconfig | 1 +
drivers/net/ethernet/intel/idpf/idpf.h | 74 +---
drivers/net/ethernet/intel/idpf/idpf_idc.c | 6 +-
drivers/net/ethernet/intel/idpf/idpf_lib.c | 382 ++++++------------
drivers/net/ethernet/intel/idpf/idpf_main.c | 3 +-
drivers/net/ethernet/intel/idpf/idpf_txrx.c | 104 +++--
drivers/net/ethernet/intel/idpf/idpf_txrx.h | 2 +-
.../net/ethernet/intel/idpf/idpf_virtchnl.c | 45 +--
.../net/ethernet/intel/idpf/idpf_virtchnl.h | 3 +-
drivers/net/ethernet/intel/libie/Kconfig | 6 +
drivers/net/ethernet/intel/libie/Makefile | 4 +
drivers/net/ethernet/intel/libie/irq.c | 256 ++++++++++++
include/linux/net/intel/libie/irq.h | 75 ++++
13 files changed, 560 insertions(+), 401 deletions(-)
create mode 100644 drivers/net/ethernet/intel/libie/irq.c
create mode 100644 include/linux/net/intel/libie/irq.h
diff --git a/drivers/net/ethernet/intel/idpf/Kconfig b/drivers/net/ethernet/intel/idpf/Kconfig
index 586df3a4afe9..6e21e4749904 100644
--- a/drivers/net/ethernet/intel/idpf/Kconfig
+++ b/drivers/net/ethernet/intel/idpf/Kconfig
@@ -8,6 +8,7 @@ config IDPF
select DIMLIB
select LIBIE_CP
select LIBETH_XDP
+ select LIBIE_IRQ
help
This driver supports Intel(R) Infrastructure Data Path Function
devices.
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index 785d551c795d..51f447135693 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -24,6 +24,7 @@ struct idpf_rss_data;
#include <linux/net/intel/iidc_rdma.h>
#include <linux/net/intel/iidc_rdma_idpf.h>
#include <linux/net/intel/libie/controlq.h>
+#include <linux/net/intel/libie/irq.h>
#include <linux/net/intel/virtchnl2.h>
#include "idpf_txrx.h"
@@ -299,7 +300,6 @@ struct idpf_fsteer_fltr {
* struct idpf_q_vec_rsrc - handle for queue and vector resources
* @dev: device pointer for DMA mapping
* @q_vectors: array of queue vectors
- * @q_vector_idxs: starting index of queue vectors
* @num_q_vectors: number of IRQ vectors allocated
* @noirq_v_idx: software IRQ index used to get hardware vector information
* @noirq_dyn_ctl_ena: value to write to the above to enable it
@@ -328,7 +328,6 @@ struct idpf_fsteer_fltr {
struct idpf_q_vec_rsrc {
struct device *dev;
struct idpf_q_vector *q_vectors;
- u16 *q_vector_idxs;
u16 num_q_vectors;
u16 noirq_v_idx;
u32 noirq_dyn_ctl_ena;
@@ -526,46 +525,6 @@ struct idpf_avail_queue_info {
u16 avail_complq;
};
-/**
- * struct idpf_vector_info - Utility structure to pass function arguments as a
- * structure
- * @num_req_vecs: Vectors required based on the number of queues updated by the
- * user via ethtool
- * @num_curr_vecs: Current number of vectors, must be >= @num_req_vecs
- * @index: Relative starting index for vectors
- * @default_vport: Vectors are for default vport
- */
-struct idpf_vector_info {
- u16 num_req_vecs;
- u16 num_curr_vecs;
- u16 index;
- bool default_vport;
-};
-
-/**
- * struct idpf_vector_lifo - Stack to maintain vector indexes used for vector
- * distribution algorithm
- * @top: Points to stack top i.e. next available vector index
- * @base: Always points to start of the free pool
- * @size: Total size of the vector stack
- * @vec_idx: Array to store all the vector indexes
- *
- * Vector stack maintains all the relative vector indexes at the *adapter*
- * level. This stack is divided into 2 parts, first one is called as 'default
- * pool' and other one is called 'free pool'. Vector distribution algorithm
- * gives priority to default vports in a way that at least IDPF_MIN_Q_VEC
- * vectors are allocated per default vport and the relative vector indexes for
- * those are maintained in default pool. Free pool contains all the unallocated
- * vector indexes which can be allocated on-demand basis. Mailbox vector index
- * is maintained in the default pool of the stack.
- */
-struct idpf_vector_lifo {
- u16 top;
- u16 base;
- u16 size;
- u16 *vec_idx;
-};
-
/**
* struct idpf_queue_id_reg_chunk - individual queue ID and register chunk
* @qtail_reg_start: queue tail register offset
@@ -652,6 +611,18 @@ struct idpf_irq_info {
int num;
};
+/**
+ * struct idpf_rdma_irq - RDMA interrupt vectors data
+ * @entries: MSIX table shared with the RDMA auxiliary device
+ * @map: libie IRQ mappings corresponding to @entries
+ * @num: number of vectors granted for RDMA
+ */
+struct idpf_rdma_irq {
+ struct msix_entry *entries;
+ struct msi_map *map;
+ u16 num;
+};
+
/**
* struct idpf_adapter - Device data struct generated on probe
* @pdev: PCI device struct given on probe
@@ -666,14 +637,11 @@ struct idpf_irq_info {
* @asq: Send control queue info
* @arq: Receive control queue info
* @xnm: Xn transaction manager
- * @num_avail_msix: Available number of MSIX vectors
- * @num_msix_entries: Number of entries in MSIX table
- * @num_rdma_msix_entries: Available number of MSIX vectors for RDMA
- * @rdma_msix_entries: RDMA MSIX table
+ * @rdma_irq: RDMA interrupt vectors data
+ * @irq: libie irq structure
* @irq_info: hardware data needed to setup irq
* @req_vec_chunks: Requested vector chunk data
* @mb_vector: Mailbox vector data
- * @vector_stack: Stack to store the msix vector indexes
* @irq_mb_handler: Handler for hard interrupt for mailbox
* @tx_timeout_count: Number of TX timeouts that have occurred
* @avail_queues: Device given queue limits
@@ -706,7 +674,6 @@ struct idpf_irq_info {
* @req_tx_splitq: TX split or single queue model to request
* @req_rx_splitq: RX split or single queue model to request
* @vport_ctrl_lock: Lock to protect the vport control flow
- * @vector_lock: Lock to protect vector distribution
* @queue_lock: Lock to protect queue distribution
* @vc_buf_lock: Lock to protect virtchnl buffer
* @ptp: Storage for PTP-related data
@@ -725,14 +692,11 @@ struct idpf_adapter {
struct libie_ctlq_info *asq;
struct libie_ctlq_info *arq;
struct libie_ctlq_xn_manager *xnm;
- u16 num_avail_msix;
- u16 num_msix_entries;
- u16 num_rdma_msix_entries;
- struct msix_entry *rdma_msix_entries;
+ struct idpf_rdma_irq rdma_irq;
+ struct libie_irq irq;
struct virtchnl2_alloc_vectors *req_vec_chunks;
struct idpf_irq_info irq_info;
struct idpf_q_vector mb_vector;
- struct idpf_vector_lifo vector_stack;
irqreturn_t (*irq_mb_handler)(int irq, void *data);
u32 tx_timeout_count;
@@ -770,7 +734,6 @@ struct idpf_adapter {
bool req_rx_splitq;
struct mutex vport_ctrl_lock;
- struct mutex vector_lock;
struct mutex queue_lock;
struct mutex vc_buf_lock;
@@ -1031,9 +994,6 @@ u16 idpf_get_max_tx_hdr_size(struct idpf_adapter *adapter);
int idpf_initiate_soft_reset(struct idpf_vport *vport,
enum idpf_vport_reset_cause reset_cause);
void idpf_deinit_task(struct idpf_adapter *adapter);
-int idpf_req_rel_vector_indexes(struct idpf_adapter *adapter,
- u16 *q_vector_idxs,
- struct idpf_vector_info *vec_info);
void idpf_set_ethtool_ops(struct net_device *netdev);
void idpf_vport_intr_write_itr(struct idpf_q_vector *q_vector,
u16 itr, bool tx);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_idc.c b/drivers/net/ethernet/intel/idpf/idpf_idc.c
index b6cd1c25ae5d..5d15bef6eec3 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_idc.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_idc.c
@@ -396,14 +396,14 @@ idpf_idc_init_msix_data(struct idpf_adapter *adapter)
struct iidc_rdma_core_dev_info *cdev_info;
struct iidc_rdma_priv_dev_info *privd;
- if (!adapter->rdma_msix_entries)
+ if (!adapter->rdma_irq.entries)
return;
cdev_info = adapter->cdev_info;
privd = cdev_info->iidc_priv;
- privd->msix_entries = adapter->rdma_msix_entries;
- privd->msix_count = adapter->num_rdma_msix_entries;
+ privd->msix_entries = adapter->rdma_irq.entries;
+ privd->msix_count = adapter->rdma_irq.num;
}
/**
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index 5a2975081227..ddd1dff391e0 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -9,56 +9,85 @@
static const struct net_device_ops idpf_netdev_ops;
-/**
- * idpf_init_vector_stack - Fill the MSIX vector stack with vector index
- * @adapter: private data struct
- *
- * Return 0 on success, error on failure
- */
-static int idpf_init_vector_stack(struct idpf_adapter *adapter)
+static int idpf_rdma_intr_init(struct idpf_adapter *adapter, u16 num)
{
- struct idpf_vector_lifo *stack;
- u16 min_vec;
- u32 i;
-
- mutex_lock(&adapter->vector_lock);
- min_vec = adapter->num_msix_entries - adapter->num_avail_msix;
- stack = &adapter->vector_stack;
- stack->size = adapter->num_msix_entries;
- /* set the base and top to point at start of the 'free pool' to
- * distribute the unused vectors on-demand basis
- */
- stack->base = min_vec;
- stack->top = min_vec;
+ struct idpf_rdma_irq *rdma_irq = &adapter->rdma_irq;
+ int i;
- stack->vec_idx = kcalloc(stack->size, sizeof(u16), GFP_KERNEL);
- if (!stack->vec_idx) {
- mutex_unlock(&adapter->vector_lock);
+ if (!idpf_is_rdma_cap_ena(adapter))
+ return 0;
+ rdma_irq->entries = kzalloc_objs(struct msix_entry, num);
+ if (!rdma_irq->entries)
return -ENOMEM;
+
+ rdma_irq->map = kzalloc_objs(struct msi_map, num);
+ if (!rdma_irq->map) {
+ kfree(rdma_irq->entries);
+ rdma_irq->entries = NULL;
+
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < min(IDPF_MIN_RDMA_VEC, num); i++) {
+ struct msi_map map = libie_irq_alloc(&adapter->irq,
+ LIBIE_IRQ_STATIC);
+ if (map.index < 0) {
+ for (int j = i - 1; j >= 0; j--)
+ libie_irq_free(&adapter->irq, rdma_irq->map[j]);
+
+ kfree(rdma_irq->map);
+ rdma_irq->map = NULL;
+ kfree(rdma_irq->entries);
+ rdma_irq->entries = NULL;
+
+ return -EINVAL;
+ }
+
+ rdma_irq->map[i] = map;
+ rdma_irq->entries[i].entry =
+ adapter->irq_info.vectors[map.index].idx;
+ rdma_irq->entries[i].vector = map.virq;
+ }
+ for (; i < num; i++) {
+ struct msi_map map = libie_irq_alloc(&adapter->irq,
+ LIBIE_IRQ_DYNAMIC);
+
+ /* Lower number of entries if alloc fails. */
+ if (map.index < 0)
+ break;
+
+ rdma_irq->map[i] = map;
+ rdma_irq->entries[i].entry =
+ adapter->irq_info.vectors[map.index].idx;
+ rdma_irq->entries[i].vector = map.virq;
}
- for (i = 0; i < stack->size; i++)
- stack->vec_idx[i] = i;
+ rdma_irq->num = i;
- mutex_unlock(&adapter->vector_lock);
+ if (i != num)
+ dev_warn(&adapter->pdev->dev,
+ "Warning: %d RDMA vectors requested, %d granted\n",
+ num, i);
return 0;
}
-/**
- * idpf_deinit_vector_stack - zero out the MSIX vector stack
- * @adapter: private data struct
- */
-static void idpf_deinit_vector_stack(struct idpf_adapter *adapter)
+static void idpf_rdma_intr_free(struct idpf_adapter *adapter)
{
- struct idpf_vector_lifo *stack;
+ struct idpf_rdma_irq *rdma_irq = &adapter->rdma_irq;
+
+ if (!idpf_is_rdma_cap_ena(adapter))
+ return;
+
+ for (int i = 0; i < rdma_irq->num; i++)
+ libie_irq_free(&adapter->irq, rdma_irq->map[i]);
- mutex_lock(&adapter->vector_lock);
- stack = &adapter->vector_stack;
- kfree(stack->vec_idx);
- stack->vec_idx = NULL;
- mutex_unlock(&adapter->vector_lock);
+ kfree(rdma_irq->map);
+ rdma_irq->map = NULL;
+ kfree(rdma_irq->entries);
+ rdma_irq->entries = NULL;
+ rdma_irq->num = 0;
}
/**
@@ -74,6 +103,7 @@ void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter)
return;
kfree(free_irq(adapter->mb_vector.irq.virq, adapter));
+ libie_irq_free(&adapter->irq, adapter->mb_vector.irq);
queue_delayed_work(adapter->mbx_wq, &adapter->mbx_task, 0);
}
@@ -84,11 +114,9 @@ void idpf_mb_intr_rel_irq(struct idpf_adapter *adapter)
void idpf_intr_rel(struct idpf_adapter *adapter)
{
idpf_mb_intr_rel_irq(adapter);
- pci_free_irq_vectors(adapter->pdev);
+ idpf_rdma_intr_free(adapter);
+ libie_irq_deinit(&adapter->irq);
idpf_send_dealloc_vectors_msg(adapter);
- idpf_deinit_vector_stack(adapter);
- kfree(adapter->rdma_msix_entries);
- adapter->rdma_msix_entries = NULL;
}
/**
@@ -151,152 +179,22 @@ static int idpf_mb_intr_req_irq(struct idpf_adapter *adapter)
*/
static int idpf_mb_intr_init(struct idpf_adapter *adapter)
{
- struct msi_map *mb_irq = &adapter->mb_vector.irq;
+ struct idpf_q_vector *mb_vector = &adapter->mb_vector;
+ int err;
+
+ mb_vector->irq = libie_irq_alloc(&adapter->irq, LIBIE_IRQ_STATIC);
- mb_irq->index = IDPF_MBX_IRQ_INDEX;
- mb_irq->virq = pci_irq_vector(adapter->pdev, mb_irq->index);
- if (mb_irq->virq < 0)
- return mb_irq->virq;
+ if (mb_vector->irq.index < 0)
+ return mb_vector->irq.index;
adapter->dev_ops.reg_ops.mb_intr_reg_init(adapter);
adapter->irq_mb_handler = idpf_mb_intr_clean;
- return idpf_mb_intr_req_irq(adapter);
-}
-
-/**
- * idpf_vector_lifo_push - push MSIX vector index onto stack
- * @adapter: private data struct
- * @vec_idx: vector index to store
- */
-static int idpf_vector_lifo_push(struct idpf_adapter *adapter, u16 vec_idx)
-{
- struct idpf_vector_lifo *stack = &adapter->vector_stack;
-
- lockdep_assert_held(&adapter->vector_lock);
-
- if (stack->top == stack->base) {
- dev_err(&adapter->pdev->dev, "Exceeded the vector stack limit: %d\n",
- stack->top);
- return -EINVAL;
- }
-
- stack->vec_idx[--stack->top] = vec_idx;
-
- return 0;
-}
-
-/**
- * idpf_vector_lifo_pop - pop MSIX vector index from stack
- * @adapter: private data struct
- */
-static int idpf_vector_lifo_pop(struct idpf_adapter *adapter)
-{
- struct idpf_vector_lifo *stack = &adapter->vector_stack;
-
- lockdep_assert_held(&adapter->vector_lock);
-
- if (stack->top == stack->size) {
- dev_err(&adapter->pdev->dev, "No interrupt vectors are available to distribute!\n");
-
- return -EINVAL;
- }
-
- return stack->vec_idx[stack->top++];
-}
-
-/**
- * idpf_vector_stash - Store the vector indexes onto the stack
- * @adapter: private data struct
- * @q_vector_idxs: vector index array
- * @vec_info: info related to the number of vectors
- *
- * This function is a no-op if there are no vectors indexes to be stashed
- */
-static void idpf_vector_stash(struct idpf_adapter *adapter, u16 *q_vector_idxs,
- struct idpf_vector_info *vec_info)
-{
- int i, base = 0;
- u16 vec_idx;
-
- lockdep_assert_held(&adapter->vector_lock);
-
- if (!vec_info->num_curr_vecs)
- return;
-
- /* For default vports, no need to stash vector allocated from the
- * default pool onto the stack
- */
- if (vec_info->default_vport)
- base = IDPF_MIN_Q_VEC;
-
- for (i = vec_info->num_curr_vecs - 1; i >= base ; i--) {
- vec_idx = q_vector_idxs[i];
- idpf_vector_lifo_push(adapter, vec_idx);
- adapter->num_avail_msix++;
- }
-}
-
-/**
- * idpf_req_rel_vector_indexes - Request or release MSIX vector indexes
- * @adapter: driver specific private structure
- * @q_vector_idxs: vector index array
- * @vec_info: info related to the number of vectors
- *
- * This is the core function to distribute the MSIX vectors acquired from the
- * OS. It expects the caller to pass the number of vectors required and
- * also previously allocated. First, it stashes previously allocated vector
- * indexes on to the stack and then figures out if it can allocate requested
- * vectors. It can wait on acquiring the mutex lock. If the caller passes 0 as
- * requested vectors, then this function just stashes the already allocated
- * vectors and returns 0.
- *
- * Returns actual number of vectors allocated on success, error value on failure
- * If 0 is returned, implies the stack has no vectors to allocate which is also
- * a failure case for the caller
- */
-int idpf_req_rel_vector_indexes(struct idpf_adapter *adapter,
- u16 *q_vector_idxs,
- struct idpf_vector_info *vec_info)
-{
- u16 num_req_vecs, num_alloc_vecs = 0, max_vecs;
- struct idpf_vector_lifo *stack;
- int i, j, vecid;
-
- mutex_lock(&adapter->vector_lock);
- stack = &adapter->vector_stack;
- num_req_vecs = vec_info->num_req_vecs;
-
- /* Stash interrupt vector indexes onto the stack if required */
- idpf_vector_stash(adapter, q_vector_idxs, vec_info);
-
- if (!num_req_vecs)
- goto rel_lock;
-
- if (vec_info->default_vport) {
- /* As IDPF_MIN_Q_VEC per default vport is put aside in the
- * default pool of the stack, use them for default vports
- */
- j = vec_info->index * IDPF_MIN_Q_VEC + IDPF_MBX_Q_VEC;
- for (i = 0; i < IDPF_MIN_Q_VEC; i++) {
- q_vector_idxs[num_alloc_vecs++] = stack->vec_idx[j++];
- num_req_vecs--;
- }
- }
-
- /* Find if stack has enough vector to allocate */
- max_vecs = min(adapter->num_avail_msix, num_req_vecs);
-
- for (j = 0; j < max_vecs; j++) {
- vecid = idpf_vector_lifo_pop(adapter);
- q_vector_idxs[num_alloc_vecs++] = vecid;
- }
- adapter->num_avail_msix -= max_vecs;
-
-rel_lock:
- mutex_unlock(&adapter->vector_lock);
+ err = idpf_mb_intr_req_irq(adapter);
+ if (err)
+ libie_irq_free(&adapter->irq, mb_vector->irq);
- return num_alloc_vecs;
+ return err;
}
/**
@@ -307,26 +205,24 @@ int idpf_req_rel_vector_indexes(struct idpf_adapter *adapter,
*/
int idpf_intr_req(struct idpf_adapter *adapter)
{
- int num_rdma_vecs = 0, min_rdma_vecs = 0, num_lan_vecs = 0;
u16 default_vports = idpf_get_default_vports(adapter);
- int min_vectors, actual_vecs, min_lan_vecs, err;
- int num_q_vecs, total_vecs;
- int i;
+ int num_q_vecs, total_vecs, static_vecs;
+ struct libie_irq *irq = &adapter->irq;
+ int num_rdma_vecs = 0;
+ int err;
total_vecs = idpf_get_reserved_vecs(adapter);
if (idpf_is_rdma_cap_ena(adapter)) {
num_rdma_vecs = idpf_get_reserved_rdma_vecs(adapter);
- min_rdma_vecs = IDPF_MIN_RDMA_VEC;
-
if (!num_rdma_vecs) {
/* If idpf_get_reserved_rdma_vecs is 0, vectors are
* pulled from the LAN pool.
*/
- num_rdma_vecs = min_rdma_vecs;
- } else if (num_rdma_vecs < min_rdma_vecs) {
+ num_rdma_vecs = IDPF_MIN_RDMA_VEC;
+ } else if (num_rdma_vecs < IDPF_MIN_RDMA_VEC) {
dev_err(&adapter->pdev->dev,
"Not enough vectors reserved for RDMA (min: %u, current: %u)\n",
- min_rdma_vecs, num_rdma_vecs);
+ IDPF_MIN_RDMA_VEC, num_rdma_vecs);
return -EINVAL;
}
}
@@ -341,70 +237,32 @@ int idpf_intr_req(struct idpf_adapter *adapter)
return -EAGAIN;
}
- min_lan_vecs = IDPF_MBX_Q_VEC + IDPF_MIN_Q_VEC * default_vports;
- min_vectors = min_lan_vecs + min_rdma_vecs;
- actual_vecs = pci_alloc_irq_vectors(adapter->pdev, min_vectors,
- total_vecs, PCI_IRQ_MSIX);
- if (actual_vecs < 0) {
- dev_err(&adapter->pdev->dev, "Failed to allocate minimum MSIX vectors required: %d\n",
- min_vectors);
- err = actual_vecs;
+ static_vecs = IDPF_MBX_Q_VEC + IDPF_MIN_Q_VEC * default_vports +
+ min(IDPF_MIN_RDMA_VEC, num_rdma_vecs);
+ err = libie_irq_init(irq, adapter->pdev, static_vecs, total_vecs);
+ if (err) {
+ dev_err(&adapter->pdev->dev, "Failed to allocate MSIX vectors: %d\n",
+ err);
+ err = -EAGAIN;
goto send_dealloc_vecs;
}
- if (idpf_is_rdma_cap_ena(adapter)) {
- if (actual_vecs < total_vecs) {
- dev_warn(&adapter->pdev->dev,
- "Warning: %d vectors requested, only %d available. Defaulting to minimum (%d) for RDMA and remaining for LAN.\n",
- total_vecs, actual_vecs, IDPF_MIN_RDMA_VEC);
- num_rdma_vecs = IDPF_MIN_RDMA_VEC;
- }
-
- adapter->rdma_msix_entries = kzalloc_objs(struct msix_entry,
- num_rdma_vecs);
- if (!adapter->rdma_msix_entries) {
- err = -ENOMEM;
- goto free_irq;
- }
- }
-
- num_lan_vecs = actual_vecs - num_rdma_vecs;
-
- for (i = 0; i < num_rdma_vecs; i++) {
- adapter->rdma_msix_entries[i].entry =
- adapter->irq_info.vectors[num_lan_vecs + i].idx;
- adapter->rdma_msix_entries[i].vector =
- pci_irq_vector(adapter->pdev, num_lan_vecs + i);
- }
-
- /* 'num_avail_msix' is used to distribute excess vectors to the vports
- * after considering the minimum vectors required per each default
- * vport
- */
- adapter->num_avail_msix = num_lan_vecs - min_lan_vecs;
- adapter->num_msix_entries = num_lan_vecs;
- if (idpf_is_rdma_cap_ena(adapter))
- adapter->num_rdma_msix_entries = num_rdma_vecs;
-
- /* Fill MSIX vector lifo stack with vector indexes */
- err = idpf_init_vector_stack(adapter);
+ err = idpf_mb_intr_init(adapter);
if (err)
- goto free_rdma_msix;
+ goto free_irq;
- err = idpf_mb_intr_init(adapter);
+ err = idpf_rdma_intr_init(adapter, num_rdma_vecs);
if (err)
- goto deinit_vec_stack;
+ goto free_mb_irq;
+
idpf_mb_irq_enable(adapter);
return 0;
-deinit_vec_stack:
- idpf_deinit_vector_stack(adapter);
-free_rdma_msix:
- kfree(adapter->rdma_msix_entries);
- adapter->rdma_msix_entries = NULL;
+free_mb_irq:
+ idpf_mb_intr_rel_irq(adapter);
free_irq:
- pci_free_irq_vectors(adapter->pdev);
+ libie_irq_deinit(irq);
send_dealloc_vecs:
idpf_send_dealloc_vectors_msg(adapter);
@@ -986,7 +844,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl)
idpf_vport_intr_deinit(vport, rsrc);
idpf_xdp_rxq_info_deinit_all(rsrc);
idpf_vport_queues_rel(vport, rsrc);
- idpf_vport_intr_rel(rsrc);
+ idpf_vport_intr_rel(rsrc, &adapter->irq);
clear_bit(IDPF_VPORT_UP, np->state);
if (rtnl)
@@ -1042,10 +900,8 @@ static void idpf_decfg_netdev(struct idpf_vport *vport)
*/
static void idpf_vport_rel(struct idpf_vport *vport)
{
- struct idpf_q_vec_rsrc *rsrc = &vport->dflt_qv_rsrc;
struct idpf_adapter *adapter = vport->adapter;
struct idpf_vport_config *vport_config;
- struct idpf_vector_info vec_info;
struct idpf_rss_data *rss_data;
struct idpf_vport_max_q max_q;
u16 idx = vport->idx;
@@ -1065,16 +921,6 @@ static void idpf_vport_rel(struct idpf_vport *vport)
max_q.max_complq = vport_config->max_q.max_complq;
idpf_vport_dealloc_max_qs(adapter, &max_q);
- /* Release all the allocated vectors on the stack */
- vec_info.num_req_vecs = 0;
- vec_info.num_curr_vecs = rsrc->num_q_vectors;
- vec_info.default_vport = vport->default_vport;
-
- idpf_req_rel_vector_indexes(adapter, rsrc->q_vector_idxs, &vec_info);
-
- kfree(rsrc->q_vector_idxs);
- rsrc->q_vector_idxs = NULL;
-
idpf_vport_deinit_queue_reg_chunks(vport_config);
kfree(adapter->vport_params_recvd[idx]);
@@ -1196,8 +1042,8 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter,
struct idpf_vport_max_q *max_q)
{
struct idpf_rss_data *rss_data;
- u16 idx = adapter->next_vport;
struct idpf_q_vec_rsrc *rsrc;
+ u16 idx = adapter->next_vport;
struct idpf_vport *vport;
u16 num_max_q;
int err;
@@ -1247,13 +1093,10 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter,
rsrc = &vport->dflt_qv_rsrc;
rsrc->dev = &adapter->pdev->dev;
- rsrc->q_vector_idxs = kcalloc(num_max_q, sizeof(u16), GFP_KERNEL);
- if (!rsrc->q_vector_idxs)
- goto free_vport;
err = idpf_vport_init(vport, max_q);
if (err)
- goto free_vector_idxs;
+ goto free_vport;
/* LUT and key are both initialized here. Key is not strictly dependent
* on how many queues we have. If we change number of queues and soft
@@ -1264,7 +1107,7 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter,
rss_data = &adapter->vport_config[idx]->user_config.rss_data;
rss_data->rss_key = kzalloc(rss_data->rss_key_size, GFP_KERNEL);
if (!rss_data->rss_key)
- goto free_qreg_chunks;
+ goto deinit_vport_queues;
/* Initialize default RSS key */
netdev_rss_key_fill((void *)rss_data->rss_key, rss_data->rss_key_size);
@@ -1287,10 +1130,8 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter,
free_rss_key:
kfree(rss_data->rss_key);
rss_data->rss_key = NULL;
-free_qreg_chunks:
+deinit_vport_queues:
idpf_vport_deinit_queue_reg_chunks(adapter->vport_config[idx]);
-free_vector_idxs:
- kfree(rsrc->q_vector_idxs);
free_vport:
kfree(vport);
@@ -1485,6 +1326,12 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl)
/* we do not allow interface up just yet */
netif_carrier_off(vport->netdev);
+ /*
+ * num_q_vectors can be lowered by previous open/close cycle or
+ * error during open. Reset it back to default value. Still can be
+ * changed when there is not enough interrupts available.
+ */
+ idpf_vport_set_num_q_vectors(rsrc, vport->num_xdp_txq);
err = idpf_vport_intr_alloc(vport, rsrc);
if (err) {
dev_err(&adapter->pdev->dev, "Failed to allocate interrupts for vport %u: %d\n",
@@ -1599,7 +1446,7 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl)
queues_rel:
idpf_vport_queues_rel(vport, rsrc);
intr_rel:
- idpf_vport_intr_rel(rsrc);
+ idpf_vport_intr_rel(rsrc, &adapter->irq);
err_rtnl_unlock:
if (rtnl)
@@ -2062,7 +1909,8 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport,
memcpy(vport, new_vport, offsetof(struct idpf_vport, link_up));
if (reset_cause == IDPF_SR_Q_CHANGE)
- idpf_vport_alloc_vec_indexes(vport, &vport->dflt_qv_rsrc);
+ idpf_vport_set_num_q_vectors(&vport->dflt_qv_rsrc,
+ vport->num_xdp_txq);
err = idpf_set_real_num_queues(vport);
if (err)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
index 1e4dd9b713a0..e27ad0e6e4e6 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_main.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -18,6 +18,7 @@ MODULE_IMPORT_NS("LIBETH");
MODULE_IMPORT_NS("LIBIE_CP");
MODULE_IMPORT_NS("LIBIE_PCI");
MODULE_IMPORT_NS("LIBETH_XDP");
+MODULE_IMPORT_NS("LIBIE_IRQ");
MODULE_LICENSE("GPL");
/**
@@ -177,7 +178,6 @@ static void idpf_remove(struct pci_dev *pdev)
adapter->netdevs = NULL;
mutex_destroy(&adapter->vport_ctrl_lock);
- mutex_destroy(&adapter->vector_lock);
mutex_destroy(&adapter->queue_lock);
mutex_destroy(&adapter->vc_buf_lock);
@@ -338,7 +338,6 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->msg_enable = netif_msg_init(-1, IDPF_AVAIL_NETIF_M);
mutex_init(&adapter->vport_ctrl_lock);
- mutex_init(&adapter->vector_lock);
mutex_init(&adapter->queue_lock);
mutex_init(&adapter->vc_buf_lock);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index 2a3dd04ebe23..a75d4cc35013 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -3823,14 +3823,18 @@ static void idpf_vport_intr_napi_dis_all(struct idpf_q_vec_rsrc *rsrc)
/**
* idpf_vport_intr_rel - Free memory allocated for interrupt vectors
* @rsrc: pointer to queue and vector resources
+ * @irq: libie_irq structure to be passed to libie_irq_free()
*
* Free the memory allocated for interrupt vectors associated to a vport
*/
-void idpf_vport_intr_rel(struct idpf_q_vec_rsrc *rsrc)
+void idpf_vport_intr_rel(struct idpf_q_vec_rsrc *rsrc, struct libie_irq *irq)
{
+ libie_put_irq(irq, rsrc->noirq_v_idx);
+
for (u16 v_idx = 0; v_idx < rsrc->num_q_vectors; v_idx++) {
struct idpf_q_vector *q_vector = &rsrc->q_vectors[v_idx];
+ libie_irq_free(irq, q_vector->irq);
kfree(q_vector->xsksq);
q_vector->xsksq = NULL;
kfree(q_vector->complq);
@@ -3863,15 +3867,17 @@ static void idpf_q_vector_set_napi(struct idpf_q_vector *q_vector, bool link)
/**
* idpf_vport_intr_rel_irq - Free the IRQ association with the OS
+ * @vport: main vport structure
* @rsrc: pointer to queue and vector resources
*/
-static void idpf_vport_intr_rel_irq(struct idpf_q_vec_rsrc *rsrc)
+static void idpf_vport_intr_rel_irq(struct idpf_vport *vport,
+ struct idpf_q_vec_rsrc *rsrc)
{
for (int vector = 0; vector < rsrc->num_q_vectors; vector++) {
struct idpf_q_vector *q_vector = &rsrc->q_vectors[vector];
/* free only the irqs that were actually requested */
- if (!q_vector)
+ if (!q_vector->irq.virq)
continue;
idpf_q_vector_set_napi(q_vector, false);
@@ -4044,10 +4050,10 @@ static int idpf_vport_intr_req_irq(struct idpf_vport *vport,
{
struct idpf_adapter *adapter = vport->adapter;
const char *drv_name, *if_name, *vec_name;
- int vector, err, vidx;
+ int vector, err;
- vidx = rsrc->q_vector_idxs[rsrc->num_q_vectors];
- adapter->dev_ops.reg_ops.noirq_intr_reg_init(adapter, rsrc, vidx);
+ adapter->dev_ops.reg_ops.noirq_intr_reg_init(adapter, rsrc,
+ rsrc->noirq_v_idx);
drv_name = dev_driver_string(&adapter->pdev->dev);
if_name = netdev_name(vport->netdev);
@@ -4057,9 +4063,8 @@ static int idpf_vport_intr_req_irq(struct idpf_vport *vport,
int virq = q_vector->irq.virq;
char *name;
- vidx = rsrc->q_vector_idxs[vector];
-
- adapter->dev_ops.reg_ops.intr_reg_init(adapter, q_vector, vidx);
+ adapter->dev_ops.reg_ops.intr_reg_init(adapter, q_vector,
+ q_vector->irq.index);
if (q_vector->num_rxq && q_vector->num_txq)
vec_name = "TxRx";
@@ -4186,7 +4191,7 @@ void idpf_vport_intr_deinit(struct idpf_vport *vport,
idpf_vport_intr_napi_dis_all(rsrc);
idpf_vport_intr_dis_dim_all(rsrc);
idpf_vport_intr_napi_del_all(rsrc);
- idpf_vport_intr_rel_irq(rsrc);
+ idpf_vport_intr_rel_irq(vport, rsrc);
}
/**
@@ -4496,31 +4501,38 @@ static void idpf_vport_intr_map_vector_to_qs(struct idpf_vport *vport,
/**
* idpf_vport_intr_init_vec_idx - Initialize the vector indexes
* @vport: virtual port
- * @rsrc: pointer to queue and vector resources
+ * @tmp_irqs: place to store reserved irqs number
+ * @num_vec: number of q_vectors
*
- * Initialize vector indexes with values returned over mailbox.
+ * Initialize vector indexes with values returned from libie_irq
*
- * Return: 0 on success, negative on failure
+ * Return: number of initialized vectors, or negative value in case of error
*/
static int idpf_vport_intr_init_vec_idx(struct idpf_vport *vport,
- struct idpf_q_vec_rsrc *rsrc)
+ struct msi_map *tmp_irqs, u16 num_vec)
{
struct idpf_adapter *adapter = vport->adapter;
int i;
- for (i = 0; i < rsrc->num_q_vectors; i++) {
- struct idpf_q_vector *q_vector = &rsrc->q_vectors[i];
+ for (i = 0; i < num_vec; i++) {
+ if (vport->default_vport && i == 0) {
+ tmp_irqs[i] = libie_irq_alloc(&adapter->irq,
+ LIBIE_IRQ_STATIC);
+ if (tmp_irqs[i].index < 0)
+ return tmp_irqs[i].index;
+ continue;
+ }
- q_vector->irq.index = rsrc->q_vector_idxs[i];
- q_vector->irq.virq = pci_irq_vector(adapter->pdev,
- q_vector->irq.index);
- if (q_vector->irq.virq < 0)
- return q_vector->irq.virq;
+ tmp_irqs[i] = libie_irq_alloc(&adapter->irq, LIBIE_IRQ_DYNAMIC);
+ /* not crucial if there is already allocated irq */
+ if (tmp_irqs[i].index < 0) {
+ if (i == 0)
+ return tmp_irqs[i].index;
+ break;
+ }
}
- rsrc->noirq_v_idx = rsrc->q_vector_idxs[i];
-
- return 0;
+ return i;
}
/**
@@ -4561,17 +4573,49 @@ int idpf_vport_intr_alloc(struct idpf_vport *vport,
{
u16 txqs_per_vector, rxqs_per_vector, bufqs_per_vector;
struct idpf_vport_user_config_data *user_config;
+ struct idpf_adapter *adapter = vport->adapter;
struct idpf_q_vector *q_vector;
struct idpf_q_coalesce *q_coal;
+ struct msi_map *tmp_irqs;
+ int irqs, i, noirq_v_idx;
u32 complqs_per_vector;
u16 idx = vport->idx;
- user_config = &vport->adapter->vport_config[idx]->user_config;
+ user_config = &adapter->vport_config[idx]->user_config;
+ tmp_irqs = kzalloc_objs(struct msi_map, rsrc->num_q_vectors);
+ if (!tmp_irqs)
+ return -ENOMEM;
+
+ noirq_v_idx = libie_irq_reserve(&adapter->irq);
+ if (noirq_v_idx < 0) {
+ kfree(tmp_irqs);
+ return noirq_v_idx;
+ }
+
+ rsrc->noirq_v_idx = noirq_v_idx;
+
+ irqs = idpf_vport_intr_init_vec_idx(vport, tmp_irqs,
+ rsrc->num_q_vectors);
+ if (irqs < 0) {
+ libie_put_irq(&adapter->irq, rsrc->noirq_v_idx);
+ kfree(tmp_irqs);
+ return irqs;
+ }
+
+ rsrc->num_q_vectors = irqs;
rsrc->q_vectors = kzalloc_objs(struct idpf_q_vector,
rsrc->num_q_vectors);
- if (!rsrc->q_vectors)
+ if (!rsrc->q_vectors) {
+ libie_put_irq(&adapter->irq, rsrc->noirq_v_idx);
+ for (i = 0; i < rsrc->num_q_vectors; i++)
+ libie_irq_free(&adapter->irq, tmp_irqs[i]);
+ kfree(tmp_irqs);
return -ENOMEM;
+ }
+
+ for (i = 0; i < rsrc->num_q_vectors; i++)
+ rsrc->q_vectors[i].irq = tmp_irqs[i];
txqs_per_vector = DIV_ROUND_UP(rsrc->num_txq_grp,
rsrc->num_q_vectors);
@@ -4627,10 +4671,12 @@ int idpf_vport_intr_alloc(struct idpf_vport *vport,
goto error;
}
+ kfree(tmp_irqs);
return 0;
error:
- idpf_vport_intr_rel(rsrc);
+ idpf_vport_intr_rel(rsrc, &adapter->irq);
+ kfree(tmp_irqs);
return -ENOMEM;
}
@@ -4646,10 +4692,6 @@ int idpf_vport_intr_init(struct idpf_vport *vport, struct idpf_q_vec_rsrc *rsrc)
{
int err;
- err = idpf_vport_intr_init_vec_idx(vport, rsrc);
- if (err)
- return err;
-
idpf_vport_intr_map_vector_to_qs(vport, rsrc);
idpf_vport_intr_napi_add_all(vport, rsrc);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
index 9a2e3665277f..f40a0a67d7ea 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h
@@ -1079,7 +1079,7 @@ int idpf_vport_queues_alloc(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc);
void idpf_vport_queues_rel(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc);
-void idpf_vport_intr_rel(struct idpf_q_vec_rsrc *rsrc);
+void idpf_vport_intr_rel(struct idpf_q_vec_rsrc *rsrc, struct libie_irq *irq);
int idpf_vport_intr_alloc(struct idpf_vport *vport,
struct idpf_q_vec_rsrc *rsrc);
void idpf_vport_intr_update_itr_ena_irq(struct idpf_q_vector *q_vector);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index 90f3c40c7928..65ddc6f893fc 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -2251,7 +2251,7 @@ static int idpf_create_vectors_info(struct idpf_irq_info *info,
if (le16_to_cpu(vectors->num_vectors) < num_vectors)
return -EINVAL;
- info->vectors = kzalloc_objs(*info->vectors, num_vectors + IDPF_MBX_Q_VEC);
+ info->vectors = kzalloc_objs(*info->vectors, all_vectors);
if (!info->vectors)
return -ENOMEM;
/* Mailbox irq information are stored in different places. Fill index 0
@@ -3257,47 +3257,16 @@ void idpf_vc_core_deinit(struct idpf_adapter *adapter)
}
/**
- * idpf_vport_alloc_vec_indexes - Get relative vector indexes
- * @vport: virtual port data struct
+ * idpf_vport_set_num_q_vectors - Set the number of vectors
* @rsrc: pointer to queue and vector resources
+ * @xdpqs: number of noirq queues (XDP)
*
- * This function requests the vector information required for the vport and
- * stores the vector indexes received from the 'global vector distribution'
- * in the vport's queue vectors array.
- *
- * Return: 0 on success, error on failure
+ * This function set the number of q_vectors for the vport.
*/
-int idpf_vport_alloc_vec_indexes(struct idpf_vport *vport,
- struct idpf_q_vec_rsrc *rsrc)
+void idpf_vport_set_num_q_vectors(struct idpf_q_vec_rsrc *rsrc, u16 xdpqs)
{
- struct idpf_vector_info vec_info;
- int num_alloc_vecs;
- u32 req;
-
- vec_info.num_curr_vecs = rsrc->num_q_vectors;
- if (vec_info.num_curr_vecs)
- vec_info.num_curr_vecs += IDPF_RESERVED_VECS;
-
/* XDPSQs are all bound to the NOIRQ vector from IDPF_RESERVED_VECS */
- req = max(rsrc->num_txq - vport->num_xdp_txq, rsrc->num_rxq) +
- IDPF_RESERVED_VECS;
- vec_info.num_req_vecs = req;
-
- vec_info.default_vport = vport->default_vport;
- vec_info.index = vport->idx;
-
- num_alloc_vecs = idpf_req_rel_vector_indexes(vport->adapter,
- rsrc->q_vector_idxs,
- &vec_info);
- if (num_alloc_vecs <= 0) {
- dev_err(&vport->adapter->pdev->dev, "Vector distribution failed: %d\n",
- num_alloc_vecs);
- return -EINVAL;
- }
-
- rsrc->num_q_vectors = num_alloc_vecs - IDPF_RESERVED_VECS;
-
- return 0;
+ rsrc->num_q_vectors = max(rsrc->num_txq - xdpqs, rsrc->num_rxq);
}
/**
@@ -3356,7 +3325,7 @@ int idpf_vport_init(struct idpf_vport *vport, struct idpf_vport_max_q *max_q)
idpf_vport_init_num_qs(vport, vport_msg, rsrc);
idpf_vport_calc_num_q_desc(vport, rsrc);
idpf_vport_calc_num_q_groups(rsrc);
- idpf_vport_alloc_vec_indexes(vport, rsrc);
+ idpf_vport_set_num_q_vectors(rsrc, vport->num_xdp_txq);
vport->crc_enable = adapter->crc_enable;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
index ec4c79191c85..8fad35a8c64c 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.h
@@ -124,8 +124,7 @@ int idpf_send_delete_queues_msg(struct idpf_adapter *adapter,
struct idpf_queue_id_reg_info *chunks,
u32 vport_id);
-int idpf_vport_alloc_vec_indexes(struct idpf_vport *vport,
- struct idpf_q_vec_rsrc *rsrc);
+void idpf_vport_set_num_q_vectors(struct idpf_q_vec_rsrc *rsrc, u16 xdpqs);
int idpf_send_alloc_vectors_msg(struct idpf_adapter *adapter, u16 num_vectors);
int idpf_send_dealloc_vectors_msg(struct idpf_adapter *adapter);
int idpf_send_map_unmap_queue_vector_msg(struct idpf_adapter *adapter,
diff --git a/drivers/net/ethernet/intel/libie/Kconfig b/drivers/net/ethernet/intel/libie/Kconfig
index 9c5fdebb6766..47fb77191604 100644
--- a/drivers/net/ethernet/intel/libie/Kconfig
+++ b/drivers/net/ethernet/intel/libie/Kconfig
@@ -37,3 +37,9 @@ config LIBIE_PCI
help
Helper functions for management of PCI resources belonging
to networking devices.
+
+config LIBIE_IRQ
+ tristate
+ help
+ Helpers used to manage irqs by drivers, take care of managing static
+ and dynamic interrupts, using xarray to store irqs and track usage.
diff --git a/drivers/net/ethernet/intel/libie/Makefile b/drivers/net/ethernet/intel/libie/Makefile
index 3065aa057798..380af47398d8 100644
--- a/drivers/net/ethernet/intel/libie/Makefile
+++ b/drivers/net/ethernet/intel/libie/Makefile
@@ -20,3 +20,7 @@ libie_fwlog-y := fwlog.o
obj-$(CONFIG_LIBIE_PCI) += libie_pci.o
libie_pci-y := pci.o
+
+obj-$(CONFIG_LIBIE_IRQ) += libie_irq.o
+
+libie_irq-y := irq.o
diff --git a/drivers/net/ethernet/intel/libie/irq.c b/drivers/net/ethernet/intel/libie/irq.c
new file mode 100644
index 000000000000..0f73f8becefb
--- /dev/null
+++ b/drivers/net/ethernet/intel/libie/irq.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include <linux/net/intel/libie/irq.h>
+
+/**
+ * libie_irq_init - init irq for whole device
+ * @irq: pointer to libie_irq structure
+ * @pdev: pdev which interrupts is used
+ * @min: number of static interrupts
+ * @max: max - min is the number of dynamic interrupts
+ *
+ * Function will call pci_alloc_irq_vectors(). Minimum vectors value is a number
+ * of LIBIE_IRQ_STATIC. It means that if there is no enough interrupts for
+ * all static interrupts this function will return -ENOSPC.
+ *
+ * If there is no support for dynamic interrupts allocation
+ * pci_alloc_irq_vectors() is called with max value, otherwise max is equal to
+ * min. There is no need to change the limits values for LIBIE_IRQ_DYNAMIC.
+ * The libie_irq_alloc() function is changing the LIBIE_IRQ_DYNAMIC to
+ * LIBIE_IRQ_STATIC when there is no such support. User of this helpers doesn't
+ * have to think if there is dynamic support or there isn't.
+ *
+ * Limits' max is the last index that can be used. Assuming 0 index is valid
+ * (which is true here) there is need to subtract one from the number of
+ * interrupts.
+ * Ex. min = 5 -> limits.min = 0, limits.max = 4; indexes 0, 1, 2, 3, 4
+ *
+ * Return: 0 in case of success otherwise -ENOSPC or -EINVAL when called
+ * with min == 0
+ */
+int libie_irq_init(struct libie_irq *irq, struct pci_dev *pdev,
+ int min, int max)
+{
+ int vectors;
+
+ /* At least one static vector needs to be allocated */
+ if (!min || min > max)
+ return -EINVAL;
+
+ irq->limits[LIBIE_IRQ_STATIC].min = 0;
+ irq->limits[LIBIE_IRQ_STATIC].max = min - 1;
+ irq->limits[LIBIE_IRQ_DYNAMIC].min = min;
+ irq->limits[LIBIE_IRQ_DYNAMIC].max = max - 1;
+
+ if (!pci_msix_can_alloc_dyn(pdev))
+ irq->limits[LIBIE_IRQ_STATIC].max = max - 1;
+ else
+ /* max can be lowered, as rest can be allocated dynamically */
+ max = min;
+
+ vectors = pci_alloc_irq_vectors(pdev, min, max, PCI_IRQ_MSIX);
+ if (vectors < 0)
+ return vectors;
+
+ /* static vectors needs to be lowered if there is not enough irqs */
+ if (irq->limits[LIBIE_IRQ_STATIC].max + 1 > vectors)
+ irq->limits[LIBIE_IRQ_STATIC].max = vectors - 1;
+
+ irq->pdev = pdev;
+ xa_init_flags(&irq->entries, XA_FLAGS_ALLOC);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(libie_irq_init, "LIBIE_IRQ");
+
+/**
+ * libie_irq_deinit - deinit irq initialized by libie_irq_init()
+ * @irq: libie_irq stored in driver data
+ *
+ * Should be called after all irqs are cleaned by libie_put_irq()
+ */
+void libie_irq_deinit(struct libie_irq *irq)
+{
+ struct libie_irq_entry *entry;
+ unsigned long i;
+
+ if (!irq->pdev)
+ return;
+
+ xa_for_each(&irq->entries, i, entry)
+ kfree(entry);
+ xa_destroy(&irq->entries);
+ pci_free_irq_vectors(irq->pdev);
+ irq->pdev = NULL;
+}
+EXPORT_SYMBOL_NS_GPL(libie_irq_deinit, "LIBIE_IRQ");
+
+/**
+ * libie_get_irq - get new allocated entry for specific irq type
+ * @irq: libie_irq structure used to get limits and entries xarray
+ * @type: one of the enum libie_irq_type
+ *
+ * Return: struct libie_irq_entry * in case of success or NULL otherwise
+ */
+static struct libie_irq_entry *libie_get_irq(struct libie_irq *irq,
+ enum libie_irq_type type)
+{
+ struct libie_irq_entry *entry;
+ unsigned int index;
+
+ if (!irq->pdev)
+ return NULL;
+
+ /* Change entry type if dynamic isn't supported. Reflect correct type
+ * to not call pci_msix_free_irq() during freeing this irq.
+ */
+ if (!pci_msix_can_alloc_dyn(irq->pdev))
+ type = LIBIE_IRQ_STATIC;
+
+ entry = kzalloc_obj(*entry);
+ if (!entry)
+ return NULL;
+
+ if (xa_alloc(&irq->entries, &index, entry, irq->limits[type],
+ GFP_KERNEL))
+ goto free_entry;
+
+ entry->index = index;
+ entry->type = type;
+
+ return entry;
+
+free_entry:
+ kfree(entry);
+ return NULL;
+}
+
+/**
+ * libie_put_irq - inform that the irq isn't used anymore
+ * @irq: libie_irq structure used to get entries xarray
+ * @index: software 0-based index of irq to be marked as unused
+ */
+void libie_put_irq(struct libie_irq *irq, unsigned int index)
+{
+ struct libie_irq_entry *entry;
+
+ entry = xa_erase(&irq->entries, index);
+ kfree(entry);
+}
+EXPORT_SYMBOL_NS_GPL(libie_put_irq, "LIBIE_IRQ");
+
+/**
+ * libie_irq_alloc - alloc new irq, or get existing one in case of static
+ * @irq: libie_irq structure
+ * @type: one of enum libie_irq_type
+ *
+ *
+ * For LIBIE_IRQ_DYNAMIC function allocs new interrupt and return it.
+ * For LIBIE_IRQ_STATIC function returns already allocated one.
+ *
+ * The function should be called for getting irq information (index and virq)
+ * for specific irq type. Returned information should be stored to use index for
+ * gathering HW specific information and virq to request/free irq line.
+ *
+ * Calling this function with LIBIE_IRQ_DYNAMIC type when dynamic irq isn't
+ * support is fine and will use limits from static field set in
+ * libie_irq_init().
+ *
+ * Return: map.index = -ENOENT if there is no free interrupts of chosen type
+ * map.index = -EINVAL if pci_irq_vector() fails
+ * correct map.index and map.virq if everything is fine
+ */
+struct msi_map libie_irq_alloc(struct libie_irq *irq, enum libie_irq_type type)
+{
+ struct msi_map map = { .index = -ENOENT,
+ .virq = 0 };
+ struct libie_irq_entry *entry;
+
+ entry = libie_get_irq(irq, type);
+ if (!entry)
+ return map;
+
+ if (entry->type == LIBIE_IRQ_DYNAMIC) {
+ map = pci_msix_alloc_irq_at(irq->pdev, entry->index, NULL);
+ if (map.index < 0)
+ goto put_irq;
+ } else {
+ map.index = entry->index;
+ map.virq = pci_irq_vector(irq->pdev, map.index);
+ if (map.virq < 0) {
+ /* In dynamic case error is in .index, put it there
+ * also for static case to allow the caller always look
+ * for an error in the same place.
+ */
+ map.index = map.virq;
+ goto put_irq;
+ }
+ }
+
+ return map;
+
+put_irq:
+ libie_put_irq(irq, entry->index);
+ return map;
+}
+EXPORT_SYMBOL_NS_GPL(libie_irq_alloc, "LIBIE_IRQ");
+
+/**
+ * libie_irq_free - free irq, allocated using libie_alloc_irq()
+ * @irq: libie_irq structure
+ * @map: msi_map structure returned from libie_alloc_irq()
+ *
+ * In case of dynamic allocation and LIBIE_IRQ_DYNAMIC type pci_msix_free_irq()
+ * is called. Otherwise only free driver irq entry related resources.
+ *
+ * It is safe to call this function with map that doesn't exist in xarray
+ * as long as the map.virq is 0 or negative. It is true when libie_irq_alloc()
+ * has failed.
+ */
+void libie_irq_free(struct libie_irq *irq, struct msi_map map)
+{
+ struct libie_irq_entry *entry;
+
+ if (map.virq <= 0 || map.index < 0)
+ return;
+
+ entry = xa_load(&irq->entries, map.index);
+ if (!entry)
+ return;
+
+ if (entry->type == LIBIE_IRQ_DYNAMIC)
+ pci_msix_free_irq(irq->pdev, map);
+
+ libie_put_irq(irq, map.index);
+}
+EXPORT_SYMBOL_NS_GPL(libie_irq_free, "LIBIE_IRQ");
+
+/**
+ * libie_irq_reserve - reserve a interrupt index without allocating MSI-X
+ * @irq: libie_irq structure containing interrupt management data
+ *
+ * This function reserves an interrupt index from the dynamic range without
+ * actually allocating the corresponding MSI-X vector. The reserved index can
+ * be used for hardware queue configuration before the actual interrupt
+ * allocation. The caller should use libie_put_irq() to release the reserved
+ * index when no longer needed.
+ *
+ * Return: Reserved interrupt index on success, or -ENOENT if no dynamic
+ * interrupt indices are available.
+ */
+int libie_irq_reserve(struct libie_irq *irq)
+{
+ struct libie_irq_entry *ent = libie_get_irq(irq, LIBIE_IRQ_DYNAMIC);
+
+ if (!ent)
+ return -ENOENT;
+
+ return ent->index;
+}
+EXPORT_SYMBOL_NS_GPL(libie_irq_reserve, "LIBIE_IRQ");
+
+/* Module */
+
+MODULE_DESCRIPTION("Helper functions for managing MSI-X in driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/net/intel/libie/irq.h b/include/linux/net/intel/libie/irq.h
new file mode 100644
index 000000000000..8af8fc22ab09
--- /dev/null
+++ b/include/linux/net/intel/libie/irq.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2025 Intel Corporation */
+
+#ifndef __LIBIE_IRQ_H
+#define __LIBIE_IRQ_H
+
+#include <linux/pci.h>
+
+/* In whole code in libie_irq index means the software 0-based irq index
+ * for driver purpose, virq means the linux irq line number. Index can be used
+ * for getting HW registers address and HW indexes (ex. for idpf there is
+ * another irq index used in virtchnl communication which isn't driver index or
+ * linux irq number). It is following the scheme from structure msi_map.
+ */
+
+/**
+ * enum libie_irq_type - enum representing types of irq entries
+ * @LIBIE_IRQ_STATIC: irq static allocated from kernel at driver probe
+ * @LIBIE_IRQ_DYNAMIC: irq dynamic allocated during normal driver operation
+ * @LIBIE_IRQ_NUM_TYPES: must be the last one, used to define the array size
+ *
+ * Enum is used to get software irq indexes from some kind of pool. The pool is
+ * based on xa_array. Depending on the limit value passed to xa_alloc() software
+ * irq indexes only from limited range can be returned.
+ *
+ * LIBIE_IRQ_STATIC .min = 0, max = 5 -> 5 irq static allocated to be sure that
+ * all default vport can operate
+ * LIBIE_IRQ_DYNAMIC .min = 6, max = HW irq max -> rest to be dynamically used
+ * when needed
+ */
+enum libie_irq_type {
+ LIBIE_IRQ_STATIC,
+ LIBIE_IRQ_DYNAMIC,
+ LIBIE_IRQ_NUM_TYPES,
+};
+
+/**
+ * struct libie_irq_entry - structure to store irq entry information
+ * @index: managed by software 0 based irq index, used to get correct hardware
+ * information about irq (HW index and HW registers address)
+ * @type: the type of irq, look at enum libie_irq_type for more information
+ *
+ * This structure is used to store the basic information about irq used during
+ * alloc and free. Type needs to be known, because freeing dynamic type needs
+ * extra call.
+ */
+struct libie_irq_entry {
+ int index;
+ enum libie_irq_type type;
+};
+
+/**
+ * struct libie_irq - main structure to be used by libie_irq code
+ * @pdev: pdev of driver that is using this lib
+ * @limits: the irq pool scheme definition, take a look at irq_type note
+ * @entries: xarray to store irq entries
+ *
+ * pdev and limits values need to be passed by the driver during lib
+ * initialization.
+ */
+struct libie_irq {
+ struct pci_dev *pdev;
+ struct xa_limit limits[LIBIE_IRQ_NUM_TYPES];
+ struct xarray entries;
+};
+
+int libie_irq_init(struct libie_irq *irq, struct pci_dev *pdev,
+ int min, int max);
+void libie_irq_deinit(struct libie_irq *irq);
+struct msi_map libie_irq_alloc(struct libie_irq *irq, enum libie_irq_type type);
+void libie_irq_free(struct libie_irq *irq, struct msi_map map);
+int libie_irq_reserve(struct libie_irq *irq);
+void libie_put_irq(struct libie_irq *irq, unsigned int index);
+
+#endif /* __LIBIE_IRQ_H */
--
2.49.0
next prev parent reply other threads:[~2026-09-11 13:29 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 12:49 [PATCH iwl-next v2 00/10] Interrupts helper in libie Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 01/10] idpf: store HW vectors information Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 02/10] idpf: fill q_vector interrupt registers one by one Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 03/10] idpf: get rid of msix_entries array Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 04/10] idpf: drop v_idx from q_vector structure Michal Swiatkowski
2026-09-11 12:49 ` Michal Swiatkowski [this message]
2026-09-11 12:49 ` [PATCH iwl-next v2 06/10] libie, idpf: move hardware irq info struct to libie Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 07/10] libie, idpf: move parsing alloc vectors command " Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 08/10] ice: use libie_irq for interrupts managing Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 09/10] ixd: support for getting lan memory regions Michal Swiatkowski
2026-09-11 12:49 ` [PATCH iwl-next v2 10/10] ixd: use interrupt for mailbox communication Michal Swiatkowski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260911124921.2881348-6-michal.swiatkowski@linux.intel.com \
--to=michal.swiatkowski@linux.intel.com \
--cc=aleksandr.loktionov@intel.com \
--cc=intel-wired-lan@lists.osuosl.org \
--cc=larysa.zaremba@intel.com \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox