Show a patch.

GET /api/patches/41620/
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 41620,
    "url": "http://patches.dpdk.org/api/patches/41620/",
    "web_url": "http://patches.dpdk.org/patch/41620/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk"
    },
    "msgid": "<20180627071740.19870-6-qi.z.zhang@intel.com>",
    "date": "2018-06-27T07:17:21",
    "name": "[v5,05/24] ethdev: enable hotplug on multi-process",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "8d0fa6e02b7b709b96d6290bc89820c71e43c0cb",
    "submitter": {
        "id": 504,
        "url": "http://patches.dpdk.org/api/people/504/",
        "name": "Zhang, Qi Z",
        "email": "qi.z.zhang@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/patch/41620/mbox/",
    "series": [
        {
            "id": 252,
            "url": "http://patches.dpdk.org/api/series/252/",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=252",
            "date": "2018-06-27T07:17:16",
            "name": "enable hotplug on multi-process",
            "version": 5,
            "mbox": "http://patches.dpdk.org/series/252/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/41620/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/41620/checks/",
    "tags": {},
    "headers": {
        "X-Mailman-Version": "2.1.15",
        "X-ExtLoop1": "1",
        "Errors-To": "dev-bounces@dpdk.org",
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Mailer": "git-send-email 2.13.6",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id EADE01BDD6;\n\tWed, 27 Jun 2018 09:17:32 +0200 (CEST)",
            "from mga09.intel.com (mga09.intel.com [134.134.136.24])\n\tby dpdk.org (Postfix) with ESMTP id 8710E1B5B8\n\tfor <dev@dpdk.org>; Wed, 27 Jun 2018 09:17:18 +0200 (CEST)",
            "from orsmga007.jf.intel.com ([10.7.209.58])\n\tby orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t27 Jun 2018 00:17:18 -0700",
            "from dpdk51.sh.intel.com ([10.67.110.190])\n\tby orsmga007.jf.intel.com with ESMTP; 27 Jun 2018 00:17:16 -0700"
        ],
        "References": "<20180607123849.14439-1-qi.z.zhang@intel.com>\n\t<20180627071740.19870-1-qi.z.zhang@intel.com>",
        "X-Amp-File-Uploaded": "False",
        "X-BeenThere": "dev@dpdk.org",
        "Message-Id": "<20180627071740.19870-6-qi.z.zhang@intel.com>",
        "X-IronPort-AV": "E=Sophos;i=\"5.51,278,1526367600\"; d=\"scan'208\";a=\"52236120\"",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "Precedence": "list",
        "From": "Qi Zhang <qi.z.zhang@intel.com>",
        "X-Original-To": "patchwork@dpdk.org",
        "List-Post": "<mailto:dev@dpdk.org>",
        "Return-Path": "<dev-bounces@dpdk.org>",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "In-Reply-To": "<20180627071740.19870-1-qi.z.zhang@intel.com>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "To": "thomas@monjalon.net,\n\tanatoly.burakov@intel.com",
        "Delivered-To": "patchwork@dpdk.org",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "Date": "Wed, 27 Jun 2018 15:17:21 +0800",
        "Cc": "konstantin.ananyev@intel.com, dev@dpdk.org, bruce.richardson@intel.com, \n\tferruh.yigit@intel.com, benjamin.h.shelton@intel.com,\n\tnarender.vangati@intel.com, Qi Zhang <qi.z.zhang@intel.com>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "Subject": "[dpdk-dev] [PATCH v5 05/24] ethdev: enable hotplug on multi-process"
    },
    "content": "We are going to introduce the solution to handle different hotplug\ncases in multi-process situation, it include below scenario:\n\n1. Attach a share device from primary\n2. Detach a share device from primary\n3. Attach a share device from secondary\n4. Detach a share device from secondary\n5. Attach a private device from secondary\n6. Detach a private device from secondary\n7. Detach a share device from secondary privately\n8. Attach a share device from secondary privately\n\nIn primary-secondary process model, we assume device is shared by default.\nthat means attach or detach a device on any process will broadcast to\nall other processes through mp channel then device information will be\nsynchronized on all processes.\n\nAny failure during attaching process will cause inconsistent status\nbetween processes, so proper rollback action should be considered.\nAlso it is not safe to detach a share device when other process still use\nit, so a handshake mechanism is introduced.\n\nThis patch covers the implementation of case 1,2,5,6,7,8.\nCase 3,4 will be implemented on separate patch as well as handshake\nmechanism.\n\nScenario for Case 1, 2:\n\nattach device\na) primary attach the new device if failed goto h).\nb) primary send attach sync request to all secondary.\nc) secondary receive request and attach device and send reply.\nd) primary check the reply if all success go to i).\ne) primary send attach rollback sync request to all secondary.\nf) secondary receive the request and detach device and send reply.\ng) primary receive the reply and detach device as rollback action.\nh) attach fail\ni) attach success\n\ndetach device\na) primary perform pre-detach check, if device is locked, goto i).\nb) primary send pre-detach sync request to all secondary.\nc) secondary perform pre-detach check and send reply.\nd) primary check the reply if any fail goto i).\ne) primary send detach sync request to all secondary\nf) secondary detach the device and send reply (assume no fail)\ng) primary detach the device.\nh) detach success\ni) detach failed\n\nCase 5, 6:\nSecondary process can attach private device which only visible to itself,\nin this case no IPC is involved, primary process is not allowed to have\nprivate device so far.\n\nCase 7, 8:\nSecondary process can also temporally to detach a share device \"privately\"\nthen attach it back later, this action also not impact other processes.\n\nAPIs changes:\n\nrte_eth_dev_attach and rte_eth_dev_attach are extended to support\nshare device attach/detach in primary-secondary process model, it will\nbe called in case 1,2,3,4.\n\nNew API rte_eth_dev_attach_private and rte_eth_dev_detach_private are\nintroduced to cover case 5,6,7,8, this API can only be invoked in secondary\nprocess.\n\nSigned-off-by: Qi Zhang <qi.z.zhang@intel.com>\n---\n lib/librte_ethdev/Makefile          |   1 +\n lib/librte_ethdev/ethdev_mp.c       | 257 ++++++++++++++++++++++++++++++++++++\n lib/librte_ethdev/ethdev_mp.h       |  41 ++++++\n lib/librte_ethdev/ethdev_private.h  |  39 ++++++\n lib/librte_ethdev/meson.build       |   1 +\n lib/librte_ethdev/rte_ethdev.c      | 210 ++++++++++++++++++++++++++---\n lib/librte_ethdev/rte_ethdev.h      |  45 +++++++\n lib/librte_ethdev/rte_ethdev_core.h |   5 +\n 8 files changed, 582 insertions(+), 17 deletions(-)\n create mode 100644 lib/librte_ethdev/ethdev_mp.c\n create mode 100644 lib/librte_ethdev/ethdev_mp.h\n create mode 100644 lib/librte_ethdev/ethdev_private.h",
    "diff": "diff --git a/lib/librte_ethdev/Makefile b/lib/librte_ethdev/Makefile\nindex c2f2f7d82..d0a059b83 100644\n--- a/lib/librte_ethdev/Makefile\n+++ b/lib/librte_ethdev/Makefile\n@@ -19,6 +19,7 @@ EXPORT_MAP := rte_ethdev_version.map\n LIBABIVER := 9\n \n SRCS-y += rte_ethdev.c\n+SRCS-y += ethdev_mp.c\n SRCS-y += rte_flow.c\n SRCS-y += rte_tm.c\n SRCS-y += rte_mtr.c\ndiff --git a/lib/librte_ethdev/ethdev_mp.c b/lib/librte_ethdev/ethdev_mp.c\nnew file mode 100644\nindex 000000000..86daff934\n--- /dev/null\n+++ b/lib/librte_ethdev/ethdev_mp.c\n@@ -0,0 +1,257 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2018 Intel Corporation\n+ */\n+#include <rte_string_fns.h>\n+#include <rte_alarm.h>\n+\n+#include \"rte_ethdev_driver.h\"\n+#include \"ethdev_mp.h\"\n+\n+#define MP_TIMEOUT_S 5 /**< 5 seconds timeouts */\n+\n+struct mp_reply_bundle {\n+\tstruct rte_mp_msg msg;\n+\tvoid *peer;\n+};\n+\n+static int detach_on_secondary(uint16_t port_id)\n+{\n+\tstruct rte_device *dev;\n+\tstruct rte_bus *bus;\n+\tint ret = 0;\n+\n+\tif (rte_eth_devices[port_id].state == RTE_ETH_DEV_UNUSED) {\n+\t\tethdev_log(ERR, \"detach on secondary: invalid port %d\\n\",\n+\t\t\t   port_id);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tdev = rte_eth_devices[port_id].device;\n+\tif (dev == NULL)\n+\t\treturn -EINVAL;\n+\n+\tbus = rte_bus_find_by_device(dev);\n+\tif (bus == NULL)\n+\t\treturn -ENOENT;\n+\n+\tret = rte_eal_hotplug_remove(bus->name, dev->name);\n+\tif (ret) {\n+\t\tethdev_log(ERR, \"failed to hot unplug bus: %s, device:%s\\n\",\n+\t\t\t   bus->name, dev->name);\n+\t\treturn ret;\n+\t}\n+\n+\trte_eth_dev_release_port_private(&rte_eth_devices[port_id]);\n+\treturn ret;\n+}\n+\n+static int attach_on_secondary(const char *devargs, uint16_t port_id)\n+{\n+\tstruct rte_devargs da;\n+\tint ret;\n+\n+\tif (rte_eth_devices[port_id].state != RTE_ETH_DEV_UNUSED) {\n+\t\tethdev_log(ERR, \"port %d already in used, failed to attach\\n\",\n+\t\t\t   port_id);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tmemset(&da, 0, sizeof(da));\n+\n+\tif (rte_devargs_parse(&da, \"%s\", devargs)) {\n+\t\tethdev_log(ERR, \"failed to parse devargs %s\\n\", devargs);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tret = rte_eal_hotplug_add(da.bus->name, da.name, \"\");\n+\tif (ret) {\n+\t\tethdev_log(ERR, \"failed to hotplug bus:%s, device:%s\\n\",\n+\t\t\t   da.bus->name, da.name);\n+\t\tfree(da.args);\n+\t\treturn ret;\n+\t}\n+\n+\tif (rte_eth_devices[port_id].state == RTE_ETH_DEV_UNUSED) {\n+\t\tethdev_log(ERR,\n+\t\t\t\"failed to attach to port %d, this is a pmd issue\\n\",\n+\t\t\t   port_id);\n+\t\tfree(da.args);\n+\t\treturn -ENODEV;\n+\t}\n+\tfree(da.args);\n+\treturn 0;\n+}\n+\n+static int\n+handle_secondary_request(const struct rte_mp_msg *msg, const void *peer)\n+{\n+\tRTE_SET_USED(msg);\n+\tRTE_SET_USED(peer);\n+\treturn -ENOTSUP;\n+}\n+\n+static void __handle_primary_request(void *param)\n+{\n+\tstruct mp_reply_bundle *bundle = param;\n+\tstruct rte_mp_msg *msg = &bundle->msg;\n+\tconst struct eth_dev_mp_req *req =\n+\t\t(const struct eth_dev_mp_req *)msg->param;\n+\tstruct rte_mp_msg mp_resp;\n+\tstruct eth_dev_mp_req *resp =\n+\t\t(struct eth_dev_mp_req *)mp_resp.param;\n+\tint ret = 0;\n+\n+\tmemset(&mp_resp, 0, sizeof(mp_resp));\n+\n+\tswitch (req->t) {\n+\tcase REQ_TYPE_ATTACH:\n+\t\tret = attach_on_secondary(req->devargs, req->port_id);\n+\t\tbreak;\n+\tcase REQ_TYPE_PRE_DETACH:\n+\t\tret = 0;\n+\t\tbreak;\n+\tcase REQ_TYPE_DETACH:\n+\tcase REQ_TYPE_ATTACH_ROLLBACK:\n+\t\tret = detach_on_secondary(req->port_id);\n+\t\tbreak;\n+\tdefault:\n+\t\tret = -EINVAL;\n+\t}\n+\n+\tstrlcpy(mp_resp.name, ETH_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));\n+\tmp_resp.len_param = sizeof(*req);\n+\tmemcpy(resp, req, sizeof(*resp));\n+\tresp->result = ret;\n+\tif (rte_mp_reply(&mp_resp, bundle->peer) < 0)\n+\t\tethdev_log(ERR, \"failed to send reply to primary request\\n\");\n+\n+\tfree(bundle->peer);\n+\tfree(bundle);\n+}\n+\n+static int\n+handle_primary_request(const struct rte_mp_msg *msg, const void *peer)\n+{\n+\n+\tstruct rte_mp_msg mp_resp;\n+\tconst struct eth_dev_mp_req *req =\n+\t\t(const struct eth_dev_mp_req *)msg->param;\n+\tstruct eth_dev_mp_req *resp =\n+\t\t(struct eth_dev_mp_req *)mp_resp.param;\n+\tstruct mp_reply_bundle *bundle;\n+\tint ret = 0;\n+\n+\tmemset(&mp_resp, 0, sizeof(mp_resp));\n+\tstrlcpy(mp_resp.name, ETH_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));\n+\tmp_resp.len_param = sizeof(*req);\n+\tmemcpy(resp, req, sizeof(*resp));\n+\n+\tbundle = calloc(1, sizeof(*bundle));\n+\tif (bundle == NULL) {\n+\t\tresp->result = -ENOMEM;\n+\t\tret = rte_mp_reply(&mp_resp, peer);\n+\t\tif (ret) {\n+\t\t\tethdev_log(ERR, \"failed to send reply to primary request\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tbundle->msg = *msg;\n+\t/**\n+\t * We need to send reply on interrupt thread, but peer can't be\n+\t * parsed directly, so this is a temporal hack, need to be fixed\n+\t * when it is ready.\n+\t */\n+\tbundle->peer = (void *)strdup(peer);\n+\n+\tret = rte_eal_alarm_set(1, __handle_primary_request, bundle);\n+\tif (ret) {\n+\t\tresp->result = ret;\n+\t\tret = rte_mp_reply(&mp_resp, peer);\n+\t\tif (ret) {\n+\t\t\tethdev_log(ERR, \"failed to send reply to primary request\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+int eth_dev_request_to_primary(struct eth_dev_mp_req *req)\n+{\n+\tRTE_SET_USED(req);\n+\treturn -ENOTSUP;\n+}\n+\n+/**\n+ * Request from primary to secondary.\n+ *\n+ * Be invoked when try to attach or detach a share device\n+ * from primary process.\n+ */\n+int eth_dev_request_to_secondary(struct eth_dev_mp_req *req)\n+{\n+\tstruct rte_mp_msg mp_req;\n+\tstruct rte_mp_reply mp_reply;\n+\tstruct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};\n+\tint ret;\n+\tint i;\n+\n+\tmemset(&mp_req, 0, sizeof(mp_req));\n+\tmemcpy(mp_req.param, req, sizeof(*req));\n+\tmp_req.len_param = sizeof(*req);\n+\tstrlcpy(mp_req.name, ETH_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name));\n+\n+\tret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);\n+\tif (ret) {\n+\t\tethdev_log(ERR, \"rte_mp_request_sync failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tif (mp_reply.nb_sent != mp_reply.nb_received) {\n+\t\tethdev_log(ERR, \"not all secondary reply\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\treq->result = 0;\n+\tfor (i = 0; i < mp_reply.nb_received; i++) {\n+\t\tstruct eth_dev_mp_req *resp =\n+\t\t\t(struct eth_dev_mp_req *)mp_reply.msgs[i].param;\n+\t\tif (resp->result) {\n+\t\t\treq->result = resp->result;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int on_mp_init(void)\n+{\n+\tint ret;\n+\n+\tif (rte_eal_process_type() == RTE_PROC_PRIMARY) {\n+\t\tret = rte_mp_action_register(ETH_DEV_MP_ACTION_REQUEST,\n+\t\t\t\t\t   handle_secondary_request);\n+\t\tif (ret) {\n+\t\t\tRTE_LOG(ERR, EAL, \"Couldn't register '%s' action\\n\",\n+\t\t\t\tETH_DEV_MP_ACTION_REQUEST);\n+\t\t\treturn ret;\n+\t\t}\n+\t} else {\n+\t\tret = rte_mp_action_register(ETH_DEV_MP_ACTION_REQUEST,\n+\t\t\t\t\t   handle_primary_request);\n+\t\tif (ret) {\n+\t\t\tRTE_LOG(ERR, EAL, \"Couldn't register '%s' action\\n\",\n+\t\t\t\tETH_DEV_MP_ACTION_REQUEST);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+RTE_INIT(ethdev_mp_init)\n+{\n+\tif (rte_eal_register_mp_init(on_mp_init))\n+\t\tRTE_LOG(ERR, EAL, \"ethdev mp channel init failed\\n\");\n+}\ndiff --git a/lib/librte_ethdev/ethdev_mp.h b/lib/librte_ethdev/ethdev_mp.h\nnew file mode 100644\nindex 000000000..40be46c89\n--- /dev/null\n+++ b/lib/librte_ethdev/ethdev_mp.h\n@@ -0,0 +1,41 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2018 Intel Corporation\n+ */\n+\n+#ifndef _RTE_ETHDEV_MP_H_\n+#define _RTE_ETHDEV_MP_H_\n+\n+#define MAX_DEV_ARGS_LEN 0x80\n+\n+#define ETH_DEV_MP_ACTION_REQUEST\t\"eth_dev_mp_request\"\n+#define ETH_DEV_MP_ACTION_RESPONSE\t\"eth_dev_mp_response\"\n+\n+enum eth_dev_req_type {\n+\tREQ_TYPE_ATTACH,\n+\tREQ_TYPE_PRE_DETACH,\n+\tREQ_TYPE_DETACH,\n+\tREQ_TYPE_ATTACH_ROLLBACK,\n+};\n+\n+struct eth_dev_mp_req {\n+\tenum eth_dev_req_type t;\n+\tchar devargs[MAX_DEV_ARGS_LEN];\n+\tuint16_t port_id;\n+\tint result;\n+};\n+\n+/**\n+ * this is a synchronous wrapper for secondary process send\n+ * request to primary process, this is invoked when an attach\n+ * or detach request issued from primary.\n+ */\n+int eth_dev_request_to_primary(struct eth_dev_mp_req *req);\n+\n+/**\n+ * this is a synchronous wrapper for primary process send\n+ * request to secondary process, this is invoked when an attach\n+ * or detach request issued from secondary process.\n+ */\n+int eth_dev_request_to_secondary(struct eth_dev_mp_req *req);\n+\n+#endif\ndiff --git a/lib/librte_ethdev/ethdev_private.h b/lib/librte_ethdev/ethdev_private.h\nnew file mode 100644\nindex 000000000..981e7de8a\n--- /dev/null\n+++ b/lib/librte_ethdev/ethdev_private.h\n@@ -0,0 +1,39 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2010-2018 Intel Corporation\n+ */\n+\n+#ifndef _ETHDEV_PRIVATE_H_\n+#define _ETHDEV_PRIVATE_H_\n+\n+/**\n+ * Attach a new Ethernet device in current process.\n+ *\n+ * @param devargs\n+ *  A pointer to a strings array describing the new device\n+ *  to be attached. The strings should be a pci address like\n+ *  '0000:01:00.0' or virtual device name like 'net_pcap0'.\n+ *\n+ * @param port_id\n+ *  A pointer to a port identifier actually attached.\n+ *\n+ * @return\n+ *  0 on success and port_id is filled, negative on error\n+ */\n+int do_eth_dev_attach(const char *devargs, uint16_t *port_id);\n+\n+/**\n+ * Detach a Ethernet device in current process.\n+ *\n+ * @param port_id\n+ *   The port identifier of the device to detach.\n+ *\n+ * @param devname\n+ *   A pointer to a buffer that will be filled with the device name.\n+ *   This buffer must be at least RTE_DEV_NAME_MAX_LEN long.\n+ *\n+ * @return\n+ *  0 on success and devname is filled, negative on error\n+ */\n+int do_eth_dev_detach(uint16_t port_id);\n+\n+#endif\ndiff --git a/lib/librte_ethdev/meson.build b/lib/librte_ethdev/meson.build\nindex aed5d2265..b60256855 100644\n--- a/lib/librte_ethdev/meson.build\n+++ b/lib/librte_ethdev/meson.build\n@@ -5,6 +5,7 @@ name = 'ethdev'\n version = 9\n allow_experimental_apis = true\n sources = files('ethdev_profile.c',\n+\t'ethdev_mp.c'\n \t'rte_ethdev.c',\n \t'rte_flow.c',\n \t'rte_mtr.c',\ndiff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c\nindex 2353fc921..6c5f465a2 100644\n--- a/lib/librte_ethdev/rte_ethdev.c\n+++ b/lib/librte_ethdev/rte_ethdev.c\n@@ -41,11 +41,13 @@\n #include \"rte_ethdev.h\"\n #include \"rte_ethdev_driver.h\"\n #include \"ethdev_profile.h\"\n+#include \"ethdev_mp.h\"\n+#include \"ethdev_private.h\"\n \n-static int ethdev_logtype;\n+int ethdev_logtype;\n \n-#define ethdev_log(level, fmt, ...) \\\n-\trte_log(RTE_LOG_ ## level, ethdev_logtype, fmt \"\\n\", ## __VA_ARGS__)\n+#define RTE_ETH_MP_ACTION_REQUEST\t\"rte_eth_mp_request\"\n+#define RTE_ETH_MP_ACTION_RESPONSE\t\"rte_eth_mp_response\"\n \n static const char *MZ_RTE_ETH_DEV_DATA = \"rte_eth_dev_data\";\n struct rte_eth_dev rte_eth_devices[RTE_MAX_ETHPORTS];\n@@ -649,9 +651,8 @@ eth_err(uint16_t port_id, int ret)\n \treturn ret;\n }\n \n-/* attach the new device, then store port_id of the device */\n int\n-rte_eth_dev_attach(const char *devargs, uint16_t *port_id)\n+do_eth_dev_attach(const char *devargs, uint16_t *port_id)\n {\n \tint current = rte_eth_dev_count_total();\n \tstruct rte_devargs da;\n@@ -696,14 +697,125 @@ rte_eth_dev_attach(const char *devargs, uint16_t *port_id)\n \treturn ret;\n }\n \n-/* detach the device, then store the name of the device */\n int\n-rte_eth_dev_detach(uint16_t port_id, char *name __rte_unused)\n+do_eth_dev_detach(uint16_t port_id)\n {\n \tstruct rte_device *dev;\n \tstruct rte_bus *bus;\n+\tint solid_release;\n+\tint ret = 0;\n+\n+\tdev = rte_eth_devices[port_id].device;\n+\tif (dev == NULL)\n+\t\treturn -EINVAL;\n+\n+\tbus = rte_bus_find_by_device(dev);\n+\tif (bus == NULL)\n+\t\treturn -ENOENT;\n+\n+\tif (rte_eal_process_type() == RTE_PROC_SECONDARY) {\n+\t/**\n+\t * A private device on secondary need\n+\t * rte_eth_dev_release_port.\n+\t * 1) only vdev support private device.\n+\t * 2) private device has no empty devargs.\n+\t */\n+\t\tif (!strcmp(bus->name, \"vdev\") &&\n+\t\t\tdev->devargs != NULL &&\n+\t\t\tstrlen(dev->devargs->args) > 0)\n+\t\t\tsolid_release = 1;\n+\t\telse\n+\t\t\tsolid_release = 0;\n+\t} else {\n+\t\tsolid_release = 0;\n+\t}\n+\n+\tret = rte_eal_hotplug_remove(bus->name, dev->name);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (solid_release)\n+\t\treturn rte_eth_dev_release_port(&rte_eth_devices[port_id]);\n+\telse\n+\t\treturn rte_eth_dev_release_port_private(\n+\t\t\t&rte_eth_devices[port_id]);\n+}\n+\n+/* attach the new device, then store port_id of the device */\n+int\n+rte_eth_dev_attach(const char *devargs, uint16_t *port_id)\n+{\n+\tstruct eth_dev_mp_req req;\n+\tint ret;\n+\n+\tif (rte_eal_process_type() != RTE_PROC_PRIMARY) {\n+\n+\t\t/**\n+\t\t * If secondary process, we just send request to primary\n+\t\t * to start the process.\n+\t\t */\n+\t\treq.t = REQ_TYPE_ATTACH;\n+\t\tstrlcpy(req.devargs, devargs, MAX_DEV_ARGS_LEN);\n+\n+\t\tret = eth_dev_request_to_primary(&req);\n+\t\tif (ret) {\n+\t\t\tethdev_log(ERR,\n+\t\t\t\t\"Failed to send device attach request to primary\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\t*port_id = req.port_id;\n+\t\treturn req.result;\n+\t}\n+\n+\tret = do_eth_dev_attach(devargs, port_id);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\t/* send attach request to seoncary */\n+\treq.t = REQ_TYPE_ATTACH;\n+\tstrlcpy(req.devargs, devargs, MAX_DEV_ARGS_LEN);\n+\treq.port_id = *port_id;\n+\tret = eth_dev_request_to_secondary(&req);\n+\tif (ret) {\n+\t\tethdev_log(ERR, \"Failed to send device attach request to secondary\\n\");\n+\t\tgoto rollback;\n+\t}\n+\n+\tif (req.result)\n+\t\tgoto rollback;\n+\n+\treturn 0;\n+\n+rollback:\n+\t/* send rollback request to secondary since some one fail to attach */\n+\treq.t = REQ_TYPE_ATTACH_ROLLBACK;\n+\treq.port_id = *port_id;\n+\teth_dev_request_to_secondary(&req);\n+\n+\tdo_eth_dev_detach(*port_id);\n+\n+\treturn -ENODEV;\n+}\n+\n+/* attach the new device, then store port_id of the device */\n+int\n+rte_eth_dev_attach_private(const char *devargs, uint16_t *port_id)\n+{\n+\n+\tif (rte_eal_process_type() == RTE_PROC_PRIMARY)\n+\t\treturn -ENOTSUP;\n+\n+\treturn do_eth_dev_attach(devargs, port_id);\n+}\n+\n+/* detach the device, then store the name of the device */\n+int\n+rte_eth_dev_detach(uint16_t port_id, char *name __rte_unused)\n+{\n+\tstruct eth_dev_mp_req req = {0};\n+\tint ret;\n \tuint32_t dev_flags;\n-\tint ret = -1;\n \n \tRTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);\n \n@@ -714,22 +826,86 @@ rte_eth_dev_detach(uint16_t port_id, char *name __rte_unused)\n \t\treturn -ENOTSUP;\n \t}\n \n-\tdev = rte_eth_devices[port_id].device;\n-\tif (dev == NULL)\n-\t\treturn -EINVAL;\n+\tif (rte_eal_process_type() != RTE_PROC_PRIMARY) {\n+\t\treq.t = REQ_TYPE_DETACH;\n+\t\treq.port_id = port_id;\n \n-\tbus = rte_bus_find_by_device(dev);\n-\tif (bus == NULL)\n-\t\treturn -ENOENT;\n+\t\t/**\n+\t\t * If secondary process, we just send request to primary\n+\t\t * to start the process.\n+\t\t */\n+\t\tret = eth_dev_request_to_primary(&req);\n+\t\tif (ret) {\n+\t\t\tethdev_log(ERR,\n+\t\t\t\t\"Failed to send device detach request to primary\\n\");\n+\t\t\treturn ret;\n+\t\t}\n \n-\tret = rte_eal_hotplug_remove(bus->name, dev->name);\n-\tif (ret < 0)\n+\t\treturn req.result;\n+\t}\n+\n+\t/* check pre_detach */\n+\treq.t = REQ_TYPE_PRE_DETACH;\n+\treq.port_id = port_id;\n+\tret = eth_dev_request_to_secondary(&req);\n+\tif (ret) {\n+\t\tethdev_log(ERR,\n+\t\t\t\"Failed to send device pre-detach request to secondary\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tif (req.result) {\n+\t\tethdev_log(ERR,\n+\t\t\t\"Device is busy on secondary, can't be detached\\n\");\n+\t\treturn req.result;\n+\t}\n+\n+\t/* detach on seconary first */\n+\treq.t = REQ_TYPE_DETACH;\n+\tret = eth_dev_request_to_secondary(&req);\n+\tif (ret) {\n+\t\tethdev_log(ERR,\n+\t\t\t\"Failed to send device detach request to secondary\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tif (req.result)\n+\t\t/**\n+\t\t * this should rarely happen, something wrong in secondary\n+\t\t * process, will not block primary detach.\n+\t\t */\n+\t\tethdev_log(ERR,\n+\t\t\t\"Failed to detach device on secondary process\\n\");\n+\n+\t/* detach on primary */\n+\tret =  do_eth_dev_detach(port_id);\n+\tif (ret)\n \t\treturn ret;\n \n-\trte_eth_dev_release_port(&rte_eth_devices[port_id]);\n \treturn 0;\n }\n \n+/* detach the device, then store the name of the device */\n+int\n+rte_eth_dev_detach_private(uint16_t port_id, char *name __rte_unused)\n+{\n+\tuint32_t dev_flags;\n+\n+\tif (rte_eal_process_type() == RTE_PROC_PRIMARY)\n+\t\treturn -ENOTSUP;\n+\n+\tRTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);\n+\n+\tdev_flags = rte_eth_devices[port_id].data->dev_flags;\n+\tif (dev_flags & RTE_ETH_DEV_BONDED_SLAVE) {\n+\t\tethdev_log(ERR,\n+\t\t\t\"Port %\" PRIu16 \" is bonded, cannot detach\", port_id);\n+\t\treturn -ENOTSUP;\n+\t}\n+\n+\treturn do_eth_dev_detach(port_id);\n+}\n+\n static int\n rte_eth_dev_rx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues)\n {\ndiff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h\nindex 36e3984ea..813806e3c 100644\n--- a/lib/librte_ethdev/rte_ethdev.h\n+++ b/lib/librte_ethdev/rte_ethdev.h\n@@ -1462,6 +1462,9 @@ uint16_t __rte_experimental rte_eth_dev_count_total(void);\n \n /**\n  * Attach a new Ethernet device specified by arguments.\n+ * In multi-process mode, it will sync with other process\n+ * to make sure all processes attach the device, any\n+ * failure on other process will rollback the action.\n  *\n  * @param devargs\n  *  A pointer to a strings array describing the new device\n@@ -1475,9 +1478,31 @@ uint16_t __rte_experimental rte_eth_dev_count_total(void);\n int rte_eth_dev_attach(const char *devargs, uint16_t *port_id);\n \n /**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * Attach a private Ethernet device specified by arguments.\n+ * A private device is invisible to other process.\n+ * Can only be invoked in secondary process.\n+ *\n+ * @param devargs\n+ *  A pointer to a strings array describing the new device\n+ *  to be attached. The strings should be a pci address like\n+ *  '0000:01:00.0' or virtual device name like 'net_pcap0'.\n+ * @param port_id\n+ *  A pointer to a port identifier actually attached.\n+ * @return\n+ *  0 on success and port_id is filled, negative on error\n+ */\n+int __rte_experimental\n+rte_eth_dev_attach_private(const char *devargs, uint16_t *port_id);\n+\n+/**\n  * Detach a Ethernet device specified by port identifier.\n  * This function must be called when the device is in the\n  * closed state.\n+ * In multi-process mode, it will sync with other process\n+ * to detach the device.\n  *\n  * @param port_id\n  *   The port identifier of the device to detach.\n@@ -1490,6 +1515,26 @@ int rte_eth_dev_attach(const char *devargs, uint16_t *port_id);\n int rte_eth_dev_detach(uint16_t port_id, char *devname);\n \n /**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change without prior notice\n+ *\n+ * Detach a private Ethernet device specified by port identifier\n+ * This function must be called when the device is in the\n+ * closed state.\n+ * Can only be invoked in secondary process.\n+ *\n+ * @param port_id\n+ *   The port identifier of the device to detach.\n+ * @param devname\n+ *   A pointer to a buffer that will be filled with the device name.\n+ *   This buffer must be at least RTE_DEV_NAME_MAX_LEN long.\n+ * @return\n+ *  0 on success and devname is filled, negative on error\n+ */\n+int __rte_experimental\n+rte_eth_dev_detach_private(uint16_t port_id, char *devname);\n+\n+/**\n  * Convert a numerical speed in Mbps to a bitmap flag that can be used in\n  * the bitmap link_speeds of the struct rte_eth_conf\n  *\ndiff --git a/lib/librte_ethdev/rte_ethdev_core.h b/lib/librte_ethdev/rte_ethdev_core.h\nindex 33d12b3a2..2cb6de745 100644\n--- a/lib/librte_ethdev/rte_ethdev_core.h\n+++ b/lib/librte_ethdev/rte_ethdev_core.h\n@@ -622,4 +622,9 @@ struct rte_eth_dev_data {\n  */\n extern struct rte_eth_dev rte_eth_devices[];\n \n+extern int ethdev_logtype;\n+#define ethdev_log(level, fmt, ...) \\\n+\trte_log(RTE_LOG_ ## level, ethdev_logtype, fmt \"\\n\", ## __VA_ARGS__)\n+\n+\n #endif /* _RTE_ETHDEV_CORE_H_ */\n",
    "prefixes": [
        "v5",
        "05/24"
    ]
}