92d8f50d39e9653dd1030132504f16f61b7eb9fe
[dpdk.git] / lib / librte_eal / common / hotplug_mp.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4 #include <string.h>
5
6 #include <rte_eal.h>
7 #include <rte_alarm.h>
8 #include <rte_string_fns.h>
9 #include <rte_devargs.h>
10
11 #include "hotplug_mp.h"
12 #include "eal_private.h"
13
14 #define MP_TIMEOUT_S 5 /**< 5 seconds timeouts */
15
16 static int cmp_dev_name(const struct rte_device *dev, const void *_name)
17 {
18         const char *name = _name;
19
20         return strcmp(dev->name, name);
21 }
22
23 struct mp_reply_bundle {
24         struct rte_mp_msg msg;
25         void *peer;
26 };
27
28 static int
29 handle_secondary_request(const struct rte_mp_msg *msg, const void *peer)
30 {
31         RTE_SET_USED(msg);
32         RTE_SET_USED(peer);
33         return -ENOTSUP;
34 }
35
36 static void __handle_primary_request(void *param)
37 {
38         struct mp_reply_bundle *bundle = param;
39         struct rte_mp_msg *msg = &bundle->msg;
40         const struct eal_dev_mp_req *req =
41                 (const struct eal_dev_mp_req *)msg->param;
42         struct rte_mp_msg mp_resp;
43         struct eal_dev_mp_req *resp =
44                 (struct eal_dev_mp_req *)mp_resp.param;
45         struct rte_devargs *da;
46         struct rte_device *dev;
47         struct rte_bus *bus;
48         int ret = 0;
49
50         memset(&mp_resp, 0, sizeof(mp_resp));
51
52         switch (req->t) {
53         case EAL_DEV_REQ_TYPE_ATTACH:
54         case EAL_DEV_REQ_TYPE_DETACH_ROLLBACK:
55                 ret = local_dev_probe(req->devargs, &dev);
56                 break;
57         case EAL_DEV_REQ_TYPE_DETACH:
58         case EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK:
59                 da = calloc(1, sizeof(*da));
60                 if (da == NULL) {
61                         ret = -ENOMEM;
62                         goto quit;
63                 }
64
65                 ret = rte_devargs_parse(da, req->devargs);
66                 if (ret != 0)
67                         goto quit;
68
69                 bus = rte_bus_find_by_name(da->bus->name);
70                 if (bus == NULL) {
71                         RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", da->bus->name);
72                         ret = -ENOENT;
73                         goto quit;
74                 }
75
76                 dev = bus->find_device(NULL, cmp_dev_name, da->name);
77                 if (dev == NULL) {
78                         RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", da->name);
79                         ret = -ENOENT;
80                         goto quit;
81                 }
82
83                 ret = local_dev_remove(dev);
84 quit:
85                 break;
86         default:
87                 ret = -EINVAL;
88         }
89
90         strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
91         mp_resp.len_param = sizeof(*req);
92         memcpy(resp, req, sizeof(*resp));
93         resp->result = ret;
94         if (rte_mp_reply(&mp_resp, bundle->peer) < 0)
95                 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
96
97         free(bundle->peer);
98         free(bundle);
99 }
100
101 static int
102 handle_primary_request(const struct rte_mp_msg *msg, const void *peer)
103 {
104         struct rte_mp_msg mp_resp;
105         const struct eal_dev_mp_req *req =
106                 (const struct eal_dev_mp_req *)msg->param;
107         struct eal_dev_mp_req *resp =
108                 (struct eal_dev_mp_req *)mp_resp.param;
109         struct mp_reply_bundle *bundle;
110         int ret = 0;
111
112         memset(&mp_resp, 0, sizeof(mp_resp));
113         strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
114         mp_resp.len_param = sizeof(*req);
115         memcpy(resp, req, sizeof(*resp));
116
117         bundle = calloc(1, sizeof(*bundle));
118         if (bundle == NULL) {
119                 resp->result = -ENOMEM;
120                 ret = rte_mp_reply(&mp_resp, peer);
121                 if (ret)
122                         RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
123                 return ret;
124         }
125
126         bundle->msg = *msg;
127         /**
128          * We need to send reply on interrupt thread, but peer can't be
129          * parsed directly, so this is a temporal hack, need to be fixed
130          * when it is ready.
131          */
132         bundle->peer = (void *)strdup(peer);
133
134         /**
135          * We are at IPC callback thread, sync IPC is not allowed due to
136          * dead lock, so we delegate the task to interrupt thread.
137          */
138         ret = rte_eal_alarm_set(1, __handle_primary_request, bundle);
139         if (ret != 0) {
140                 resp->result = ret;
141                 ret = rte_mp_reply(&mp_resp, peer);
142                 if  (ret != 0) {
143                         RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
144                         return ret;
145                 }
146         }
147         return 0;
148 }
149
150 int eal_dev_hotplug_request_to_primary(struct eal_dev_mp_req *req)
151 {
152         RTE_SET_USED(req);
153         return -ENOTSUP;
154 }
155
156 int eal_dev_hotplug_request_to_secondary(struct eal_dev_mp_req *req)
157 {
158         struct rte_mp_msg mp_req;
159         struct rte_mp_reply mp_reply;
160         struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
161         int ret;
162         int i;
163
164         memset(&mp_req, 0, sizeof(mp_req));
165         memcpy(mp_req.param, req, sizeof(*req));
166         mp_req.len_param = sizeof(*req);
167         strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name));
168
169         ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);
170         if (ret != 0) {
171                 RTE_LOG(ERR, EAL, "rte_mp_request_sync failed\n");
172                 return ret;
173         }
174
175         if (mp_reply.nb_sent != mp_reply.nb_received) {
176                 RTE_LOG(ERR, EAL, "not all secondary reply\n");
177                 return -1;
178         }
179
180         req->result = 0;
181         for (i = 0; i < mp_reply.nb_received; i++) {
182                 struct eal_dev_mp_req *resp =
183                         (struct eal_dev_mp_req *)mp_reply.msgs[i].param;
184                 if (resp->result != 0) {
185                         req->result = resp->result;
186                         if (req->t == EAL_DEV_REQ_TYPE_ATTACH &&
187                                 req->result != -EEXIST)
188                                 break;
189                         if (req->t == EAL_DEV_REQ_TYPE_DETACH &&
190                                 req->result != -ENOENT)
191                                 break;
192                 }
193         }
194
195         return 0;
196 }
197
198 int rte_mp_dev_hotplug_init(void)
199 {
200         int ret;
201
202         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
203                 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST,
204                                         handle_secondary_request);
205                 if (ret != 0) {
206                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
207                                 EAL_DEV_MP_ACTION_REQUEST);
208                         return ret;
209                 }
210         } else {
211                 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST,
212                                         handle_primary_request);
213                 if (ret != 0) {
214                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
215                                 EAL_DEV_MP_ACTION_REQUEST);
216                         return ret;
217                 }
218         }
219
220         return 0;
221 }