New upstream version 18.11-rc1
[deb_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 struct mp_reply_bundle {
17         struct rte_mp_msg msg;
18         void *peer;
19 };
20
21 static int cmp_dev_name(const struct rte_device *dev, const void *_name)
22 {
23         const char *name = _name;
24
25         return strcmp(dev->name, name);
26 }
27
28 /**
29  * Secondary to primary request.
30  * start from function eal_dev_hotplug_request_to_primary.
31  *
32  * device attach on secondary:
33  * a) secondary send sync request to the primary.
34  * b) primary receive the request and attach the new device if
35  *    failed goto i).
36  * c) primary forward attach sync request to all secondary.
37  * d) secondary receive the request and attach the device and send a reply.
38  * e) primary check the reply if all success goes to j).
39  * f) primary send attach rollback sync request to all secondary.
40  * g) secondary receive the request and detach the device and send a reply.
41  * h) primary receive the reply and detach device as rollback action.
42  * i) send attach fail to secondary as a reply of step a), goto k).
43  * j) send attach success to secondary as a reply of step a).
44  * k) secondary receive reply and return.
45  *
46  * device detach on secondary:
47  * a) secondary send sync request to the primary.
48  * b) primary send detach sync request to all secondary.
49  * c) secondary detach the device and send a reply.
50  * d) primary check the reply if all success goes to g).
51  * e) primary send detach rollback sync request to all secondary.
52  * f) secondary receive the request and attach back device. goto h).
53  * g) primary detach the device if success goto i), else goto e).
54  * h) primary send detach fail to secondary as a reply of step a), goto j).
55  * i) primary send detach success to secondary as a reply of step a).
56  * j) secondary receive reply and return.
57  */
58
59 static int
60 send_response_to_secondary(const struct eal_dev_mp_req *req,
61                         int result,
62                         const void *peer)
63 {
64         struct rte_mp_msg mp_resp;
65         struct eal_dev_mp_req *resp =
66                 (struct eal_dev_mp_req *)mp_resp.param;
67         int ret;
68
69         memset(&mp_resp, 0, sizeof(mp_resp));
70         mp_resp.len_param = sizeof(*resp);
71         strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
72         memcpy(resp, req, sizeof(*req));
73         resp->result = result;
74
75         ret = rte_mp_reply(&mp_resp, peer);
76         if (ret != 0)
77                 RTE_LOG(ERR, EAL, "failed to send response to secondary\n");
78
79         return ret;
80 }
81
82 static void
83 __handle_secondary_request(void *param)
84 {
85         struct mp_reply_bundle *bundle = param;
86                 const struct rte_mp_msg *msg = &bundle->msg;
87         const struct eal_dev_mp_req *req =
88                 (const struct eal_dev_mp_req *)msg->param;
89         struct eal_dev_mp_req tmp_req;
90         struct rte_devargs *da;
91         struct rte_device *dev;
92         struct rte_bus *bus;
93         int ret = 0;
94
95         tmp_req = *req;
96
97         if (req->t == EAL_DEV_REQ_TYPE_ATTACH) {
98                 ret = local_dev_probe(req->devargs, &dev);
99                 if (ret != 0) {
100                         RTE_LOG(ERR, EAL, "Failed to hotplug add device on primary\n");
101                         if (ret != -EEXIST)
102                                 goto finish;
103                 }
104                 ret = eal_dev_hotplug_request_to_secondary(&tmp_req);
105                 if (ret != 0) {
106                         RTE_LOG(ERR, EAL, "Failed to send hotplug request to secondary\n");
107                         ret = -ENOMSG;
108                         goto rollback;
109                 }
110                 if (tmp_req.result != 0) {
111                         ret = tmp_req.result;
112                         RTE_LOG(ERR, EAL, "Failed to hotplug add device on secondary\n");
113                         if (ret != -EEXIST)
114                                 goto rollback;
115                 }
116         } else if (req->t == EAL_DEV_REQ_TYPE_DETACH) {
117                 da = calloc(1, sizeof(*da));
118                 if (da == NULL) {
119                         ret = -ENOMEM;
120                         goto finish;
121                 }
122
123                 ret = rte_devargs_parse(da, req->devargs);
124                 if (ret != 0)
125                         goto finish;
126
127                 ret = eal_dev_hotplug_request_to_secondary(&tmp_req);
128                 if (ret != 0) {
129                         RTE_LOG(ERR, EAL, "Failed to send hotplug request to secondary\n");
130                         ret = -ENOMSG;
131                         goto rollback;
132                 }
133
134                 bus = rte_bus_find_by_name(da->bus->name);
135                 if (bus == NULL) {
136                         RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", da->bus->name);
137                         ret = -ENOENT;
138                         goto finish;
139                 }
140
141                 dev = bus->find_device(NULL, cmp_dev_name, da->name);
142                 if (dev == NULL) {
143                         RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", da->name);
144                         ret = -ENOENT;
145                         goto finish;
146                 }
147
148                 if (tmp_req.result != 0) {
149                         RTE_LOG(ERR, EAL, "Failed to hotplug remove device on secondary\n");
150                         ret = tmp_req.result;
151                         if (ret != -ENOENT)
152                                 goto rollback;
153                 }
154
155                 ret = local_dev_remove(dev);
156                 if (ret != 0) {
157                         RTE_LOG(ERR, EAL, "Failed to hotplug remove device on primary\n");
158                         if (ret != -ENOENT)
159                                 goto rollback;
160                 }
161         } else {
162                 RTE_LOG(ERR, EAL, "unsupported secondary to primary request\n");
163                 ret = -ENOTSUP;
164         }
165         goto finish;
166
167 rollback:
168         if (req->t == EAL_DEV_REQ_TYPE_ATTACH) {
169                 tmp_req.t = EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK;
170                 eal_dev_hotplug_request_to_secondary(&tmp_req);
171                 local_dev_remove(dev);
172         } else {
173                 tmp_req.t = EAL_DEV_REQ_TYPE_DETACH_ROLLBACK;
174                 eal_dev_hotplug_request_to_secondary(&tmp_req);
175         }
176
177 finish:
178         ret = send_response_to_secondary(&tmp_req, ret, bundle->peer);
179         if (ret)
180                 RTE_LOG(ERR, EAL, "failed to send response to secondary\n");
181
182         free(bundle->peer);
183         free(bundle);
184 }
185
186 static int
187 handle_secondary_request(const struct rte_mp_msg *msg, const void *peer)
188 {
189         struct mp_reply_bundle *bundle;
190         const struct eal_dev_mp_req *req =
191                 (const struct eal_dev_mp_req *)msg->param;
192         int ret = 0;
193
194         bundle = malloc(sizeof(*bundle));
195         if (bundle == NULL) {
196                 RTE_LOG(ERR, EAL, "not enough memory\n");
197                 return send_response_to_secondary(req, -ENOMEM, peer);
198         }
199
200         bundle->msg = *msg;
201         /**
202          * We need to send reply on interrupt thread, but peer can't be
203          * parsed directly, so this is a temporal hack, need to be fixed
204          * when it is ready.
205          */
206         bundle->peer = strdup(peer);
207
208         /**
209          * We are at IPC callback thread, sync IPC is not allowed due to
210          * dead lock, so we delegate the task to interrupt thread.
211          */
212         ret = rte_eal_alarm_set(1, __handle_secondary_request, bundle);
213         if (ret != 0) {
214                 RTE_LOG(ERR, EAL, "failed to add mp task\n");
215                 return send_response_to_secondary(req, ret, peer);
216         }
217         return 0;
218 }
219
220 static void __handle_primary_request(void *param)
221 {
222         struct mp_reply_bundle *bundle = param;
223         struct rte_mp_msg *msg = &bundle->msg;
224         const struct eal_dev_mp_req *req =
225                 (const struct eal_dev_mp_req *)msg->param;
226         struct rte_mp_msg mp_resp;
227         struct eal_dev_mp_req *resp =
228                 (struct eal_dev_mp_req *)mp_resp.param;
229         struct rte_devargs *da;
230         struct rte_device *dev;
231         struct rte_bus *bus;
232         int ret = 0;
233
234         memset(&mp_resp, 0, sizeof(mp_resp));
235
236         switch (req->t) {
237         case EAL_DEV_REQ_TYPE_ATTACH:
238         case EAL_DEV_REQ_TYPE_DETACH_ROLLBACK:
239                 ret = local_dev_probe(req->devargs, &dev);
240                 break;
241         case EAL_DEV_REQ_TYPE_DETACH:
242         case EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK:
243                 da = calloc(1, sizeof(*da));
244                 if (da == NULL) {
245                         ret = -ENOMEM;
246                         goto quit;
247                 }
248
249                 ret = rte_devargs_parse(da, req->devargs);
250                 if (ret != 0)
251                         goto quit;
252
253                 bus = rte_bus_find_by_name(da->bus->name);
254                 if (bus == NULL) {
255                         RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", da->bus->name);
256                         ret = -ENOENT;
257                         goto quit;
258                 }
259
260                 dev = bus->find_device(NULL, cmp_dev_name, da->name);
261                 if (dev == NULL) {
262                         RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", da->name);
263                         ret = -ENOENT;
264                         goto quit;
265                 }
266
267                 ret = local_dev_remove(dev);
268 quit:
269                 break;
270         default:
271                 ret = -EINVAL;
272         }
273
274         strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
275         mp_resp.len_param = sizeof(*req);
276         memcpy(resp, req, sizeof(*resp));
277         resp->result = ret;
278         if (rte_mp_reply(&mp_resp, bundle->peer) < 0)
279                 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
280
281         free(bundle->peer);
282         free(bundle);
283 }
284
285 static int
286 handle_primary_request(const struct rte_mp_msg *msg, const void *peer)
287 {
288         struct rte_mp_msg mp_resp;
289         const struct eal_dev_mp_req *req =
290                 (const struct eal_dev_mp_req *)msg->param;
291         struct eal_dev_mp_req *resp =
292                 (struct eal_dev_mp_req *)mp_resp.param;
293         struct mp_reply_bundle *bundle;
294         int ret = 0;
295
296         memset(&mp_resp, 0, sizeof(mp_resp));
297         strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
298         mp_resp.len_param = sizeof(*req);
299         memcpy(resp, req, sizeof(*resp));
300
301         bundle = calloc(1, sizeof(*bundle));
302         if (bundle == NULL) {
303                 resp->result = -ENOMEM;
304                 ret = rte_mp_reply(&mp_resp, peer);
305                 if (ret)
306                         RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
307                 return ret;
308         }
309
310         bundle->msg = *msg;
311         /**
312          * We need to send reply on interrupt thread, but peer can't be
313          * parsed directly, so this is a temporal hack, need to be fixed
314          * when it is ready.
315          */
316         bundle->peer = (void *)strdup(peer);
317
318         /**
319          * We are at IPC callback thread, sync IPC is not allowed due to
320          * dead lock, so we delegate the task to interrupt thread.
321          */
322         ret = rte_eal_alarm_set(1, __handle_primary_request, bundle);
323         if (ret != 0) {
324                 resp->result = ret;
325                 ret = rte_mp_reply(&mp_resp, peer);
326                 if  (ret != 0) {
327                         RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
328                         return ret;
329                 }
330         }
331         return 0;
332 }
333
334 int eal_dev_hotplug_request_to_primary(struct eal_dev_mp_req *req)
335 {
336         struct rte_mp_msg mp_req;
337         struct rte_mp_reply mp_reply;
338         struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
339         struct eal_dev_mp_req *resp;
340         int ret;
341
342         memset(&mp_req, 0, sizeof(mp_req));
343         memcpy(mp_req.param, req, sizeof(*req));
344         mp_req.len_param = sizeof(*req);
345         strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name));
346
347         ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);
348         if (ret || mp_reply.nb_received != 1) {
349                 RTE_LOG(ERR, EAL, "cannot send request to primary");
350                 if (!ret)
351                         return -1;
352                 return ret;
353         }
354
355         resp = (struct eal_dev_mp_req *)mp_reply.msgs[0].param;
356         req->result = resp->result;
357
358         return ret;
359 }
360
361 int eal_dev_hotplug_request_to_secondary(struct eal_dev_mp_req *req)
362 {
363         struct rte_mp_msg mp_req;
364         struct rte_mp_reply mp_reply;
365         struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
366         int ret;
367         int i;
368
369         memset(&mp_req, 0, sizeof(mp_req));
370         memcpy(mp_req.param, req, sizeof(*req));
371         mp_req.len_param = sizeof(*req);
372         strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name));
373
374         ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);
375         if (ret != 0) {
376                 RTE_LOG(ERR, EAL, "rte_mp_request_sync failed\n");
377                 return ret;
378         }
379
380         if (mp_reply.nb_sent != mp_reply.nb_received) {
381                 RTE_LOG(ERR, EAL, "not all secondary reply\n");
382                 return -1;
383         }
384
385         req->result = 0;
386         for (i = 0; i < mp_reply.nb_received; i++) {
387                 struct eal_dev_mp_req *resp =
388                         (struct eal_dev_mp_req *)mp_reply.msgs[i].param;
389                 if (resp->result != 0) {
390                         req->result = resp->result;
391                         if (req->t == EAL_DEV_REQ_TYPE_ATTACH &&
392                                 req->result != -EEXIST)
393                                 break;
394                         if (req->t == EAL_DEV_REQ_TYPE_DETACH &&
395                                 req->result != -ENOENT)
396                                 break;
397                 }
398         }
399
400         return 0;
401 }
402
403 int rte_mp_dev_hotplug_init(void)
404 {
405         int ret;
406
407         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
408                 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST,
409                                         handle_secondary_request);
410                 if (ret != 0) {
411                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
412                                 EAL_DEV_MP_ACTION_REQUEST);
413                         return ret;
414                 }
415         } else {
416                 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST,
417                                         handle_primary_request);
418                 if (ret != 0) {
419                         RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
420                                 EAL_DEV_MP_ACTION_REQUEST);
421                         return ret;
422                 }
423         }
424
425         return 0;
426 }