1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2018 Intel Corporation
8 #include <rte_string_fns.h>
9 #include <rte_devargs.h>
11 #include "hotplug_mp.h"
12 #include "eal_private.h"
14 #define MP_TIMEOUT_S 5 /**< 5 seconds timeouts */
16 struct mp_reply_bundle
{
17 struct rte_mp_msg msg
;
21 static int cmp_dev_name(const struct rte_device
*dev
, const void *_name
)
23 const char *name
= _name
;
25 return strcmp(dev
->name
, name
);
29 * Secondary to primary request.
30 * start from function eal_dev_hotplug_request_to_primary.
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
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.
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.
60 send_response_to_secondary(const struct eal_dev_mp_req
*req
,
64 struct rte_mp_msg mp_resp
;
65 struct eal_dev_mp_req
*resp
=
66 (struct eal_dev_mp_req
*)mp_resp
.param
;
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
;
75 ret
= rte_mp_reply(&mp_resp
, peer
);
77 RTE_LOG(ERR
, EAL
, "failed to send response to secondary\n");
83 __handle_secondary_request(void *param
)
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
;
97 if (req
->t
== EAL_DEV_REQ_TYPE_ATTACH
) {
98 ret
= local_dev_probe(req
->devargs
, &dev
);
100 RTE_LOG(ERR
, EAL
, "Failed to hotplug add device on primary\n");
104 ret
= eal_dev_hotplug_request_to_secondary(&tmp_req
);
106 RTE_LOG(ERR
, EAL
, "Failed to send hotplug request to secondary\n");
110 if (tmp_req
.result
!= 0) {
111 ret
= tmp_req
.result
;
112 RTE_LOG(ERR
, EAL
, "Failed to hotplug add device on secondary\n");
116 } else if (req
->t
== EAL_DEV_REQ_TYPE_DETACH
) {
117 ret
= rte_devargs_parse(&da
, req
->devargs
);
120 free(da
.args
); /* we don't need those */
123 ret
= eal_dev_hotplug_request_to_secondary(&tmp_req
);
125 RTE_LOG(ERR
, EAL
, "Failed to send hotplug request to secondary\n");
130 bus
= rte_bus_find_by_name(da
.bus
->name
);
132 RTE_LOG(ERR
, EAL
, "Cannot find bus (%s)\n", da
.bus
->name
);
137 dev
= bus
->find_device(NULL
, cmp_dev_name
, da
.name
);
139 RTE_LOG(ERR
, EAL
, "Cannot find plugged device (%s)\n", da
.name
);
144 if (tmp_req
.result
!= 0) {
145 RTE_LOG(ERR
, EAL
, "Failed to hotplug remove device on secondary\n");
146 ret
= tmp_req
.result
;
151 ret
= local_dev_remove(dev
);
153 RTE_LOG(ERR
, EAL
, "Failed to hotplug remove device on primary\n");
158 RTE_LOG(ERR
, EAL
, "unsupported secondary to primary request\n");
164 if (req
->t
== EAL_DEV_REQ_TYPE_ATTACH
) {
165 tmp_req
.t
= EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK
;
166 eal_dev_hotplug_request_to_secondary(&tmp_req
);
167 local_dev_remove(dev
);
169 tmp_req
.t
= EAL_DEV_REQ_TYPE_DETACH_ROLLBACK
;
170 eal_dev_hotplug_request_to_secondary(&tmp_req
);
174 ret
= send_response_to_secondary(&tmp_req
, ret
, bundle
->peer
);
176 RTE_LOG(ERR
, EAL
, "failed to send response to secondary\n");
183 handle_secondary_request(const struct rte_mp_msg
*msg
, const void *peer
)
185 struct mp_reply_bundle
*bundle
;
186 const struct eal_dev_mp_req
*req
=
187 (const struct eal_dev_mp_req
*)msg
->param
;
190 bundle
= malloc(sizeof(*bundle
));
191 if (bundle
== NULL
) {
192 RTE_LOG(ERR
, EAL
, "not enough memory\n");
193 return send_response_to_secondary(req
, -ENOMEM
, peer
);
198 * We need to send reply on interrupt thread, but peer can't be
199 * parsed directly, so this is a temporal hack, need to be fixed
202 bundle
->peer
= strdup(peer
);
203 if (bundle
->peer
== NULL
) {
205 RTE_LOG(ERR
, EAL
, "not enough memory\n");
206 return send_response_to_secondary(req
, -ENOMEM
, peer
);
210 * We are at IPC callback thread, sync IPC is not allowed due to
211 * dead lock, so we delegate the task to interrupt thread.
213 ret
= rte_eal_alarm_set(1, __handle_secondary_request
, bundle
);
215 RTE_LOG(ERR
, EAL
, "failed to add mp task\n");
218 return send_response_to_secondary(req
, ret
, peer
);
223 static void __handle_primary_request(void *param
)
225 struct mp_reply_bundle
*bundle
= param
;
226 struct rte_mp_msg
*msg
= &bundle
->msg
;
227 const struct eal_dev_mp_req
*req
=
228 (const struct eal_dev_mp_req
*)msg
->param
;
229 struct rte_mp_msg mp_resp
;
230 struct eal_dev_mp_req
*resp
=
231 (struct eal_dev_mp_req
*)mp_resp
.param
;
232 struct rte_devargs
*da
;
233 struct rte_device
*dev
;
237 memset(&mp_resp
, 0, sizeof(mp_resp
));
240 case EAL_DEV_REQ_TYPE_ATTACH
:
241 case EAL_DEV_REQ_TYPE_DETACH_ROLLBACK
:
242 ret
= local_dev_probe(req
->devargs
, &dev
);
244 case EAL_DEV_REQ_TYPE_DETACH
:
245 case EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK
:
246 da
= calloc(1, sizeof(*da
));
252 ret
= rte_devargs_parse(da
, req
->devargs
);
256 bus
= rte_bus_find_by_name(da
->bus
->name
);
258 RTE_LOG(ERR
, EAL
, "Cannot find bus (%s)\n", da
->bus
->name
);
263 dev
= bus
->find_device(NULL
, cmp_dev_name
, da
->name
);
265 RTE_LOG(ERR
, EAL
, "Cannot find plugged device (%s)\n", da
->name
);
270 if (!rte_dev_is_probed(dev
)) {
271 if (req
->t
== EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK
) {
273 * Don't fail the rollback just because there's
283 ret
= local_dev_remove(dev
);
292 strlcpy(mp_resp
.name
, EAL_DEV_MP_ACTION_REQUEST
, sizeof(mp_resp
.name
));
293 mp_resp
.len_param
= sizeof(*req
);
294 memcpy(resp
, req
, sizeof(*resp
));
296 if (rte_mp_reply(&mp_resp
, bundle
->peer
) < 0)
297 RTE_LOG(ERR
, EAL
, "failed to send reply to primary request\n");
304 handle_primary_request(const struct rte_mp_msg
*msg
, const void *peer
)
306 struct rte_mp_msg mp_resp
;
307 const struct eal_dev_mp_req
*req
=
308 (const struct eal_dev_mp_req
*)msg
->param
;
309 struct eal_dev_mp_req
*resp
=
310 (struct eal_dev_mp_req
*)mp_resp
.param
;
311 struct mp_reply_bundle
*bundle
;
314 memset(&mp_resp
, 0, sizeof(mp_resp
));
315 strlcpy(mp_resp
.name
, EAL_DEV_MP_ACTION_REQUEST
, sizeof(mp_resp
.name
));
316 mp_resp
.len_param
= sizeof(*req
);
317 memcpy(resp
, req
, sizeof(*resp
));
319 bundle
= calloc(1, sizeof(*bundle
));
320 if (bundle
== NULL
) {
321 RTE_LOG(ERR
, EAL
, "not enough memory\n");
322 resp
->result
= -ENOMEM
;
323 ret
= rte_mp_reply(&mp_resp
, peer
);
325 RTE_LOG(ERR
, EAL
, "failed to send reply to primary request\n");
331 * We need to send reply on interrupt thread, but peer can't be
332 * parsed directly, so this is a temporal hack, need to be fixed
335 bundle
->peer
= (void *)strdup(peer
);
336 if (bundle
->peer
== NULL
) {
337 RTE_LOG(ERR
, EAL
, "not enough memory\n");
339 resp
->result
= -ENOMEM
;
340 ret
= rte_mp_reply(&mp_resp
, peer
);
342 RTE_LOG(ERR
, EAL
, "failed to send reply to primary request\n");
347 * We are at IPC callback thread, sync IPC is not allowed due to
348 * dead lock, so we delegate the task to interrupt thread.
350 ret
= rte_eal_alarm_set(1, __handle_primary_request
, bundle
);
355 ret
= rte_mp_reply(&mp_resp
, peer
);
357 RTE_LOG(ERR
, EAL
, "failed to send reply to primary request\n");
364 int eal_dev_hotplug_request_to_primary(struct eal_dev_mp_req
*req
)
366 struct rte_mp_msg mp_req
;
367 struct rte_mp_reply mp_reply
;
368 struct timespec ts
= {.tv_sec
= MP_TIMEOUT_S
, .tv_nsec
= 0};
369 struct eal_dev_mp_req
*resp
;
372 memset(&mp_req
, 0, sizeof(mp_req
));
373 memcpy(mp_req
.param
, req
, sizeof(*req
));
374 mp_req
.len_param
= sizeof(*req
);
375 strlcpy(mp_req
.name
, EAL_DEV_MP_ACTION_REQUEST
, sizeof(mp_req
.name
));
377 ret
= rte_mp_request_sync(&mp_req
, &mp_reply
, &ts
);
378 if (ret
|| mp_reply
.nb_received
!= 1) {
379 RTE_LOG(ERR
, EAL
, "Cannot send request to primary\n");
385 resp
= (struct eal_dev_mp_req
*)mp_reply
.msgs
[0].param
;
386 req
->result
= resp
->result
;
392 int eal_dev_hotplug_request_to_secondary(struct eal_dev_mp_req
*req
)
394 struct rte_mp_msg mp_req
;
395 struct rte_mp_reply mp_reply
;
396 struct timespec ts
= {.tv_sec
= MP_TIMEOUT_S
, .tv_nsec
= 0};
400 memset(&mp_req
, 0, sizeof(mp_req
));
401 memcpy(mp_req
.param
, req
, sizeof(*req
));
402 mp_req
.len_param
= sizeof(*req
);
403 strlcpy(mp_req
.name
, EAL_DEV_MP_ACTION_REQUEST
, sizeof(mp_req
.name
));
405 ret
= rte_mp_request_sync(&mp_req
, &mp_reply
, &ts
);
407 RTE_LOG(ERR
, EAL
, "rte_mp_request_sync failed\n");
411 if (mp_reply
.nb_sent
!= mp_reply
.nb_received
) {
412 RTE_LOG(ERR
, EAL
, "not all secondary reply\n");
418 for (i
= 0; i
< mp_reply
.nb_received
; i
++) {
419 struct eal_dev_mp_req
*resp
=
420 (struct eal_dev_mp_req
*)mp_reply
.msgs
[i
].param
;
421 if (resp
->result
!= 0) {
422 if (req
->t
== EAL_DEV_REQ_TYPE_ATTACH
&&
423 resp
->result
== -EEXIST
)
425 if (req
->t
== EAL_DEV_REQ_TYPE_DETACH
&&
426 resp
->result
== -ENOENT
)
428 req
->result
= resp
->result
;
436 int rte_mp_dev_hotplug_init(void)
440 if (rte_eal_process_type() == RTE_PROC_PRIMARY
) {
441 ret
= rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST
,
442 handle_secondary_request
);
444 RTE_LOG(ERR
, EAL
, "Couldn't register '%s' action\n",
445 EAL_DEV_MP_ACTION_REQUEST
);
449 ret
= rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST
,
450 handle_primary_request
);
452 RTE_LOG(ERR
, EAL
, "Couldn't register '%s' action\n",
453 EAL_DEV_MP_ACTION_REQUEST
);