diff --git a/app/test/test_event_eth_tx_adapter.c b/app/test/test_event_eth_tx_adapter.c index 98debfdd2c..c19a87a86a 100644 --- a/app/test/test_event_eth_tx_adapter.c +++ b/app/test/test_event_eth_tx_adapter.c @@ -711,6 +711,90 @@ tx_adapter_instance_get(void) return TEST_SUCCESS; } +static int +tx_adapter_queue_start_stop(void) +{ + int err; + uint16_t eth_dev_id; + struct rte_eth_dev_info dev_info; + + /* Case 1: Test without adding eth Tx queue */ + err = rte_event_eth_tx_adapter_queue_start(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + err = rte_event_eth_tx_adapter_queue_stop(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + /* Case 2: Test with wrong eth port */ + eth_dev_id = rte_eth_dev_count_total() + 1; + err = rte_event_eth_tx_adapter_queue_start(eth_dev_id, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + err = rte_event_eth_tx_adapter_queue_stop(eth_dev_id, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + /* Case 3: Test with wrong tx queue */ + err = rte_eth_dev_info_get(TEST_ETHDEV_ID, &dev_info); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + err = rte_event_eth_tx_adapter_queue_start(TEST_ETHDEV_ID, + dev_info.max_tx_queues + 1); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + err = rte_event_eth_tx_adapter_queue_stop(TEST_ETHDEV_ID, + dev_info.max_tx_queues + 1); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + /* Case 4: Test with right instance, port & rxq */ + /* Add queue to tx adapter */ + err = rte_event_eth_tx_adapter_queue_add(TEST_INST_ID, + TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + err = rte_event_eth_tx_adapter_queue_stop(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + err = rte_event_eth_tx_adapter_queue_start(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + /* Add another queue to tx adapter */ + err = rte_event_eth_tx_adapter_queue_add(TEST_INST_ID, + TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID + 1); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + err = rte_event_eth_tx_adapter_queue_stop(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID + 1); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + err = rte_event_eth_tx_adapter_queue_start(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID + 1); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + /* Case 5: Test with right instance, port & wrong rxq */ + err = rte_event_eth_tx_adapter_queue_stop(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID + 2); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + err = rte_event_eth_tx_adapter_queue_start(TEST_ETHDEV_ID, + TEST_ETH_QUEUE_ID + 2); + TEST_ASSERT(err == -EINVAL, "Expected -EINVAL got %d", err); + + /* Delete all queues from the Tx adapter */ + err = rte_event_eth_tx_adapter_queue_del(TEST_INST_ID, + TEST_ETHDEV_ID, + -1); + TEST_ASSERT(err == 0, "Expected 0 got %d", err); + + return TEST_SUCCESS; +} + static int tx_adapter_dynamic_device(void) { @@ -770,6 +854,8 @@ static struct unit_test_suite event_eth_tx_tests = { tx_adapter_service), TEST_CASE_ST(tx_adapter_create, tx_adapter_free, tx_adapter_instance_get), + TEST_CASE_ST(tx_adapter_create, tx_adapter_free, + tx_adapter_queue_start_stop), TEST_CASE_ST(NULL, NULL, tx_adapter_dynamic_device), TEST_CASES_END() /**< NULL terminate unit test array */ } diff --git a/doc/guides/prog_guide/event_ethernet_tx_adapter.rst b/doc/guides/prog_guide/event_ethernet_tx_adapter.rst index d9a720ebe6..905cb445e0 100644 --- a/doc/guides/prog_guide/event_ethernet_tx_adapter.rst +++ b/doc/guides/prog_guide/event_ethernet_tx_adapter.rst @@ -182,3 +182,28 @@ mbufs are destined to the same ethernet port and queue by setting the bit ``rte_event_vector::queue``. If ``rte_event_vector::attr_valid`` is not set then the Tx adapter should peek into each mbuf and transmit them to the requested ethernet port and queue pair. + +Queue start/stop +~~~~~~~~~~~~~~~~ + +The adapter can be configured to start/stop enqueueing of packets to a +associated NIC queue using ``rte_event_eth_tx_adapter_queue_start()`` or +``rte_event_eth_tx_adapter_queue_stop()`` respectively. By default the queue +is in start state. + +These APIs help avoid some unexpected behavior with application stopping ethdev +Tx queues and adapter being unaware of it. With these APIs, the application can +call stop API to notify adapter that corresponding ethdev Tx queue is stopped +and any in-flight packets are freed by adapter dataplane code. Adapter queue +stop API is called before stopping the ethdev Tx queue. When ethdev Tx queue +is enabled, application can notify adapter to resume processing of the packets +for that queue by calling the start API. The ethdev Tx queue is started before +calling adapter start API. + +Start function enables the adapter runtime to start enqueueing of packets +to the Tx queue. + +Stop function stops the adapter runtime function from enqueueing any +packets to the associated Tx queue. This API also frees any packets that +may have been buffered for this queue. All inflight packets destined to the +queue are freed by the adapter runtime until the queue is started again. diff --git a/doc/guides/rel_notes/release_22_11.rst b/doc/guides/rel_notes/release_22_11.rst index 510485017d..efd2d33d4c 100644 --- a/doc/guides/rel_notes/release_22_11.rst +++ b/doc/guides/rel_notes/release_22_11.rst @@ -80,6 +80,13 @@ New Features * Added ``rte_event_eth_tx_adapter_instance_get`` to get Tx adapter instance ID for specified ethernet device ID and Tx queue index. +* **Added eventdev Tx adapter queue start/stop API.** + + * Added ``rte_event_eth_tx_adapter_queue_start`` to start + enqueueing packets to the Tx queue by Tx adapter. + * Added ``rte_event_eth_tx_adapter_queue_stop`` to stop the Tx Adapter + from enqueueing any packets to the Tx queue. + Removed Items ------------- diff --git a/lib/eventdev/eventdev_pmd.h b/lib/eventdev/eventdev_pmd.h index 94abf495d6..c764a31387 100644 --- a/lib/eventdev/eventdev_pmd.h +++ b/lib/eventdev/eventdev_pmd.h @@ -1277,6 +1277,43 @@ typedef int (*eventdev_eth_tx_adapter_stats_reset_t)(uint8_t id, typedef int (*eventdev_eth_tx_adapter_instance_get_t) (uint16_t eth_dev_id, uint16_t tx_queue_id, uint8_t *txa_inst_id); +/** + * Start a Tx queue that is assigned to Tx adapter instance + * + * @param id + * Adapter identifier + * + * @param eth_dev_id + * Port identifier of Ethernet device + * + * @param tx_queue_id + * Ethernet device Tx queue index + * + * @return + * - 0: Success + * - <0: Error code on failure + */ +typedef int (*eventdev_eth_tx_adapter_queue_start) + (uint8_t id, uint16_t eth_dev_id, uint16_t tx_queue_id); + +/** + * Stop a Tx queue that is assigned to Tx adapter instance + * + * @param id + * Adapter identifier + * + * @param eth_dev_id + * Port identifier of Ethernet device + * + * @param tx_queue_id + * Ethernet device Tx queue index + * + * @return + * - 0: Success + * - <0: Error code on failure + */ +typedef int (*eventdev_eth_tx_adapter_queue_stop) + (uint8_t id, uint16_t eth_dev_id, uint16_t tx_queue_id); /** Event device operations function pointer table */ struct eventdev_ops { @@ -1390,6 +1427,10 @@ struct eventdev_ops { /**< Reset eth Tx adapter statistics */ eventdev_eth_tx_adapter_instance_get_t eth_tx_adapter_instance_get; /**< Get Tx adapter instance ID for Tx queue */ + eventdev_eth_tx_adapter_queue_start eth_tx_adapter_queue_start; + /**< Start Tx queue assigned to Tx adapter instance */ + eventdev_eth_tx_adapter_queue_stop eth_tx_adapter_queue_stop; + /**< Stop Tx queue assigned to Tx adapter instance */ eventdev_selftest dev_selftest; /**< Start eventdev Selftest */ diff --git a/lib/eventdev/rte_event_eth_tx_adapter.c b/lib/eventdev/rte_event_eth_tx_adapter.c index 170f9b3b6a..7f7d86f683 100644 --- a/lib/eventdev/rte_event_eth_tx_adapter.c +++ b/lib/eventdev/rte_event_eth_tx_adapter.c @@ -47,6 +47,12 @@ #define txa_dev_instance_get(id) \ txa_evdev(id)->dev_ops->eth_tx_adapter_instance_get +#define txa_dev_queue_start(id) \ + txa_evdev(id)->dev_ops->eth_tx_adapter_queue_start + +#define txa_dev_queue_stop(id) \ + txa_evdev(id)->dev_ops->eth_tx_adapter_queue_stop + #define RTE_EVENT_ETH_TX_ADAPTER_ID_VALID_OR_ERR_RET(id, retval) \ do { \ if (!txa_valid_id(id)) { \ @@ -94,6 +100,8 @@ struct txa_retry { struct txa_service_queue_info { /* Queue has been added */ uint8_t added; + /* Queue is stopped */ + bool stopped; /* Retry callback argument */ struct txa_retry txa_retry; /* Tx buffer */ @@ -556,7 +564,7 @@ txa_process_event_vector(struct txa_service_data *txa, port = vec->port; queue = vec->queue; tqi = txa_service_queue(txa, port, queue); - if (unlikely(tqi == NULL || !tqi->added)) { + if (unlikely(tqi == NULL || !tqi->added || tqi->stopped)) { rte_pktmbuf_free_bulk(&mbufs[vec->elem_offset], vec->nb_elem); rte_mempool_put(rte_mempool_from_obj(vec), vec); @@ -572,7 +580,8 @@ txa_process_event_vector(struct txa_service_data *txa, port = mbufs[i]->port; queue = rte_event_eth_tx_adapter_txq_get(mbufs[i]); tqi = txa_service_queue(txa, port, queue); - if (unlikely(tqi == NULL || !tqi->added)) { + if (unlikely(tqi == NULL || !tqi->added || + tqi->stopped)) { rte_pktmbuf_free(mbufs[i]); continue; } @@ -609,7 +618,8 @@ txa_service_tx(struct txa_service_data *txa, struct rte_event *ev, queue = rte_event_eth_tx_adapter_txq_get(m); tqi = txa_service_queue(txa, port, queue); - if (unlikely(tqi == NULL || !tqi->added)) { + if (unlikely(tqi == NULL || !tqi->added || + tqi->stopped)) { rte_pktmbuf_free(m); continue; } @@ -673,7 +683,8 @@ txa_service_func(void *args) for (q = 0; q < dev->data->nb_tx_queues; q++) { tqi = txa_service_queue(txa, i, q); - if (unlikely(tqi == NULL || !tqi->added)) + if (unlikely(tqi == NULL || !tqi->added || + tqi->stopped)) continue; nb_tx += rte_eth_tx_buffer_flush(i, q, @@ -868,6 +879,7 @@ txa_service_queue_add(uint8_t id, tqi->tx_buf = tb; tqi->added = 1; + tqi->stopped = false; tdi->nb_queues++; txa->nb_queues++; @@ -886,6 +898,20 @@ txa_service_queue_add(uint8_t id, return -1; } +static inline void +txa_txq_buffer_drain(struct txa_service_queue_info *tqi) +{ + struct rte_eth_dev_tx_buffer *b; + uint16_t i; + + b = tqi->tx_buf; + + for (i = 0; i < b->length; i++) + rte_pktmbuf_free(b->pkts[i]); + + b->length = 0; +} + static int txa_service_queue_del(uint8_t id, const struct rte_eth_dev *dev, @@ -931,6 +957,8 @@ txa_service_queue_del(uint8_t id, if (tqi == NULL || !tqi->added) goto ret_unlock; + /* Drain the buffered mbufs */ + txa_txq_buffer_drain(tqi); tb = tqi->tx_buf; tqi->added = 0; tqi->tx_buf = NULL; @@ -1321,3 +1349,79 @@ rte_event_eth_tx_adapter_instance_get(uint16_t eth_dev_id, return -EINVAL; } + +static inline int +txa_sw_queue_start_state_set(uint16_t eth_dev_id, uint16_t tx_queue_id, + bool start_state, struct txa_service_data *txa) +{ + struct txa_service_queue_info *tqi = NULL; + + rte_spinlock_lock(&txa->tx_lock); + tqi = txa_service_queue(txa, eth_dev_id, tx_queue_id); + if (unlikely(tqi == NULL || !tqi->added)) { + rte_spinlock_unlock(&txa->tx_lock); + return -EINVAL; + } + if (start_state == false) + txa_txq_buffer_drain(tqi); + + tqi->stopped = !start_state; + rte_spinlock_unlock(&txa->tx_lock); + return 0; +} + +static int +txa_queue_start_state_set(uint16_t eth_dev_id, uint16_t tx_queue_id, + bool start_state) +{ + struct txa_service_data *txa; + uint8_t txa_inst_id; + int ret; + uint32_t caps = 0; + + /* Below API already does validation of input parameters. + * Hence skipping the validation here. + */ + ret = rte_event_eth_tx_adapter_instance_get(eth_dev_id, + tx_queue_id, + &txa_inst_id); + if (ret < 0) + return -EINVAL; + + txa = txa_service_id_to_data(txa_inst_id); + ret = rte_event_eth_tx_adapter_caps_get(txa->eventdev_id, + eth_dev_id, + &caps); + if (ret < 0) + return -EINVAL; + + if (caps & RTE_EVENT_ETH_TX_ADAPTER_CAP_INTERNAL_PORT) { + if (start_state == true) { + ret = txa_dev_queue_start(txa_inst_id) ? + txa_dev_queue_start(txa_inst_id)(txa_inst_id, + eth_dev_id, + tx_queue_id) : 0; + } else { + ret = txa_dev_queue_stop(txa_inst_id) ? + txa_dev_queue_stop(txa_inst_id)(txa_inst_id, + eth_dev_id, + tx_queue_id) : 0; + } + return ret; + } + + return txa_sw_queue_start_state_set(eth_dev_id, tx_queue_id, + start_state, txa); +} + +int +rte_event_eth_tx_adapter_queue_start(uint16_t eth_dev_id, uint16_t tx_queue_id) +{ + return txa_queue_start_state_set(eth_dev_id, tx_queue_id, true); +} + +int +rte_event_eth_tx_adapter_queue_stop(uint16_t eth_dev_id, uint16_t tx_queue_id) +{ + return txa_queue_start_state_set(eth_dev_id, tx_queue_id, false); +} diff --git a/lib/eventdev/rte_event_eth_tx_adapter.h b/lib/eventdev/rte_event_eth_tx_adapter.h index 9432b740e8..74ded7b651 100644 --- a/lib/eventdev/rte_event_eth_tx_adapter.h +++ b/lib/eventdev/rte_event_eth_tx_adapter.h @@ -35,6 +35,8 @@ * - rte_event_eth_tx_adapter_event_port_get() * - rte_event_eth_tx_adapter_service_id_get() * - rte_event_eth_tx_adapter_instance_get() + * - rte_event_eth_tx_adapter_queue_start() + * - rte_event_eth_tx_adapter_queue_stop() * * The application creates the adapter using * rte_event_eth_tx_adapter_create() or rte_event_eth_tx_adapter_create_ext(). @@ -446,6 +448,58 @@ int rte_event_eth_tx_adapter_instance_get(uint16_t eth_dev_id, uint16_t tx_queue_id, uint8_t *txa_inst_id); +/** + * Enables the adapter to start enqueueing of packets to the + * Tx queue. + * + * This function is provided so that the application can + * resume enqueueing packets that reference packets for + * after calling + * rte_event_eth_tx_adapter_queue_stop(). + * @see rte_event_eth_tx_adapter_queue_stop + * + * Use case: + * -------- + * The queue start/stop APIs help avoid some unexpected behavior with + * application stopping ethdev Tx queues and adapter being unaware of it. + * With these APIs, the application can call stop API to notify adapter + * that corresponding ethdev Tx queue is stopped and any in-flight + * packets are freed by adapter dataplane code. Adapter queue stop API + * is called before stopping the ethdev Tx queue. When ethdev Tx queue + * is enabled, application can notify adapter to resume processing of + * the packets for that queue by calling the start API. The ethdev Tx + * queue is started before calling adapter start API. + * + * @param eth_dev_id + * Port identifier of Ethernet device. + * @param tx_queue_id + * Ethernet device transmit queue index. + * @return + * - 0: Success + * - <0: Error code on failure + */ +__rte_experimental +int +rte_event_eth_tx_adapter_queue_start(uint16_t eth_dev_id, uint16_t tx_queue_id); + +/** + * Stops the adapter runtime function from enqueueing any packets + * to the associated Tx queue. This API also frees any packets that may + * have been buffered for this queue. All inflight packets destined to the + * queue are freed by the adapter runtime until the queue is started again. + * @see rte_event_eth_tx_adapter_queue_start + * + * @param eth_dev_id + * Port identifier of Ethernet device. + * @param tx_queue_id + * Ethernet device transmit queue index. + * @return + * - 0: Success + * - <0: Error code on failure + */ +__rte_experimental +int +rte_event_eth_tx_adapter_queue_stop(uint16_t eth_dev_id, uint16_t tx_queue_id); #ifdef __cplusplus } diff --git a/lib/eventdev/version.map b/lib/eventdev/version.map index 9a71cf3f8f..dd63ec6f68 100644 --- a/lib/eventdev/version.map +++ b/lib/eventdev/version.map @@ -116,6 +116,8 @@ EXPERIMENTAL { # added in 22.11 rte_event_eth_rx_adapter_instance_get; rte_event_eth_tx_adapter_instance_get; + rte_event_eth_tx_adapter_queue_start; + rte_event_eth_tx_adapter_queue_stop; }; INTERNAL {