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);
205 * We are at IPC callback thread, sync IPC is not allowed due to
206 * dead lock, so we delegate the task to interrupt thread.
208 ret = rte_eal_alarm_set(1, __handle_secondary_request, bundle);
210 RTE_LOG(ERR, EAL, "failed to add mp task\n");
211 return send_response_to_secondary(req, ret, peer);
216 static void __handle_primary_request(void *param)
218 struct mp_reply_bundle *bundle = param;
219 struct rte_mp_msg *msg = &bundle->msg;
220 const struct eal_dev_mp_req *req =
221 (const struct eal_dev_mp_req *)msg->param;
222 struct rte_mp_msg mp_resp;
223 struct eal_dev_mp_req *resp =
224 (struct eal_dev_mp_req *)mp_resp.param;
225 struct rte_devargs *da;
226 struct rte_device *dev;
230 memset(&mp_resp, 0, sizeof(mp_resp));
233 case EAL_DEV_REQ_TYPE_ATTACH:
234 case EAL_DEV_REQ_TYPE_DETACH_ROLLBACK:
235 ret = local_dev_probe(req->devargs, &dev);
237 case EAL_DEV_REQ_TYPE_DETACH:
238 case EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK:
239 da = calloc(1, sizeof(*da));
245 ret = rte_devargs_parse(da, req->devargs);
249 bus = rte_bus_find_by_name(da->bus->name);
251 RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", da->bus->name);
256 dev = bus->find_device(NULL, cmp_dev_name, da->name);
258 RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", da->name);
263 if (!rte_dev_is_probed(dev)) {
264 if (req->t == EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK) {
266 * Don't fail the rollback just because there's
276 ret = local_dev_remove(dev);
285 strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
286 mp_resp.len_param = sizeof(*req);
287 memcpy(resp, req, sizeof(*resp));
289 if (rte_mp_reply(&mp_resp, bundle->peer) < 0)
290 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
297 handle_primary_request(const struct rte_mp_msg *msg, const void *peer)
299 struct rte_mp_msg mp_resp;
300 const struct eal_dev_mp_req *req =
301 (const struct eal_dev_mp_req *)msg->param;
302 struct eal_dev_mp_req *resp =
303 (struct eal_dev_mp_req *)mp_resp.param;
304 struct mp_reply_bundle *bundle;
307 memset(&mp_resp, 0, sizeof(mp_resp));
308 strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name));
309 mp_resp.len_param = sizeof(*req);
310 memcpy(resp, req, sizeof(*resp));
312 bundle = calloc(1, sizeof(*bundle));
313 if (bundle == NULL) {
314 resp->result = -ENOMEM;
315 ret = rte_mp_reply(&mp_resp, peer);
317 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
323 * We need to send reply on interrupt thread, but peer can't be
324 * parsed directly, so this is a temporal hack, need to be fixed
327 bundle->peer = (void *)strdup(peer);
330 * We are at IPC callback thread, sync IPC is not allowed due to
331 * dead lock, so we delegate the task to interrupt thread.
333 ret = rte_eal_alarm_set(1, __handle_primary_request, bundle);
336 ret = rte_mp_reply(&mp_resp, peer);
338 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n");
345 int eal_dev_hotplug_request_to_primary(struct eal_dev_mp_req *req)
347 struct rte_mp_msg mp_req;
348 struct rte_mp_reply mp_reply;
349 struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
350 struct eal_dev_mp_req *resp;
353 memset(&mp_req, 0, sizeof(mp_req));
354 memcpy(mp_req.param, req, sizeof(*req));
355 mp_req.len_param = sizeof(*req);
356 strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name));
358 ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);
359 if (ret || mp_reply.nb_received != 1) {
360 RTE_LOG(ERR, EAL, "cannot send request to primary");
366 resp = (struct eal_dev_mp_req *)mp_reply.msgs[0].param;
367 req->result = resp->result;
373 int eal_dev_hotplug_request_to_secondary(struct eal_dev_mp_req *req)
375 struct rte_mp_msg mp_req;
376 struct rte_mp_reply mp_reply;
377 struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0};
381 memset(&mp_req, 0, sizeof(mp_req));
382 memcpy(mp_req.param, req, sizeof(*req));
383 mp_req.len_param = sizeof(*req);
384 strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name));
386 ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts);
388 RTE_LOG(ERR, EAL, "rte_mp_request_sync failed\n");
392 if (mp_reply.nb_sent != mp_reply.nb_received) {
393 RTE_LOG(ERR, EAL, "not all secondary reply\n");
399 for (i = 0; i < mp_reply.nb_received; i++) {
400 struct eal_dev_mp_req *resp =
401 (struct eal_dev_mp_req *)mp_reply.msgs[i].param;
402 if (resp->result != 0) {
403 if (req->t == EAL_DEV_REQ_TYPE_ATTACH &&
404 resp->result == -EEXIST)
406 if (req->t == EAL_DEV_REQ_TYPE_DETACH &&
407 resp->result == -ENOENT)
409 req->result = resp->result;
417 int rte_mp_dev_hotplug_init(void)
421 if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
422 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST,
423 handle_secondary_request);
425 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
426 EAL_DEV_MP_ACTION_REQUEST);
430 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST,
431 handle_primary_request);
433 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n",
434 EAL_DEV_MP_ACTION_REQUEST);