<div dir="auto">Does that means that the RSS of the secondary application can be also applied to the primary application, or in case of multiple instances on the same interface ? <div dir="auto"><br></div><div dir="auto">Best, </div><div dir="auto">Khadem</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Wed, Jul 9, 2025, 22:36 Stephen Hemminger <<a href="mailto:stephen@networkplumber.org">stephen@networkplumber.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Before this patch if secondary process called start/stop<br>
it would only impact the secondary process, the ethdev on the<br>
primary process was not started.<br>
<br>
With this patch, when start/stop is called from secondary,<br>
it calls the primary and does the operation there. The design<br>
is generic, and we can later add queue and other operations<br>
as needed.<br>
<br>
Bugzilla ID: 73<br>
<br>
Signed-off-by: Stephen Hemminger <<a href="mailto:stephen@networkplumber.org" target="_blank" rel="noreferrer">stephen@networkplumber.org</a>><br>
---<br>
 lib/ethdev/ethdev_driver.c  | 11 ++++-<br>
 lib/ethdev/ethdev_private.c | 93 +++++++++++++++++++++++++++++++++++++<br>
 lib/ethdev/ethdev_private.h | 26 +++++++++++<br>
 lib/ethdev/rte_ethdev.c     | 84 +++++++++++++++++++--------------<br>
 4 files changed, 177 insertions(+), 37 deletions(-)<br>
<br>
diff --git a/lib/ethdev/ethdev_driver.c b/lib/ethdev/ethdev_driver.c<br>
index ec0c1e1176..66ba7dafc9 100644<br>
--- a/lib/ethdev/ethdev_driver.c<br>
+++ b/lib/ethdev/ethdev_driver.c<br>
@@ -114,6 +114,14 @@ rte_eth_dev_allocate(const char *name)<br>
                goto unlock;<br>
        }<br>
<br>
+       if (eth_dev_shared_data->allocated_ports == 0 &&<br>
+           rte_mp_action_register(ETHDEV_MP, ethdev_server) &&<br>
+           rte_errno != ENOTSUP) {<br>
+               RTE_ETHDEV_LOG_LINE(ERR,<br>
+                        "Could not start %s service", ETHDEV_MP);<br>
+               goto unlock;<br>
+       }<br>
+<br>
        eth_dev = eth_dev_get(port_id);<br>
        eth_dev->flow_fp_ops = &rte_flow_fp_default_ops;<br>
        strlcpy(eth_dev->data->name, name, sizeof(eth_dev->data->name));<br>
@@ -282,7 +290,8 @@ rte_eth_dev_release_port(struct rte_eth_dev *eth_dev)<br>
                memset(eth_dev->data, 0, sizeof(struct rte_eth_dev_data));<br>
                eth_dev->data = NULL;<br>
<br>
-               eth_dev_shared_data->allocated_ports--;<br>
+               if (--eth_dev_shared_data->allocated_ports == 0)<br>
+                       rte_mp_action_unregister(ETHDEV_MP);<br>
                eth_dev_shared_data_release();<br>
        }<br>
<br>
diff --git a/lib/ethdev/ethdev_private.c b/lib/ethdev/ethdev_private.c<br>
index 285d377d91..184cf33f99 100644<br>
--- a/lib/ethdev/ethdev_private.c<br>
+++ b/lib/ethdev/ethdev_private.c<br>
@@ -2,6 +2,8 @@<br>
  * Copyright(c) 2018 Gaëtan Rivet<br>
  */<br>
<br>
+#include <assert.h><br>
+<br>
 #include <eal_export.h><br>
 #include <rte_debug.h><br>
<br>
@@ -477,3 +479,94 @@ eth_dev_tx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues)<br>
        dev->data->nb_tx_queues = nb_queues;<br>
        return 0;<br>
 }<br>
+<br>
+static int<br>
+ethdev_handle_request(const struct ethdev_mp_request *req)<br>
+{<br>
+       switch (req->operation) {<br>
+       case ETH_REQ_START:<br>
+               return rte_eth_dev_start(req->port_id);<br>
+<br>
+       case ETH_REQ_STOP:<br>
+               return rte_eth_dev_stop(req->port_id);<br>
+<br>
+       default:<br>
+               return -EINVAL;<br>
+       }<br>
+}<br>
+<br>
+static_assert(sizeof(struct ethdev_mp_request) <= RTE_MP_MAX_PARAM_LEN,<br>
+       "ethdev MP request bigger than available param space");<br>
+<br>
+static_assert(sizeof(struct ethdev_mp_response) <= RTE_MP_MAX_PARAM_LEN,<br>
+       "ethdev MP response bigger than available param space");<br>
+<br>
+int<br>
+ethdev_server(const struct rte_mp_msg *mp_msg, const void *peer)<br>
+{<br>
+       const struct ethdev_mp_request *req<br>
+               = (const struct ethdev_mp_request *)mp_msg->param;<br>
+<br>
+       struct rte_mp_msg mp_resp = {<br>
+               .name = ETHDEV_MP,<br>
+       };<br>
+       struct ethdev_mp_response *resp;<br>
+<br>
+       resp = (struct ethdev_mp_response *)mp_resp.param;<br>
+       mp_resp.len_param = sizeof(*resp);<br>
+       resp->res_op = req->operation;<br>
+<br>
+       /* recv client requests */<br>
+       if (mp_msg->len_param != sizeof(*req))<br>
+               resp->err_value = -EINVAL;<br>
+       else<br>
+               resp->err_value = ethdev_handle_request(req);<br>
+<br>
+       return rte_mp_reply(&mp_resp, peer);<br>
+}<br>
+<br>
+int<br>
+ethdev_request(uint16_t port_id, enum ethdev_mp_operation operation,<br>
+              const void *buf, size_t buf_len)<br>
+{<br>
+       struct rte_mp_msg mp_req = { };<br>
+       struct rte_mp_reply mp_reply;<br>
+       struct ethdev_mp_request *req;<br>
+       struct timespec ts = {.tv_sec = 5, .tv_nsec = 0};<br>
+       int ret;<br>
+<br>
+       if (sizeof(*req) + buf_len > RTE_MP_MAX_PARAM_LEN) {<br>
+               RTE_ETHDEV_LOG_LINE(ERR,<br>
+                                   "request %u port %u invalid len %zu",<br>
+                                   operation, port_id, buf_len);<br>
+               return -EINVAL;<br>
+       }<br>
+<br>
+       strlcpy(<a href="http://mp_req.name" rel="noreferrer noreferrer" target="_blank">mp_req.name</a>, ETHDEV_MP, RTE_MP_MAX_NAME_LEN);<br>
+       mp_req.len_param = sizeof(*req) + buf_len;<br>
+<br>
+       req = (struct ethdev_mp_request *)mp_req.param;<br>
+       req->operation = operation;<br>
+       req->port_id = port_id;<br>
+<br>
+       if (buf_len > 0)<br>
+               memcpy(req->config, buf, buf_len);<br>
+<br>
+       ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);<br>
+       if (ret == 0) {<br>
+               const struct rte_mp_msg *mp_rep = &mp_reply.msgs[0];<br>
+               const struct ethdev_mp_response *resp<br>
+                       = (const struct ethdev_mp_response *)mp_rep->param;<br>
+<br>
+               if (resp->err_value == 0)<br>
+                       ret = 0;<br>
+               else<br>
+                       rte_errno = -resp->err_value;<br>
+               free(mp_reply.msgs);<br>
+       }<br>
+<br>
+       if (ret < 0)<br>
+               RTE_ETHDEV_LOG_LINE(ERR,<br>
+                      "port %up ethdev op %u failed", port_id, operation);<br>
+       return ret;<br>
+}<br>
diff --git a/lib/ethdev/ethdev_private.h b/lib/ethdev/ethdev_private.h<br>
index b07b1b4c42..f58f161871 100644<br>
--- a/lib/ethdev/ethdev_private.h<br>
+++ b/lib/ethdev/ethdev_private.h<br>
@@ -79,4 +79,30 @@ void eth_dev_txq_release(struct rte_eth_dev *dev, uint16_t qid);<br>
 int eth_dev_rx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues);<br>
 int eth_dev_tx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues);<br>
<br>
+/* Used to allow start/stop from secondary */<br>
+#define ETHDEV_MP      "mp_ethdev"<br>
+<br>
+enum ethdev_mp_operation {<br>
+       ETH_REQ_START,<br>
+       ETH_REQ_STOP,<br>
+};<br>
+<br>
+struct ethdev_mp_request {<br>
+       uint16_t operation;<br>
+       uint16_t port_id;<br>
+       uint32_t reserved;<br>
+<br>
+       uint8_t config[]; /* operation specific */<br>
+};<br>
+<br>
+struct ethdev_mp_response {<br>
+       uint16_t res_op;<br>
+       uint16_t port_id;<br>
+       int32_t err_value;<br>
+};<br>
+<br>
+int ethdev_server(const struct rte_mp_msg *mp_msg, const void *peer);<br>
+int ethdev_request(uint16_t port_id, enum ethdev_mp_operation op,<br>
+                  const void *buf, size_t buf_len);<br>
+<br>
 #endif /* _ETH_PRIVATE_H_ */<br>
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c<br>
index dd7c00bc94..41363af2c3 100644<br>
--- a/lib/ethdev/rte_ethdev.c<br>
+++ b/lib/ethdev/rte_ethdev.c<br>
@@ -1800,54 +1800,61 @@ rte_eth_dev_start(uint16_t port_id)<br>
<br>
        if (dev->data->dev_configured == 0) {<br>
                RTE_ETHDEV_LOG_LINE(INFO,<br>
-                       "Device with port_id=%"PRIu16" is not configured.",<br>
-                       port_id);<br>
+                                   "Device with port_id=%"PRIu16" is not configured.",<br>
+                                   port_id);<br>
                return -EINVAL;<br>
        }<br>
<br>
        if (dev->data->dev_started != 0) {<br>
                RTE_ETHDEV_LOG_LINE(INFO,<br>
-                       "Device with port_id=%"PRIu16" already started",<br>
-                       port_id);<br>
+                                   "Device with port_id=%"PRIu16" already started",<br>
+                                   port_id);<br>
                return 0;<br>
        }<br>
<br>
-       ret = rte_eth_dev_info_get(port_id, &dev_info);<br>
-       if (ret != 0)<br>
-               return ret;<br>
+       if (rte_eal_process_type() == RTE_PROC_PRIMARY) {<br>
+               ret = rte_eth_dev_info_get(port_id, &dev_info);<br>
+               if (ret != 0)<br>
+                       return ret;<br>
<br>
-       restore_flags = rte_eth_get_restore_flags(dev, RTE_ETH_START);<br>
+               restore_flags = rte_eth_get_restore_flags(dev, RTE_ETH_START);<br>
<br>
-       /* Lets restore MAC now if device does not support live change */<br>
-       if ((*dev_info.dev_flags & RTE_ETH_DEV_NOLIVE_MAC_ADDR) &&<br>
-           (restore_flags & RTE_ETH_RESTORE_MAC_ADDR))<br>
-               eth_dev_mac_restore(dev, &dev_info);<br>
+               /* Restore MAC now if device does not support live change */<br>
+               if ((*dev_info.dev_flags & RTE_ETH_DEV_NOLIVE_MAC_ADDR) &&<br>
+                   (restore_flags & RTE_ETH_RESTORE_MAC_ADDR))<br>
+                       eth_dev_mac_restore(dev, &dev_info);<br>
<br>
-       diag = dev->dev_ops->dev_start(dev);<br>
-       if (diag == 0)<br>
-               dev->data->dev_started = 1;<br>
-       else<br>
-               return eth_err(port_id, diag);<br>
+               diag = dev->dev_ops->dev_start(dev);<br>
+               if (diag == 0)<br>
+                       dev->data->dev_started = 1;<br>
+               else<br>
+                       return eth_err(port_id, diag);<br>
<br>
-       ret = eth_dev_config_restore(dev, &dev_info, restore_flags, port_id);<br>
-       if (ret != 0) {<br>
-               RTE_ETHDEV_LOG_LINE(ERR,<br>
-                       "Error during restoring configuration for device (port %u): %s",<br>
-                       port_id, rte_strerror(-ret));<br>
-               ret_stop = rte_eth_dev_stop(port_id);<br>
-               if (ret_stop != 0) {<br>
+               ret = eth_dev_config_restore(dev, &dev_info, restore_flags, port_id);<br>
+               if (ret != 0) {<br>
                        RTE_ETHDEV_LOG_LINE(ERR,<br>
-                               "Failed to stop device (port %u): %s",<br>
-                               port_id, rte_strerror(-ret_stop));<br>
-               }<br>
+                               "Error during restoring configuration for device (port %u): %s",<br>
+                                port_id, rte_strerror(-ret));<br>
+                       ret_stop = rte_eth_dev_stop(port_id);<br>
+                       if (ret_stop != 0) {<br>
+                               RTE_ETHDEV_LOG_LINE(ERR,<br>
+                                       "Failed to stop device (port %u): %s",<br>
+                                        port_id, rte_strerror(-ret_stop));<br>
+                       }<br>
<br>
-               return ret;<br>
-       }<br>
+                       return ret;<br>
+               }<br>
<br>
-       if (dev->data->dev_conf.intr_conf.lsc == 0) {<br>
-               if (dev->dev_ops->link_update == NULL)<br>
-                       return -ENOTSUP;<br>
-               dev->dev_ops->link_update(dev, 0);<br>
+               if (dev->data->dev_conf.intr_conf.lsc == 0) {<br>
+                       if (dev->dev_ops->link_update == NULL)<br>
+                               return -ENOTSUP;<br>
+                       dev->dev_ops->link_update(dev, 0);<br>
+               }<br>
+       } else {<br>
+               /* in secondary, proxy to primary */<br>
+               ret = ethdev_request(port_id, ETH_REQ_START, NULL, 0);<br>
+               if (ret != 0)<br>
+                       return ret;<br>
        }<br>
<br>
        /* expose selection of PMD fast-path functions */<br>
@@ -1880,9 +1887,14 @@ rte_eth_dev_stop(uint16_t port_id)<br>
        /* point fast-path functions to dummy ones */<br>
        eth_dev_fp_ops_reset(rte_eth_fp_ops + port_id);<br>
<br>
-       ret = dev->dev_ops->dev_stop(dev);<br>
-       if (ret == 0)<br>
-               dev->data->dev_started = 0;<br>
+       if (rte_eal_process_type() == RTE_PROC_PRIMARY) {<br>
+               ret = dev->dev_ops->dev_stop(dev);<br>
+               if (ret == 0)<br>
+                       dev->data->dev_started = 0;<br>
+       } else {<br>
+               ret = ethdev_request(port_id, ETH_REQ_STOP, NULL, 0);<br>
+       }<br>
+<br>
        rte_ethdev_trace_stop(port_id, ret);<br>
<br>
        return ret;<br>
-- <br>
2.47.2<br>
<br>
</blockquote></div>