diff mbox series

[net-next,RFC,5/5] ice: Add driver support for ndo_queue_set_napi

Message ID 171234779427.5075.586255342877398659.stgit@anambiarhost.jf.intel.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series Configuring NAPI instance for a queue | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; GEN HAS DIFF 2 files changed, 159 insertions(+);
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 943 this patch: 943
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 2 maintainers not CCed: jesse.brandeburg@intel.com intel-wired-lan@lists.osuosl.org
netdev/build_clang success Errors and warnings before: 953 this patch: 953
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 954 this patch: 954
netdev/checkpatch warning WARNING: line length of 82 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc fail Errors and warnings before: 196 this patch: 202
netdev/source_inline success Was 0 now: 0

Commit Message

Nambiar, Amritha April 5, 2024, 8:09 p.m. UTC
Add support in the ice driver to change the NAPI instance
associated with the queue. This is achieved by updating the
interrupt vector association for the queue.

Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
---
 drivers/net/ethernet/intel/ice/ice_lib.c  |  311 +++++++++++++++++++++++++++++
 drivers/net/ethernet/intel/ice/ice_lib.h  |    2 
 drivers/net/ethernet/intel/ice/ice_main.c |    1 
 3 files changed, 310 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 419d9561bc2a..3a93b53a0da0 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4186,7 +4186,7 @@  static void ice_qvec_configure(struct ice_vsi *vsi, struct ice_q_vector *q_vecto
  * @vsi: the VSI that contains queue vector
  * @q_vector: queue vector
  */
-static int __maybe_unused
+static int
 ice_q_vector_dis(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
 {
 	struct ice_hw *hw = &vsi->back->hw;
@@ -4221,7 +4221,7 @@  ice_q_vector_dis(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
  * @vsi: the VSI that contains queue vector
  * @q_vector: queue vector
  */
-static int __maybe_unused
+static int
 ice_q_vector_ena(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
 {
 	struct ice_rx_ring *rx_ring;
@@ -4278,7 +4278,7 @@  ice_qvec_release_msix(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
  * @vsi: the VSI that contains queue vector
  * @q_vector: queue vector
  */
-static void __maybe_unused
+static void
 ice_qvec_free(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
 {
 	int irq_num = q_vector->irq.virq;
@@ -4309,7 +4309,7 @@  ice_qvec_free(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
  * @vsi: the VSI that contains queue vector
  * @q_vector: queue vector
  */
-static int __maybe_unused
+static int
 ice_qvec_prep(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
 {
 	struct ice_pf *pf = vsi->back;
@@ -4365,3 +4365,306 @@  ice_qvec_prep(struct ice_vsi *vsi, struct ice_q_vector *q_vector)
 
 	return err;
 }
+
+/**
+ * ice_vsi_rename_irq_msix
+ * @vsi: VSI being configured
+ * @basename: name for the vector
+ *
+ * Rename the vector. The default vector names assumed a 1:1 mapping between
+ * queues and vectors in a serial fashion. When the NAPI association for the
+ * queue is changed, it is possible to have multiple queues sharing a vector
+ * in a non-serial way.
+ */
+static void ice_vsi_rename_irq_msix(struct ice_vsi *vsi, char *basename)
+{
+	int q_vectors = vsi->num_q_vectors;
+	int vector, err;
+
+	for (vector = 0; vector < q_vectors; vector++) {
+		struct ice_q_vector *q_vector = vsi->q_vectors[vector];
+
+		if (q_vector->tx.tx_ring && q_vector->rx.rx_ring) {
+			err = snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+				       "%s-%s", basename, "TxRx");
+		} else if (q_vector->rx.rx_ring) {
+			err = snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+				       "%s-%s", basename, "rx");
+		} else if (q_vector->tx.tx_ring) {
+			err = snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+				       "%s-%s", basename, "tx");
+		} else {
+			err = snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+				       "%s", basename);
+		}
+		/* Catching the return quiets a Wformat-truncation complaint */
+		if (err > sizeof(q_vector->name) - 1)
+			netdev_dbg(vsi->netdev, "vector name truncated, ignore\n");
+	}
+}
+
+/**
+ * ice_tx_ring_unmap_qvec - Unmap tx ring from its current q_vector
+ * @tx_ring: rx ring to be removed
+ *
+ * Unmap tx ring from its current vector association in SW
+ */
+static void
+ice_tx_ring_unmap_qvec(struct ice_tx_ring *tx_ring)
+{
+	struct ice_q_vector *q_vector = tx_ring->q_vector;
+	struct ice_tx_ring *prev, *ring;
+
+	/* Remove a tx ring from its corresponding vector's ring container */
+	ring = q_vector->tx.tx_ring;
+	if (!ring)
+		return;
+
+	if (tx_ring == ring) {
+		q_vector->tx.tx_ring = tx_ring->next;
+		q_vector->num_ring_tx--;
+		return;
+	}
+
+	while (ring && ring != tx_ring) {
+		prev = ring;
+		ring = ring->next;
+	}
+	if (!ring)
+		return;
+	prev->next = ring->next;
+	q_vector->num_ring_tx--;
+}
+
+/**
+ * ice_rx_ring_unmap_qvec - Unmap rx ring from its current q_vector
+ * @rx_ring: rx ring to be removed
+ *
+ * Unmap rx ring from its current vector association in SW
+ */
+static void
+ice_rx_ring_unmap_qvec(struct ice_rx_ring *rx_ring)
+{
+	struct ice_q_vector *q_vector = rx_ring->q_vector;
+	struct ice_rx_ring *prev, *ring;
+
+	/* Remove a rx ring from its corresponding vector's ring container */
+	ring = q_vector->rx.rx_ring;
+	if (!ring)
+		return;
+
+	if (rx_ring == ring) {
+		q_vector->rx.rx_ring = rx_ring->next;
+		q_vector->num_ring_rx--;
+		return;
+	}
+
+	while (ring && ring != rx_ring) {
+		prev = ring;
+		ring = ring->next;
+	}
+	if (!ring)
+		return;
+	prev->next = ring->next;
+	q_vector->num_ring_rx--;
+}
+
+static int
+ice_tx_queue_update_q_vector(struct ice_vsi *vsi, u32 q_idx,
+			     struct ice_q_vector *new_qvec)
+{
+	struct ice_q_vector *old_qvec;
+	struct ice_tx_ring *tx_ring;
+	int timeout = 50;
+	int err;
+
+	if (q_idx >= vsi->num_txq)
+		return -EINVAL;
+	tx_ring = vsi->tx_rings[q_idx];
+	if (!tx_ring)
+		return -EINVAL;
+	old_qvec = tx_ring->q_vector;
+
+	if (old_qvec->irq.virq == new_qvec->irq.virq)
+		return 0;
+
+	while (test_and_set_bit(ICE_CFG_BUSY, vsi->state)) {
+		timeout--;
+		if (!timeout)
+			return -EBUSY;
+		usleep_range(1000, 2000);
+	}
+
+	err = ice_q_vector_dis(vsi, old_qvec);
+	if (err)
+		return err;
+
+	ice_tx_ring_unmap_qvec(tx_ring);
+
+	/* free vector if it has no queues as all of its queues are now moved */
+	if (ice_is_q_vector_unused(old_qvec))
+		ice_qvec_free(vsi, old_qvec);
+
+	/* Prepare new q_vector if it was previously unused */
+	if (ice_is_q_vector_unused(new_qvec)) {
+		err = ice_qvec_prep(vsi, new_qvec);
+		if (err)
+			return err;
+	} else {
+		err = ice_q_vector_dis(vsi, new_qvec);
+		if (err)
+			return err;
+	}
+
+	tx_ring->q_vector = new_qvec;
+	tx_ring->next = new_qvec->tx.tx_ring;
+	new_qvec->tx.tx_ring = tx_ring;
+	new_qvec->num_ring_tx++;
+
+	err = ice_q_vector_ena(vsi, new_qvec);
+	if (err)
+		return err;
+
+	if (!ice_is_q_vector_unused(old_qvec)) {
+		err = ice_q_vector_ena(vsi, old_qvec);
+		if (err)
+			return err;
+	}
+
+	clear_bit(ICE_CFG_BUSY, vsi->state);
+
+	return 0;
+}
+
+static int
+ice_rx_queue_update_q_vector(struct ice_vsi *vsi, u32 q_idx,
+			     struct ice_q_vector *new_qvec)
+{
+	struct ice_q_vector *old_qvec;
+	struct ice_rx_ring *rx_ring;
+	int timeout = 50;
+	int err;
+
+	if (q_idx >= vsi->num_rxq)
+		return -EINVAL;
+	rx_ring = vsi->rx_rings[q_idx];
+	if (!rx_ring)
+		return -EINVAL;
+
+	old_qvec = rx_ring->q_vector;
+
+	if (old_qvec->irq.virq == new_qvec->irq.virq)
+		return 0;
+
+	while (test_and_set_bit(ICE_CFG_BUSY, vsi->state)) {
+		timeout--;
+		if (!timeout)
+			return -EBUSY;
+		usleep_range(1000, 2000);
+	}
+
+	err = ice_q_vector_dis(vsi, old_qvec);
+	if (err)
+		return err;
+
+	ice_rx_ring_unmap_qvec(rx_ring);
+
+	/* free vector if it has no queues as all of its queues are now moved */
+	if (ice_is_q_vector_unused(old_qvec))
+		ice_qvec_free(vsi, old_qvec);
+
+	/* Prepare new q_vector if it was previously unused */
+	if (ice_is_q_vector_unused(new_qvec)) {
+		err = ice_qvec_prep(vsi, new_qvec);
+		if (err)
+			return err;
+	} else {
+		err = ice_q_vector_dis(vsi, new_qvec);
+		if (err)
+			return err;
+	}
+
+	rx_ring->q_vector = new_qvec;
+	rx_ring->next = new_qvec->rx.rx_ring;
+	new_qvec->rx.rx_ring = rx_ring;
+	new_qvec->num_ring_rx++;
+
+	err = ice_q_vector_ena(vsi, new_qvec);
+	if (err)
+		return err;
+
+	if (!ice_is_q_vector_unused(old_qvec)) {
+		err = ice_q_vector_ena(vsi, old_qvec);
+		if (err)
+			return err;
+	}
+
+	clear_bit(ICE_CFG_BUSY, vsi->state);
+
+	return 0;
+}
+
+/**
+ * ice_vsi_get_vector_from_irq
+ * @vsi: the VSI being configured
+ * @irq_num: Interrupt vector number
+ *
+ * Get the q_vector from the Linux interrupt vector number
+ */
+static struct ice_q_vector *
+ice_vsi_get_vector_from_irq(struct ice_vsi *vsi, int irq_num)
+{
+	int i;
+
+	ice_for_each_q_vector(vsi, i) {
+		if (vsi->q_vectors[i]->irq.virq == irq_num)
+			return vsi->q_vectors[i];
+	}
+	return NULL;
+}
+
+/**
+ * ice_queue_change_napi - Change the NAPI instance for the queue
+ * @dev: device to which NAPI and queue belong
+ * @q_idx: Index of queue
+ * @q_type: queue type as RX or TX
+ * @napi: NAPI context for the queue
+ */
+int ice_queue_change_napi(struct net_device *dev, u32 q_idx, u32 q_type,
+			  struct napi_struct *napi)
+{
+	struct ice_netdev_priv *np = netdev_priv(dev);
+	char int_name[ICE_INT_NAME_STR_LEN];
+	struct ice_q_vector *q_vector;
+	struct ice_vsi *vsi = np->vsi;
+	struct ice_pf *pf = vsi->back;
+	int err;
+
+	q_vector = ice_vsi_get_vector_from_irq(vsi, napi->irq);
+	if (!q_vector)
+		return -EINVAL;
+
+	switch (q_type) {
+	case NETDEV_QUEUE_TYPE_RX:
+		err = ice_rx_queue_update_q_vector(vsi, q_idx, q_vector);
+		if (err)
+			return err;
+		break;
+	case NETDEV_QUEUE_TYPE_TX:
+		err = ice_tx_queue_update_q_vector(vsi, q_idx, q_vector);
+		if (err)
+			return err;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
+		 dev_driver_string(ice_pf_to_dev(pf)), vsi->netdev->name);
+	ice_vsi_rename_irq_msix(vsi, int_name);
+
+	/* Now report to the stack */
+	netif_queue_set_napi(dev, q_idx, q_type, napi);
+
+	return 0;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h b/drivers/net/ethernet/intel/ice/ice_lib.h
index 66a9709ff612..d53f399487bf 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_lib.h
@@ -170,4 +170,6 @@  ice_is_q_vector_unused(struct ice_q_vector *q_vector)
 	return (!q_vector->num_ring_tx && !q_vector->num_ring_rx);
 }
 
+int ice_queue_change_napi(struct net_device *dev, u32 q_idx, u32 q_type,
+			  struct napi_struct *napi);
 #endif /* !_ICE_LIB_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 0884b53a0b01..08c20f8b17e2 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -9494,4 +9494,5 @@  static const struct net_device_ops ice_netdev_ops = {
 	.ndo_bpf = ice_xdp,
 	.ndo_xdp_xmit = ice_xdp_xmit,
 	.ndo_xsk_wakeup = ice_xsk_wakeup,
+	.ndo_queue_set_napi = ice_queue_change_napi,
 };