get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 29333,
    "url": "https://patches.dpdk.org/api/patches/29333/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/1506606959-76230-8-git-send-email-jianfeng.tan@intel.com/",
    "project": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/projects/1/?format=api",
        "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",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<1506606959-76230-8-git-send-email-jianfeng.tan@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/1506606959-76230-8-git-send-email-jianfeng.tan@intel.com",
    "date": "2017-09-28T13:55:54",
    "name": "[dpdk-dev,v2,07/12] eal: add channel for primary/secondary communication",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "832dd59420b1e85046a125ebde4147be79d11621",
    "submitter": {
        "id": 313,
        "url": "https://patches.dpdk.org/api/people/313/?format=api",
        "name": "Jianfeng Tan",
        "email": "jianfeng.tan@intel.com"
    },
    "delegate": null,
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/1506606959-76230-8-git-send-email-jianfeng.tan@intel.com/mbox/",
    "series": [],
    "comments": "https://patches.dpdk.org/api/patches/29333/comments/",
    "check": "fail",
    "checks": "https://patches.dpdk.org/api/patches/29333/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@dpdk.org",
        "Delivered-To": "patchwork@dpdk.org",
        "Received": [
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 35AFC1B1FF;\n\tThu, 28 Sep 2017 15:55:02 +0200 (CEST)",
            "from mga03.intel.com (mga03.intel.com [134.134.136.65])\n\tby dpdk.org (Postfix) with ESMTP id EC78E1B1F6\n\tfor <dev@dpdk.org>; Thu, 28 Sep 2017 15:54:55 +0200 (CEST)",
            "from fmsmga006.fm.intel.com ([10.253.24.20])\n\tby orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t28 Sep 2017 06:54:55 -0700",
            "from dpdk06.sh.intel.com ([10.67.110.196])\n\tby fmsmga006.fm.intel.com with ESMTP; 28 Sep 2017 06:54:53 -0700"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.42,450,1500966000\"; d=\"scan'208\";a=\"157117715\"",
        "From": "Jianfeng Tan <jianfeng.tan@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "bruce.richardson@intel.com, konstantin.ananyev@intel.com,\n\tpablo.de.lara.guarch@intel.com, thomas@monjalon.net,\n\tyliu@fridaylinux.org, maxime.coquelin@redhat.com, mtetsuyah@gmail.com,\n\tferruh.yigit@intel.com, Jianfeng Tan <jianfeng.tan@intel.com>",
        "Date": "Thu, 28 Sep 2017 13:55:54 +0000",
        "Message-Id": "<1506606959-76230-8-git-send-email-jianfeng.tan@intel.com>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": "<1506606959-76230-1-git-send-email-jianfeng.tan@intel.com>",
        "References": "<1503654052-84730-1-git-send-email-jianfeng.tan@intel.com>\n\t<1506606959-76230-1-git-send-email-jianfeng.tan@intel.com>",
        "Subject": "[dpdk-dev] [PATCH v2 07/12] eal: add channel for primary/secondary\n\tcommunication",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<http://dpdk.org/ml/options/dev>,\n\t<mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://dpdk.org/ml/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<http://dpdk.org/ml/listinfo/dev>,\n\t<mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Previouly, there is only one way for primary/secondary to exchange\nmessages, that is, primary process writes info into some predefind\nfile, and secondary process reads info out. That cannot address\nthe requirements:\n  a. Secondary wants to send info to primary, for example, secondary\n     would like to send request (about some specific vdev to primary).\n  b. Sending info at any time, instead of just initialization time.\n  c. Share FDs with the other side, for vdev like vhost, related FDs\n     (memory region, kick) should be shared.\n\nThis patch proposes to create a communication channel, as an unix\nsocket connection, for above requirements. Primary will listen on\nthe unix socket; secondary will connect this socket to talk.\n\nThree new APIs are added:\n\n  1. rte_eal_mp_action_register is used to register an action,\n     indexed by a string; if the calling component wants to\n     response the messages from the corresponding component in\n     its primary process or secondary processes.\n  2. rte_eal_mp_action_unregister is used to unregister the action\n     if the calling component does not want to response the messages.\n  3. rte_eal_mp_sendmsg is used to send a message.\n\nSigned-off-by: Jianfeng Tan <jianfeng.tan@intel.com>\n---\n lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   8 +\n lib/librte_eal/common/eal_common_proc.c         | 498 ++++++++++++++++++++++++\n lib/librte_eal/common/eal_filesystem.h          |  18 +\n lib/librte_eal/common/eal_private.h             |  10 +\n lib/librte_eal/common/include/rte_eal.h         |  68 ++++\n lib/librte_eal/linuxapp/eal/eal.c               |   6 +\n lib/librte_eal/linuxapp/eal/rte_eal_version.map |   8 +\n 7 files changed, 616 insertions(+)",
    "diff": "diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map\nindex 47a09ea..f895916 100644\n--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map\n+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map\n@@ -238,3 +238,11 @@ EXPERIMENTAL {\n \trte_service_start_with_defaults;\n \n } DPDK_17.08;\n+\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\trte_eal_primary_secondary_add_action;\n+\trte_eal_primary_secondary_del_action;\n+\trte_eal_primary_secondary_sendmsg;\n+} DPDK_17.11;\ndiff --git a/lib/librte_eal/common/eal_common_proc.c b/lib/librte_eal/common/eal_common_proc.c\nindex 60526ca..eb5a502 100644\n--- a/lib/librte_eal/common/eal_common_proc.c\n+++ b/lib/librte_eal/common/eal_common_proc.c\n@@ -33,8 +33,21 @@\n #include <stdio.h>\n #include <fcntl.h>\n #include <stdlib.h>\n+#include <sys/types.h>\n+#include <sys/socket.h>\n+#include <sys/epoll.h>\n+#include <limits.h>\n+#include <unistd.h>\n+#include <sys/un.h>\n+#include <errno.h>\n+#include <pthread.h>\n+\n+#include <rte_log.h>\n #include <rte_eal.h>\n+#include <rte_lcore.h>\n+#include <rte_common.h>\n \n+#include \"eal_private.h\"\n #include \"eal_filesystem.h\"\n #include \"eal_internal_cfg.h\"\n \n@@ -59,3 +72,488 @@ rte_eal_primary_proc_alive(const char *config_file_path)\n \n \treturn !!ret;\n }\n+\n+struct action_entry {\n+\tTAILQ_ENTRY(action_entry) next;      /**< Next attached action entry */\n+\n+#define MAX_ACTION_NAME_LEN\t64\n+\tchar action_name[MAX_ACTION_NAME_LEN];\n+\trte_eal_mp_t action;\n+};\n+\n+/** Double linked list of actions. */\n+TAILQ_HEAD(action_entry_list, action_entry);\n+\n+static struct action_entry_list action_entry_list =\n+\tTAILQ_HEAD_INITIALIZER(action_entry_list);\n+\n+static struct action_entry *\n+find_action_entry_by_name(const char *name)\n+{\n+\tint len = strlen(name);\n+\tstruct action_entry *entry;\n+\n+\tTAILQ_FOREACH(entry, &action_entry_list, next) {\n+\t\tif (strncmp(entry->action_name, name, len) == 0)\n+\t\t\tbreak;\n+\t}\n+\n+\treturn entry;\n+}\n+\n+int\n+rte_eal_mp_action_register(const char *action_name, rte_eal_mp_t action)\n+{\n+\tstruct action_entry *entry = malloc(sizeof(struct action_entry));\n+\n+\tif (entry == NULL)\n+\t\treturn -ENOMEM;\n+\n+\tif (find_action_entry_by_name(action_name) != NULL)\n+\t\treturn -EEXIST;\n+\n+\tstrncpy(entry->action_name, action_name, MAX_ACTION_NAME_LEN);\n+\tentry->action = action;\n+\tTAILQ_INSERT_TAIL(&action_entry_list, entry, next);\n+\treturn 0;\n+}\n+\n+void\n+rte_eal_mp_action_unregister(const char *name)\n+{\n+\tstruct action_entry *entry = find_action_entry_by_name(name);\n+\n+\tTAILQ_REMOVE(&action_entry_list, entry, next);\n+\tfree(entry);\n+}\n+\n+/* The maximum amount of fd for one recvmsg/sendmsg */\n+#define SCM_MAX_FD\t\t253\n+#define MAX_SECONDARY_PROCS\t8\n+#define MAX_MESSAGE_LENGTH\t1024\n+\n+struct mp_fds {\n+\tint efd;\n+\n+\tunion {\n+\t\t/* fds for primary process */\n+\t\tstruct {\n+\t\t\tint listen;\n+\t\t\t/* fds used to send msg to secondary process(es) */\n+\t\t\tint secondaries[MAX_SECONDARY_PROCS];\n+\t\t};\n+\n+\t\t/* fds for secondary process */\n+\t\tstruct {\n+\t\t\t/* fds used to send msg to the primary process */\n+\t\t\tint primary;\n+\t\t};\n+\t};\n+};\n+\n+static struct mp_fds mp_fds;\n+\n+struct msg_hdr {\n+\tchar action_name[MAX_ACTION_NAME_LEN];\n+\tint fds_num;\n+\tint len_params;\n+\tchar params[0];\n+} __rte_packed;\n+\n+static int\n+add_sec_proc(int fd)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_SECONDARY_PROCS; ++i)\n+\t\tif (mp_fds.secondaries[i] == -1)\n+\t\t\tbreak;\n+\n+\tif (i >= MAX_SECONDARY_PROCS)\n+\t\treturn -1;\n+\n+\tmp_fds.secondaries[i] = fd;\n+\n+\treturn i;\n+}\n+\n+static void\n+del_sec_proc(int fd)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_SECONDARY_PROCS; ++i) {\n+\t\tif (mp_fds.secondaries[i] == fd) {\n+\t\t\tmp_fds.secondaries[i] = -1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n+static int\n+read_msg(int sockfd, char *buf, int buflen, int *fds, int fds_num)\n+{\n+\tstruct iovec iov;\n+\tstruct msghdr msgh;\n+\tsize_t fdsize = fds_num * sizeof(int);\n+\tchar control[CMSG_SPACE(fdsize)];\n+\tstruct cmsghdr *cmsg;\n+\tstruct msg_hdr *hdr = (struct msg_hdr *)buf;\n+\tint ret, total;\n+\n+\t/* read msg_hdr */\n+\tmemset(&msgh, 0, sizeof(msgh));\n+\tiov.iov_base = hdr;\n+\tiov.iov_len  = sizeof(*hdr);\n+\n+\tmsgh.msg_iov = &iov;\n+\tmsgh.msg_iovlen = 1;\n+\tmsgh.msg_control = control;\n+\tmsgh.msg_controllen = sizeof(control);\n+\n+\tret = recvmsg(sockfd, &msgh, 0);\n+\tif (ret != sizeof(struct msg_hdr)) {\n+\t\tRTE_LOG(ERR, EAL, \"recvmsg failed\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tif (msgh.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {\n+\t\tRTE_LOG(ERR, EAL, \"truncted msg\\n\");\n+\t\treturn -1;\n+\t}\n+\ttotal = ret;\n+\n+\t/* read auxiliary FDs if any */\n+\tfor (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;\n+\t\tcmsg = CMSG_NXTHDR(&msgh, cmsg)) {\n+\t\tif ((cmsg->cmsg_level == SOL_SOCKET) &&\n+\t\t\t(cmsg->cmsg_type == SCM_RIGHTS)) {\n+\t\t\tmemcpy(fds, CMSG_DATA(cmsg), fdsize);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\t/* read params */\n+\tif (hdr->len_params) {\n+\t\tif (hdr->len_params > buflen - (int)sizeof(*hdr))\n+\t\t\trte_exit(EXIT_FAILURE, \"params too long\\n\");\n+\n+\t\tret = read(sockfd, &hdr->params, hdr->len_params);\n+\t\tif (ret != hdr->len_params)\n+\t\t\trte_exit(EXIT_FAILURE, \"failed to recv params\\n\");\n+\n+\t\ttotal += ret;\n+\t}\n+\n+\tRTE_LOG(INFO, EAL, \"read msg: %s, %d\\n\", hdr->action_name,\n+\t\t(int)sizeof(*hdr) + hdr->len_params);\n+\treturn total;\n+}\n+\n+static int\n+process_msg(int fd)\n+{\n+\tint len;\n+\tint params_len;\n+\tchar buf[MAX_MESSAGE_LENGTH];\n+\tint fds[SCM_MAX_FD];\n+\tstruct msg_hdr *hdr;\n+\tstruct action_entry *entry;\n+\n+\tlen = read_msg(fd, buf, MAX_MESSAGE_LENGTH, fds, SCM_MAX_FD);\n+\tif (len <= 0) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to read message: %s\\n\",\n+\t\t\tstrerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\thdr = (struct msg_hdr *) buf;\n+\n+\tentry = find_action_entry_by_name(hdr->action_name);\n+\tif (entry == NULL) {\n+\t\tRTE_LOG(ERR, EAL, \"cannot find action by: %s\\n\",\n+\t\t\thdr->action_name);\n+\t\treturn -1;\n+\t}\n+\n+\tparams_len = len - sizeof(struct msg_hdr);\n+\tentry->action(hdr->params, params_len, fds, hdr->fds_num);\n+\n+\treturn 0;\n+}\n+\n+static int\n+add_secondary(void)\n+{\n+\tint fd;\n+\tstruct epoll_event ev;\n+\n+\twhile (1) {\n+\t\tfd = accept(mp_fds.listen, NULL, NULL);\n+\t\tif (fd < 0 && errno == EAGAIN)\n+\t\t\tbreak;\n+\t\telse if (fd < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"primary failed to accept: %s\\n\",\n+\t\t\t\tstrerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tev.events = EPOLLIN | EPOLLRDHUP;\n+\t\tev.data.fd = fd;\n+\t\tif (epoll_ctl(mp_fds.efd, EPOLL_CTL_ADD, fd, &ev) < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"failed to add secondary: %s\\n\",\n+\t\t\t\tstrerror(errno));\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (add_sec_proc(fd) < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"too many secondary processes\\n\");\n+\t\t\tclose(fd);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void *\n+mp_handler(void *arg __rte_unused)\n+{\n+\tint fd;\n+\tint i, n;\n+\tstruct epoll_event ev;\n+\tstruct epoll_event *events;\n+\tint is_primary = rte_eal_process_type() == RTE_PROC_PRIMARY;\n+\n+\tev.events = EPOLLIN | EPOLLRDHUP;\n+\tev.data.fd = (is_primary) ? mp_fds.listen : mp_fds.primary;\n+\tif (epoll_ctl(mp_fds.efd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to epoll_ctl: %s\\n\",\n+\t\t\tstrerror(errno));\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tevents = calloc(20, sizeof ev);\n+\n+\twhile (1) {\n+\t\tn = epoll_wait(mp_fds.efd, events, 20, -1);\n+\t\tfor (i = 0; i < n; i++) {\n+\t\t\tif (is_primary && events[i].data.fd == mp_fds.listen) {\n+\t\t\t\tif (events[i].events != EPOLLIN) {\n+\t\t\t\t\tRTE_LOG(ERR, EAL, \"what happens?\\n\");\n+\t\t\t\t\texit(EXIT_FAILURE);\n+\t\t\t\t}\n+\n+\t\t\t\tif (add_secondary() < 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tfd = events[i].data.fd;\n+\n+\t\t\tif ((events[i].events & EPOLLIN)) {\n+\t\t\t\tif (process_msg(fd) < 0) {\n+\t\t\t\t\tRTE_LOG(ERR, EAL,\n+\t\t\t\t\t\t\"failed to process msg\\n\");\n+\t\t\t\t\tif (!is_primary)\n+\t\t\t\t\t\texit(EXIT_FAILURE);\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/* EPOLLERR, EPOLLHUP, etc */\n+\t\t\tif (is_primary) {\n+\t\t\t\tRTE_LOG(ERR, EAL, \"secondary exit: %d\\n\", fd);\n+\t\t\t\tepoll_ctl(mp_fds.efd, EPOLL_CTL_DEL, fd, NULL);\n+\t\t\t\tdel_sec_proc(fd);\n+\t\t\t\tclose(fd);\n+\t\t\t} else {\n+\t\t\t\tRTE_LOG(ERR, EAL, \"primary exits, so do I\\n\");\n+\t\t\t\t/* Exit secondary when primary exits? */\n+\t\t\t\texit(EXIT_FAILURE);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+int\n+rte_eal_mp_channel_init(void)\n+{\n+\tint i, fd, ret;\n+\tconst char *path;\n+\tstruct sockaddr_un un;\n+\tpthread_t tid;\n+\tchar thread_name[RTE_MAX_THREAD_NAME_LEN];\n+\n+\tmp_fds.efd = epoll_create1(0);\n+\tif (mp_fds.efd < 0) {\n+\t\tRTE_LOG(ERR, EAL, \"epoll_create1 failed\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tfd = socket(AF_UNIX, SOCK_STREAM, 0);\n+\tif (fd < 0) {\n+\t\tRTE_LOG(ERR, EAL, \"Failed to create unix socket\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tmemset(&un, 0, sizeof(un));\n+\tun.sun_family = AF_UNIX;\n+\tpath = eal_mp_unix_path();\n+\tstrncpy(un.sun_path, path, sizeof(un.sun_path));\n+\tun.sun_path[sizeof(un.sun_path) - 1] = '\\0';\n+\n+\tif (rte_eal_process_type() == RTE_PROC_PRIMARY) {\n+\t\tfor (i = 0; i < MAX_SECONDARY_PROCS; ++i)\n+\t\t\tmp_fds.secondaries[i] = -1;\n+\n+\t\tif (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"cannot set nonblocking mode\\n\");\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/* The file still exists since last run */\n+\t\tunlink(path);\n+\n+\t\tret = bind(fd, (struct sockaddr *)&un, sizeof(un));\n+\t\tif (ret < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"failed to bind to %s: %s\\n\",\n+\t\t\t\tpath, strerror(errno));\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tRTE_LOG(INFO, EAL, \"primary bind to %s\\n\", path);\n+\n+\t\tret = listen(fd, 1024);\n+\t\tif (ret < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"failed to listen: %s\\n\",\n+\t\t\t\tstrerror(errno));\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tmp_fds.listen = fd;\n+\t} else {\n+\t\tret = connect(fd, (struct sockaddr *)&un, sizeof(un));\n+\t\tif (ret < 0) {\n+\t\t\tRTE_LOG(ERR, EAL, \"failed to connect primary\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\t\tmp_fds.primary = fd;\n+\t}\n+\n+\tret = pthread_create(&tid, NULL, mp_handler, NULL);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to create thead: %s\\n\",\n+\t\t\tstrerror(errno));\n+\t\tclose(fd);\n+\t\tclose(mp_fds.efd);\n+\t\treturn -1;\n+\t}\n+\n+\tsnprintf(thread_name, RTE_MAX_THREAD_NAME_LEN,\n+\t\t \"rte_mp_handle\");\n+\tret = rte_thread_setname(tid, thread_name);\n+\tif (ret < 0) {\n+\t\tRTE_LOG(ERR, EAL, \"failed to set thead name\\n\");\n+\t\tclose(fd);\n+\t\tclose(mp_fds.efd);\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+send_msg(int fd, struct msghdr *p_msgh)\n+{\n+\tint ret;\n+\n+\tdo {\n+\t\tret = sendmsg(fd, p_msgh, 0);\n+\t} while (ret < 0 && errno == EINTR);\n+\n+\tif (ret < 0)\n+\t\tRTE_LOG(ERR, EAL, \"failed to send msg: %s\\n\", strerror(errno));\n+\n+\treturn ret;\n+}\n+\n+int\n+rte_eal_mp_sendmsg(const char *action_name,\n+\t\t\t\t  const void *params,\n+\t\t\t\t  int len_params,\n+\t\t\t\t  int fds[],\n+\t\t\t\t  int fds_num)\n+{\n+\tint i;\n+\tint ret = 0;\n+\tstruct msghdr msgh;\n+\tstruct iovec iov;\n+\tsize_t fd_size = fds_num * sizeof(int);\n+\tchar control[CMSG_SPACE(fd_size)];\n+\tstruct cmsghdr *cmsg;\n+\tstruct msg_hdr *msg;\n+\tint len_msg;\n+\n+\tif (fds_num > SCM_MAX_FD) {\n+\t\tRTE_LOG(ERR, EAL,\n+\t\t\t\"Cannot send more than %d FDs\\n\", SCM_MAX_FD);\n+\t\treturn -E2BIG;\n+\t}\n+\n+\tlen_msg = sizeof(struct msg_hdr) + len_params;\n+\tif (len_msg > MAX_MESSAGE_LENGTH) {\n+\t\tRTE_LOG(ERR, EAL, \"Message is too long\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tRTE_LOG(INFO, EAL, \"send msg: %s, %d\\n\", action_name, len_msg);\n+\n+\tmsg = malloc(len_msg);\n+\tif (!msg) {\n+\t\tRTE_LOG(ERR, EAL, \"Cannot alloc memory for msg\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\tmemset(msg, 0, len_msg);\n+\tstrcpy(msg->action_name, action_name);\n+\tmsg->fds_num = fds_num;\n+\tmsg->len_params = len_params;\n+\tmemcpy(msg->params, params, len_params);\n+\n+\tmemset(&msgh, 0, sizeof(msgh));\n+\tmemset(control, 0, sizeof(control));\n+\n+\tiov.iov_base = (uint8_t *)msg;\n+\tiov.iov_len = len_msg;\n+\n+\tmsgh.msg_iov = &iov;\n+\tmsgh.msg_iovlen = 1;\n+\tmsgh.msg_control = control;\n+\tmsgh.msg_controllen = sizeof(control);\n+\n+\tcmsg = CMSG_FIRSTHDR(&msgh);\n+\tcmsg->cmsg_len = CMSG_LEN(fd_size);\n+\tcmsg->cmsg_level = SOL_SOCKET;\n+\tcmsg->cmsg_type = SCM_RIGHTS;\n+\tmemcpy(CMSG_DATA(cmsg), fds, fd_size);\n+\n+\tif (rte_eal_process_type() == RTE_PROC_PRIMARY) {\n+\t\tfor (i = 0; i < MAX_SECONDARY_PROCS; ++i) {\n+\t\t\tif (mp_fds.secondaries[i] == -1)\n+\t\t\t\tcontinue;\n+\n+\t\t\tret = send_msg(mp_fds.secondaries[i], &msgh);\n+\t\t\tif (ret < 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t} else {\n+\t\tret = send_msg(mp_fds.primary, &msgh);\n+\t}\n+\n+\tfree(msg);\n+\n+\treturn ret;\n+}\ndiff --git a/lib/librte_eal/common/eal_filesystem.h b/lib/librte_eal/common/eal_filesystem.h\nindex 8acbd99..3d9514f 100644\n--- a/lib/librte_eal/common/eal_filesystem.h\n+++ b/lib/librte_eal/common/eal_filesystem.h\n@@ -67,6 +67,24 @@ eal_runtime_config_path(void)\n \treturn buffer;\n }\n \n+/** Path of primary/secondary communication unix socket file. */\n+#define MP_UNIX_PATH_FMT \"%s/.%s_unix\"\n+static inline const char *\n+eal_mp_unix_path(void)\n+{\n+\tstatic char buffer[PATH_MAX]; /* static so auto-zeroed */\n+\tconst char *directory = default_config_dir;\n+\tconst char *home_dir = getenv(\"HOME\");\n+\n+\tif (getuid() != 0 && home_dir != NULL)\n+\t\tdirectory = home_dir;\n+\tsnprintf(buffer, sizeof(buffer) - 1, MP_UNIX_PATH_FMT,\n+\t\t directory, internal_config.hugefile_prefix);\n+\n+\treturn buffer;\n+\n+}\n+\n /** Path of hugepage info file. */\n #define HUGEPAGE_INFO_FMT \"%s/.%s_hugepage_info\"\n \ndiff --git a/lib/librte_eal/common/eal_private.h b/lib/librte_eal/common/eal_private.h\nindex 597d82e..7fbfbdf 100644\n--- a/lib/librte_eal/common/eal_private.h\n+++ b/lib/librte_eal/common/eal_private.h\n@@ -355,4 +355,14 @@ bool rte_eal_using_phys_addrs(void);\n  */\n struct rte_bus *rte_bus_find_by_device_name(const char *str);\n \n+/**\n+ * Create the unix channel for primary/secondary communication.\n+ *\n+ * @return\n+ *   0 on success;\n+ *   (<0) on failure.\n+ */\n+\n+int rte_eal_mp_channel_init(void);\n+\n #endif /* _EAL_PRIVATE_H_ */\ndiff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h\nindex 0e7363d..4e3d4d2 100644\n--- a/lib/librte_eal/common/include/rte_eal.h\n+++ b/lib/librte_eal/common/include/rte_eal.h\n@@ -210,6 +210,74 @@ int rte_eal_init(int argc, char **argv);\n int rte_eal_primary_proc_alive(const char *config_file_path);\n \n /**\n+ * Action function typedef used by other components.\n+ *\n+ * As we create unix socket channel for primary/secondary communication, use\n+ * this function typedef to register action for coming messages.\n+ */\n+typedef int (*rte_eal_mp_t)(const void *params, int len,\n+\t\t\t    int fds[], int fds_num);\n+/**\n+ * Register an action function for primary/secondary communication.\n+ *\n+ * Call this function to register an action, if the calling component wants\n+ * to response the messages from the corresponding component in its primary\n+ * process or secondary processes.\n+ *\n+ * @param action_name\n+ *   The action_name argument plays as the nonredundant key to find the action.\n+ *\n+ * @param action\n+ *   The action argument is the function pointer to the action function.\n+ *\n+ * @return\n+ *  - 0 on success.\n+ *  - (<0) on failure.\n+ */\n+int rte_eal_mp_action_register(const char *action_name, rte_eal_mp_t action);\n+/**\n+ * Unregister an action function for primary/secondary communication.\n+ *\n+ * Call this function to unregister an action  if the calling component does\n+ * not want to response the messages from the corresponding component in its\n+ * primary process or secondary processes.\n+ *\n+ * @param action_name\n+ *   The action_name argument plays as the nonredundant key to find the action.\n+ *\n+ */\n+void rte_eal_mp_action_unregister(const char *name);\n+\n+/**\n+ * Send a message to the primary process or the secondary processes.\n+ *\n+ * This function will send a message which will be responsed by the action\n+ * identified by action_name of the process on the other side.\n+ *\n+ * @param action_name\n+ *   The action_name argument is used to identify which action will be used.\n+ *\n+ * @param params\n+ *   The params argument contains the customized message.\n+ *\n+ * @param len_params\n+ *   The len_params argument is the length of the customized message.\n+ *\n+ * @param fds\n+ *   The fds argument is an array of fds sent with sendmsg.\n+ *\n+ * @param fds_num\n+ *   The fds_num argument is number of fds to be sent with sendmsg.\n+ *\n+ * @return\n+ *  - (>=0) on success.\n+ *  - (<0) on failure.\n+ */\n+int\n+rte_eal_mp_sendmsg(const char *action_name, const void *params,\n+\t\t   int len_params, int fds[], int fds_num);\n+\n+/**\n  * Usage function typedef used by the application usage function.\n  *\n  * Use this function typedef to define and call rte_set_applcation_usage_hook()\ndiff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c\nindex 48f12f4..4b491b9 100644\n--- a/lib/librte_eal/linuxapp/eal/eal.c\n+++ b/lib/librte_eal/linuxapp/eal/eal.c\n@@ -873,6 +873,12 @@ rte_eal_init(int argc, char **argv)\n \n \teal_check_mem_on_local_socket();\n \n+\tif (rte_eal_mp_channel_init() < 0) {\n+\t\trte_eal_init_alert(\"failed to init mp channel\\n\");\n+\t\trte_errno = EFAULT;\n+\t\treturn -1;\n+\t}\n+\n \tif (eal_plugins_init() < 0)\n \t\trte_eal_init_alert(\"Cannot init plugins\\n\");\n \ndiff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map\nindex 8c08b8d..2e1d0e5 100644\n--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map\n+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map\n@@ -243,3 +243,11 @@ EXPERIMENTAL {\n \trte_service_start_with_defaults;\n \n } DPDK_17.08;\n+\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\trte_eal_primary_secondary_add_action;\n+\trte_eal_primary_secondary_del_action;\n+\trte_eal_primary_secondary_sendmsg;\n+} DPDK_17.11;\n",
    "prefixes": [
        "dpdk-dev",
        "v2",
        "07/12"
    ]
}