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 ret = rte_devargs_parse(&da, req->devargs); 118 if (ret != 0) 119 goto finish; 120 free(da.args); /* we don't need those */ 121 da.args = NULL; 122 123 ret = eal_dev_hotplug_request_to_secondary(&tmp_req); 124 if (ret != 0) { 125 RTE_LOG(ERR, EAL, "Failed to send hotplug request to secondary\n"); 126 ret = -ENOMSG; 127 goto rollback; 128 } 129 130 bus = rte_bus_find_by_name(da.bus->name); 131 if (bus == NULL) { 132 RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", da.bus->name); 133 ret = -ENOENT; 134 goto finish; 135 } 136 137 dev = bus->find_device(NULL, cmp_dev_name, da.name); 138 if (dev == NULL) { 139 RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", da.name); 140 ret = -ENOENT; 141 goto finish; 142 } 143 144 if (tmp_req.result != 0) { 145 RTE_LOG(ERR, EAL, "Failed to hotplug remove device on secondary\n"); 146 ret = tmp_req.result; 147 if (ret != -ENOENT) 148 goto rollback; 149 } 150 151 ret = local_dev_remove(dev); 152 if (ret != 0) { 153 RTE_LOG(ERR, EAL, "Failed to hotplug remove device on primary\n"); 154 if (ret != -ENOENT) 155 goto rollback; 156 } 157 } else { 158 RTE_LOG(ERR, EAL, "unsupported secondary to primary request\n"); 159 ret = -ENOTSUP; 160 } 161 goto finish; 162 163 rollback: 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); 168 } else { 169 tmp_req.t = EAL_DEV_REQ_TYPE_DETACH_ROLLBACK; 170 eal_dev_hotplug_request_to_secondary(&tmp_req); 171 } 172 173 finish: 174 ret = send_response_to_secondary(&tmp_req, ret, bundle->peer); 175 if (ret) 176 RTE_LOG(ERR, EAL, "failed to send response to secondary\n"); 177 178 free(bundle->peer); 179 free(bundle); 180 } 181 182 static int 183 handle_secondary_request(const struct rte_mp_msg *msg, const void *peer) 184 { 185 struct mp_reply_bundle *bundle; 186 const struct eal_dev_mp_req *req = 187 (const struct eal_dev_mp_req *)msg->param; 188 int ret = 0; 189 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); 194 } 195 196 bundle->msg = *msg; 197 /** 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 200 * when it is ready. 201 */ 202 bundle->peer = strdup(peer); 203 204 /** 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. 207 */ 208 ret = rte_eal_alarm_set(1, __handle_secondary_request, bundle); 209 if (ret != 0) { 210 RTE_LOG(ERR, EAL, "failed to add mp task\n"); 211 free(bundle->peer); 212 free(bundle); 213 return send_response_to_secondary(req, ret, peer); 214 } 215 return 0; 216 } 217 218 static void __handle_primary_request(void *param) 219 { 220 struct mp_reply_bundle *bundle = param; 221 struct rte_mp_msg *msg = &bundle->msg; 222 const struct eal_dev_mp_req *req = 223 (const struct eal_dev_mp_req *)msg->param; 224 struct rte_mp_msg mp_resp; 225 struct eal_dev_mp_req *resp = 226 (struct eal_dev_mp_req *)mp_resp.param; 227 struct rte_devargs *da; 228 struct rte_device *dev; 229 struct rte_bus *bus; 230 int ret = 0; 231 232 memset(&mp_resp, 0, sizeof(mp_resp)); 233 234 switch (req->t) { 235 case EAL_DEV_REQ_TYPE_ATTACH: 236 case EAL_DEV_REQ_TYPE_DETACH_ROLLBACK: 237 ret = local_dev_probe(req->devargs, &dev); 238 break; 239 case EAL_DEV_REQ_TYPE_DETACH: 240 case EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK: 241 da = calloc(1, sizeof(*da)); 242 if (da == NULL) { 243 ret = -ENOMEM; 244 break; 245 } 246 247 ret = rte_devargs_parse(da, req->devargs); 248 if (ret != 0) 249 goto quit; 250 251 bus = rte_bus_find_by_name(da->bus->name); 252 if (bus == NULL) { 253 RTE_LOG(ERR, EAL, "Cannot find bus (%s)\n", da->bus->name); 254 ret = -ENOENT; 255 goto quit; 256 } 257 258 dev = bus->find_device(NULL, cmp_dev_name, da->name); 259 if (dev == NULL) { 260 RTE_LOG(ERR, EAL, "Cannot find plugged device (%s)\n", da->name); 261 ret = -ENOENT; 262 goto quit; 263 } 264 265 if (!rte_dev_is_probed(dev)) { 266 if (req->t == EAL_DEV_REQ_TYPE_ATTACH_ROLLBACK) { 267 /** 268 * Don't fail the rollback just because there's 269 * nothing to do. 270 */ 271 ret = 0; 272 } else 273 ret = -ENODEV; 274 275 goto quit; 276 } 277 278 ret = local_dev_remove(dev); 279 quit: 280 free(da->args); 281 free(da); 282 break; 283 default: 284 ret = -EINVAL; 285 } 286 287 strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name)); 288 mp_resp.len_param = sizeof(*req); 289 memcpy(resp, req, sizeof(*resp)); 290 resp->result = ret; 291 if (rte_mp_reply(&mp_resp, bundle->peer) < 0) 292 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n"); 293 294 free(bundle->peer); 295 free(bundle); 296 } 297 298 static int 299 handle_primary_request(const struct rte_mp_msg *msg, const void *peer) 300 { 301 struct rte_mp_msg mp_resp; 302 const struct eal_dev_mp_req *req = 303 (const struct eal_dev_mp_req *)msg->param; 304 struct eal_dev_mp_req *resp = 305 (struct eal_dev_mp_req *)mp_resp.param; 306 struct mp_reply_bundle *bundle; 307 int ret = 0; 308 309 memset(&mp_resp, 0, sizeof(mp_resp)); 310 strlcpy(mp_resp.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_resp.name)); 311 mp_resp.len_param = sizeof(*req); 312 memcpy(resp, req, sizeof(*resp)); 313 314 bundle = calloc(1, sizeof(*bundle)); 315 if (bundle == NULL) { 316 resp->result = -ENOMEM; 317 ret = rte_mp_reply(&mp_resp, peer); 318 if (ret) 319 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n"); 320 return ret; 321 } 322 323 bundle->msg = *msg; 324 /** 325 * We need to send reply on interrupt thread, but peer can't be 326 * parsed directly, so this is a temporal hack, need to be fixed 327 * when it is ready. 328 */ 329 bundle->peer = (void *)strdup(peer); 330 331 /** 332 * We are at IPC callback thread, sync IPC is not allowed due to 333 * dead lock, so we delegate the task to interrupt thread. 334 */ 335 ret = rte_eal_alarm_set(1, __handle_primary_request, bundle); 336 if (ret != 0) { 337 free(bundle->peer); 338 free(bundle); 339 resp->result = ret; 340 ret = rte_mp_reply(&mp_resp, peer); 341 if (ret != 0) { 342 RTE_LOG(ERR, EAL, "failed to send reply to primary request\n"); 343 return ret; 344 } 345 } 346 return 0; 347 } 348 349 int eal_dev_hotplug_request_to_primary(struct eal_dev_mp_req *req) 350 { 351 struct rte_mp_msg mp_req; 352 struct rte_mp_reply mp_reply; 353 struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0}; 354 struct eal_dev_mp_req *resp; 355 int ret; 356 357 memset(&mp_req, 0, sizeof(mp_req)); 358 memcpy(mp_req.param, req, sizeof(*req)); 359 mp_req.len_param = sizeof(*req); 360 strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name)); 361 362 ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts); 363 if (ret || mp_reply.nb_received != 1) { 364 RTE_LOG(ERR, EAL, "Cannot send request to primary\n"); 365 if (!ret) 366 return -1; 367 return ret; 368 } 369 370 resp = (struct eal_dev_mp_req *)mp_reply.msgs[0].param; 371 req->result = resp->result; 372 373 free(mp_reply.msgs); 374 return ret; 375 } 376 377 int eal_dev_hotplug_request_to_secondary(struct eal_dev_mp_req *req) 378 { 379 struct rte_mp_msg mp_req; 380 struct rte_mp_reply mp_reply; 381 struct timespec ts = {.tv_sec = MP_TIMEOUT_S, .tv_nsec = 0}; 382 int ret; 383 int i; 384 385 memset(&mp_req, 0, sizeof(mp_req)); 386 memcpy(mp_req.param, req, sizeof(*req)); 387 mp_req.len_param = sizeof(*req); 388 strlcpy(mp_req.name, EAL_DEV_MP_ACTION_REQUEST, sizeof(mp_req.name)); 389 390 ret = rte_mp_request_sync(&mp_req, &mp_reply, &ts); 391 if (ret != 0) { 392 RTE_LOG(ERR, EAL, "rte_mp_request_sync failed\n"); 393 return ret; 394 } 395 396 if (mp_reply.nb_sent != mp_reply.nb_received) { 397 RTE_LOG(ERR, EAL, "not all secondary reply\n"); 398 free(mp_reply.msgs); 399 return -1; 400 } 401 402 req->result = 0; 403 for (i = 0; i < mp_reply.nb_received; i++) { 404 struct eal_dev_mp_req *resp = 405 (struct eal_dev_mp_req *)mp_reply.msgs[i].param; 406 if (resp->result != 0) { 407 if (req->t == EAL_DEV_REQ_TYPE_ATTACH && 408 resp->result == -EEXIST) 409 continue; 410 if (req->t == EAL_DEV_REQ_TYPE_DETACH && 411 resp->result == -ENOENT) 412 continue; 413 req->result = resp->result; 414 } 415 } 416 417 free(mp_reply.msgs); 418 return 0; 419 } 420 421 int rte_mp_dev_hotplug_init(void) 422 { 423 int ret; 424 425 if (rte_eal_process_type() == RTE_PROC_PRIMARY) { 426 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST, 427 handle_secondary_request); 428 if (ret != 0) { 429 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n", 430 EAL_DEV_MP_ACTION_REQUEST); 431 return ret; 432 } 433 } else { 434 ret = rte_mp_action_register(EAL_DEV_MP_ACTION_REQUEST, 435 handle_primary_request); 436 if (ret != 0) { 437 RTE_LOG(ERR, EAL, "Couldn't register '%s' action\n", 438 EAL_DEV_MP_ACTION_REQUEST); 439 return ret; 440 } 441 } 442 443 return 0; 444 } 445