Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2018 Intel Corporation
3 : : */
4 : :
5 : : #include <stdlib.h>
6 : : #include <string.h>
7 : : #include <unistd.h>
8 : : #include <signal.h>
9 : : #include <sys/socket.h>
10 : : #include <linux/netlink.h>
11 : :
12 : : #include <rte_string_fns.h>
13 : : #include <rte_log.h>
14 : : #include <rte_dev.h>
15 : : #include <rte_interrupts.h>
16 : : #include <rte_alarm.h>
17 : : #include <bus_driver.h>
18 : : #include <rte_spinlock.h>
19 : : #include <rte_errno.h>
20 : :
21 : : #include <eal_export.h>
22 : : #include "eal_private.h"
23 : :
24 : : static struct rte_intr_handle *intr_handle;
25 : : static rte_rwlock_t monitor_lock = RTE_RWLOCK_INITIALIZER;
26 : : static uint32_t monitor_refcount;
27 : : static bool hotplug_handle;
28 : :
29 : : #define EAL_UEV_MSG_LEN 4096
30 : : #define EAL_UEV_MSG_ELEM_LEN 128
31 : :
32 : : /* Listen only to messages from kernel (not libudev) */
33 : : #define EAL_UEV_GROUP_KERNEL 1
34 : :
35 : : /* Large uevent buffer */
36 : : #define EAL_UEV_MSG_RCVBUF (4 * 1024 * 1024)
37 : :
38 : : /*
39 : : * spinlock for device hot-unplug failure handling. If it try to access bus or
40 : : * device, such as handle sigbus on bus or handle memory failure for device
41 : : * just need to use this lock. It could protect the bus and the device to avoid
42 : : * race condition.
43 : : */
44 : : static rte_spinlock_t failure_handle_lock = RTE_SPINLOCK_INITIALIZER;
45 : :
46 : : static struct sigaction sigbus_action_old;
47 : :
48 : : static int sigbus_need_recover;
49 : :
50 : : static void dev_uev_handler(__rte_unused void *param);
51 : :
52 : : /* identify the system layer which reports this event. */
53 : : enum eal_dev_event_subsystem {
54 : : EAL_DEV_EVENT_SUBSYSTEM_PCI, /* PCI bus device event */
55 : : EAL_DEV_EVENT_SUBSYSTEM_UIO, /* UIO driver device event */
56 : : EAL_DEV_EVENT_SUBSYSTEM_VFIO, /* VFIO driver device event */
57 : : EAL_DEV_EVENT_SUBSYSTEM_MAX
58 : : };
59 : :
60 : : static void
61 : : sigbus_action_recover(void)
62 : : {
63 : 0 : if (sigbus_need_recover) {
64 : 0 : sigaction(SIGBUS, &sigbus_action_old, NULL);
65 : 0 : sigbus_need_recover = 0;
66 : : }
67 : : }
68 : :
69 : 0 : static void sigbus_handler(int signum, siginfo_t *info,
70 : : void *ctx __rte_unused)
71 : : {
72 : : int ret;
73 : :
74 : 0 : EAL_LOG(DEBUG, "Thread catch SIGBUS, fault address:%p",
75 : : info->si_addr);
76 : :
77 : : rte_spinlock_lock(&failure_handle_lock);
78 : 0 : ret = rte_bus_sigbus_handler(info->si_addr);
79 : : rte_spinlock_unlock(&failure_handle_lock);
80 [ # # ]: 0 : if (ret == -1) {
81 : 0 : rte_exit(EXIT_FAILURE,
82 : : "Failed to handle SIGBUS for hot-unplug, "
83 : : "(rte_errno: %s)!", strerror(rte_errno));
84 [ # # ]: 0 : } else if (ret == 1) {
85 [ # # ]: 0 : if (sigbus_action_old.sa_flags == SA_SIGINFO
86 [ # # ]: 0 : && sigbus_action_old.sa_sigaction) {
87 : 0 : (*(sigbus_action_old.sa_sigaction))(signum,
88 : : info, ctx);
89 [ # # ]: 0 : } else if (sigbus_action_old.sa_flags != SA_SIGINFO
90 [ # # ]: 0 : && sigbus_action_old.sa_handler) {
91 : 0 : (*(sigbus_action_old.sa_handler))(signum);
92 : : } else {
93 : 0 : rte_exit(EXIT_FAILURE,
94 : : "Failed to handle generic SIGBUS!");
95 : : }
96 : : }
97 : :
98 : 0 : EAL_LOG(DEBUG, "Success to handle SIGBUS for hot-unplug!");
99 : 0 : }
100 : :
101 : 0 : static int cmp_dev_name(const struct rte_device *dev,
102 : : const void *_name)
103 : : {
104 : : const char *name = _name;
105 : :
106 : 0 : return strcmp(dev->name, name);
107 : : }
108 : :
109 : : /*
110 : : * To avoid losing uevents increase the netlink receive buffer size,
111 : : * and override the kernel clamp value if necessary.
112 : : */
113 : : static int
114 : 0 : dev_uev_set_rcvbuf(int fd, int n)
115 : : {
116 : : int ret, val;
117 : 0 : socklen_t len = sizeof(val);
118 : :
119 : 0 : ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
120 [ # # ]: 0 : if (ret < 0)
121 : : return ret;
122 : :
123 : : /* kernel may have clamped our request */
124 : 0 : ret = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len);
125 [ # # # # : 0 : if (ret >= 0 && len == sizeof(val) && val == n * 2)
# # ]
126 : : return 0; /* request worked */
127 : :
128 : : /* try again to override kernel restriction */
129 : 0 : return setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &n, sizeof(n));
130 : : }
131 : :
132 : : static int
133 : 0 : dev_uev_socket_fd_create(void)
134 : : {
135 : : struct sockaddr_nl addr;
136 : : int ret, fd;
137 : :
138 : 0 : fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
139 : : NETLINK_KOBJECT_UEVENT);
140 [ # # ]: 0 : if (fd < 0) {
141 : 0 : EAL_LOG(ERR, "create uevent fd failed.");
142 : 0 : return -1;
143 : : }
144 : :
145 : : memset(&addr, 0, sizeof(addr));
146 : 0 : addr.nl_family = AF_NETLINK;
147 : : addr.nl_pid = 0;
148 : 0 : addr.nl_groups = EAL_UEV_GROUP_KERNEL;
149 : :
150 : 0 : ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
151 [ # # ]: 0 : if (ret < 0) {
152 : 0 : EAL_LOG(ERR, "Failed to bind uevent socket.");
153 : 0 : goto err;
154 : : }
155 : :
156 [ # # ]: 0 : if (dev_uev_set_rcvbuf(fd, EAL_UEV_MSG_RCVBUF) < 0)
157 : 0 : EAL_LOG(NOTICE, "Failed to set rcvbuf.");
158 : :
159 [ # # ]: 0 : if (rte_intr_fd_set(intr_handle, fd))
160 : 0 : goto err;
161 : :
162 : : return 0;
163 : 0 : err:
164 : 0 : close(fd);
165 : 0 : return ret;
166 : : }
167 : :
168 : : struct rte_dev_event {
169 : : enum rte_dev_event_type type; /**< device event type */
170 : : int subsystem; /**< subsystem id */
171 : : char *devname; /**< device name */
172 : : };
173 : :
174 : : static int
175 : 0 : dev_uev_parse(const char *buf, struct rte_dev_event *event, int length)
176 : : {
177 : : char action[EAL_UEV_MSG_ELEM_LEN];
178 : : char subsystem[EAL_UEV_MSG_ELEM_LEN];
179 : : char pci_slot_name[EAL_UEV_MSG_ELEM_LEN];
180 : : int i = 0;
181 : :
182 : : memset(action, 0, EAL_UEV_MSG_ELEM_LEN);
183 : : memset(subsystem, 0, EAL_UEV_MSG_ELEM_LEN);
184 : : memset(pci_slot_name, 0, EAL_UEV_MSG_ELEM_LEN);
185 : :
186 [ # # ]: 0 : while (i < length) {
187 [ # # ]: 0 : for (; i < length; i++) {
188 [ # # ]: 0 : if (*buf)
189 : : break;
190 : 0 : buf++;
191 : : }
192 [ # # ]: 0 : if (i >= length)
193 : : break;
194 : :
195 [ # # ]: 0 : if (!strncmp(buf, "ACTION=", 7)) {
196 : 0 : buf += 7;
197 : 0 : i += 7;
198 : : strlcpy(action, buf, sizeof(action));
199 [ # # ]: 0 : } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
200 : 0 : buf += 10;
201 : 0 : i += 10;
202 : : strlcpy(subsystem, buf, sizeof(subsystem));
203 [ # # ]: 0 : } else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
204 : 0 : buf += 14;
205 : 0 : i += 14;
206 : : strlcpy(pci_slot_name, buf, sizeof(subsystem));
207 : 0 : event->devname = strdup(pci_slot_name);
208 [ # # ]: 0 : if (event->devname == NULL)
209 : : return -1;
210 : : }
211 [ # # ]: 0 : for (; i < length; i++) {
212 [ # # ]: 0 : if (*buf == '\0')
213 : : break;
214 : 0 : buf++;
215 : : }
216 : : }
217 : :
218 : : /* parse the subsystem layer */
219 [ # # ]: 0 : if (!strncmp(subsystem, "uio", 3))
220 : 0 : event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_UIO;
221 [ # # ]: 0 : else if (!strncmp(subsystem, "pci", 3))
222 : 0 : event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_PCI;
223 [ # # ]: 0 : else if (!strncmp(subsystem, "vfio", 4))
224 : 0 : event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_VFIO;
225 : : else
226 : 0 : goto err;
227 : :
228 : : /* parse the action type */
229 [ # # ]: 0 : if (!strncmp(action, "add", 3))
230 : 0 : event->type = RTE_DEV_EVENT_ADD;
231 [ # # ]: 0 : else if (!strncmp(action, "remove", 6))
232 : 0 : event->type = RTE_DEV_EVENT_REMOVE;
233 : : else
234 : 0 : goto err;
235 : : return 0;
236 : 0 : err:
237 : 0 : free(event->devname);
238 : 0 : return -1;
239 : : }
240 : :
241 : : static void
242 : 0 : dev_delayed_unregister(void *param)
243 : : {
244 : 0 : rte_intr_callback_unregister(intr_handle, dev_uev_handler, param);
245 [ # # ]: 0 : if (rte_intr_fd_get(intr_handle) >= 0) {
246 : 0 : close(rte_intr_fd_get(intr_handle));
247 : 0 : rte_intr_fd_set(intr_handle, -1);
248 : : }
249 : 0 : }
250 : :
251 : : static void
252 : 0 : dev_uev_handler(__rte_unused void *param)
253 : : {
254 : : struct rte_dev_event uevent;
255 : : int ret;
256 : : char buf[EAL_UEV_MSG_LEN + 1];
257 : : struct rte_bus *bus;
258 : : struct rte_device *dev;
259 : : const char *busname = "";
260 : :
261 : : memset(&uevent, 0, sizeof(struct rte_dev_event));
262 : : memset(buf, 0, EAL_UEV_MSG_LEN + 1);
263 : :
264 [ # # ]: 0 : if (rte_intr_fd_get(intr_handle) < 0)
265 : 0 : return;
266 : :
267 : 0 : ret = recv(rte_intr_fd_get(intr_handle), buf, EAL_UEV_MSG_LEN,
268 : : MSG_DONTWAIT);
269 [ # # ]: 0 : if (ret < 0) {
270 : : /* transient error */
271 [ # # ]: 0 : if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
272 : : return;
273 : :
274 : : /* kernel netlink messages lost */
275 [ # # ]: 0 : if (errno == ENOBUFS) {
276 : 0 : EAL_LOG(NOTICE, "kernel receive buffer overrun");
277 : 0 : return;
278 : : }
279 : :
280 : 0 : EAL_LOG(ERR, "unexpected error on uevent recv: %s",
281 : : strerror(errno));
282 : :
283 : 0 : rte_eal_alarm_set(1, dev_delayed_unregister, NULL);
284 : 0 : return;
285 : : }
286 : :
287 : 0 : ret = dev_uev_parse(buf, &uevent, EAL_UEV_MSG_LEN);
288 [ # # ]: 0 : if (ret < 0) {
289 : 0 : EAL_LOG(DEBUG, "Ignoring uevent '%s'", buf);
290 : 0 : return;
291 : : }
292 : :
293 : 0 : EAL_LOG(DEBUG, "receive uevent(name:%s, type:%d, subsystem:%d)",
294 : : uevent.devname, uevent.type, uevent.subsystem);
295 : :
296 [ # # ]: 0 : switch (uevent.subsystem) {
297 : 0 : case EAL_DEV_EVENT_SUBSYSTEM_PCI:
298 : : case EAL_DEV_EVENT_SUBSYSTEM_UIO:
299 : : busname = "pci";
300 : 0 : break;
301 : : default:
302 : : break;
303 : : }
304 : :
305 [ # # ]: 0 : if (uevent.devname) {
306 [ # # # # ]: 0 : if (uevent.type == RTE_DEV_EVENT_REMOVE && hotplug_handle) {
307 : : rte_spinlock_lock(&failure_handle_lock);
308 : 0 : bus = rte_bus_find_by_name(busname);
309 [ # # ]: 0 : if (bus == NULL) {
310 : 0 : EAL_LOG(ERR, "Cannot find bus (%s)",
311 : : busname);
312 : 0 : goto failure_handle_err;
313 : : }
314 : :
315 : 0 : dev = bus->find_device(bus, NULL, cmp_dev_name, uevent.devname);
316 [ # # ]: 0 : if (dev == NULL) {
317 : 0 : EAL_LOG(ERR, "Cannot find device (%s) on "
318 : : "bus (%s)", uevent.devname, busname);
319 : 0 : goto failure_handle_err;
320 : : }
321 : :
322 : 0 : ret = bus->hot_unplug_handler(dev);
323 [ # # ]: 0 : if (ret) {
324 : 0 : EAL_LOG(ERR, "Can not handle hot-unplug "
325 : : "for device (%s)", dev->name);
326 : : }
327 : : rte_spinlock_unlock(&failure_handle_lock);
328 : : }
329 : 0 : rte_dev_event_callback_process(uevent.devname, uevent.type);
330 : 0 : free(uevent.devname);
331 : : }
332 : :
333 : : return;
334 : :
335 : 0 : failure_handle_err:
336 : : rte_spinlock_unlock(&failure_handle_lock);
337 : 0 : free(uevent.devname);
338 : : }
339 : :
340 : : RTE_EXPORT_SYMBOL(rte_dev_event_monitor_start)
341 : : int
342 : 0 : rte_dev_event_monitor_start(void)
343 : : {
344 : : int ret = 0;
345 : :
346 : 0 : rte_rwlock_write_lock(&monitor_lock);
347 : :
348 [ # # ]: 0 : if (monitor_refcount) {
349 : 0 : monitor_refcount++;
350 : 0 : goto exit;
351 : : }
352 : :
353 : 0 : intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
354 [ # # ]: 0 : if (intr_handle == NULL) {
355 : 0 : EAL_LOG(ERR, "Fail to allocate intr_handle");
356 : 0 : goto exit;
357 : : }
358 : :
359 : 0 : ret = rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_DEV_EVENT);
360 [ # # ]: 0 : if (ret)
361 : 0 : goto exit;
362 : :
363 : 0 : ret = rte_intr_fd_set(intr_handle, -1);
364 [ # # ]: 0 : if (ret)
365 : 0 : goto exit;
366 : :
367 : 0 : ret = dev_uev_socket_fd_create();
368 [ # # ]: 0 : if (ret) {
369 : 0 : EAL_LOG(ERR, "error create device event fd.");
370 : 0 : goto exit;
371 : : }
372 : :
373 : 0 : ret = rte_intr_callback_register(intr_handle, dev_uev_handler, NULL);
374 : :
375 [ # # ]: 0 : if (ret) {
376 : 0 : close(rte_intr_fd_get(intr_handle));
377 : 0 : goto exit;
378 : : }
379 : :
380 : 0 : monitor_refcount++;
381 : :
382 : 0 : exit:
383 [ # # ]: 0 : if (ret) {
384 : 0 : rte_intr_instance_free(intr_handle);
385 : 0 : intr_handle = NULL;
386 : : }
387 : : rte_rwlock_write_unlock(&monitor_lock);
388 : 0 : return ret;
389 : : }
390 : :
391 : : RTE_EXPORT_SYMBOL(rte_dev_event_monitor_stop)
392 : : int
393 : 0 : rte_dev_event_monitor_stop(void)
394 : : {
395 : : int ret = 0;
396 : :
397 : 0 : rte_rwlock_write_lock(&monitor_lock);
398 : :
399 [ # # ]: 0 : if (!monitor_refcount) {
400 : 0 : EAL_LOG(ERR, "device event monitor already stopped");
401 : 0 : goto exit;
402 : : }
403 : :
404 [ # # ]: 0 : if (monitor_refcount > 1) {
405 : 0 : monitor_refcount--;
406 : 0 : goto exit;
407 : : }
408 : :
409 : 0 : ret = rte_intr_callback_unregister(intr_handle, dev_uev_handler,
410 : : (void *)-1);
411 [ # # ]: 0 : if (ret < 0) {
412 : 0 : EAL_LOG(ERR, "fail to unregister uevent callback.");
413 : 0 : goto exit;
414 : : }
415 : :
416 : 0 : close(rte_intr_fd_get(intr_handle));
417 : 0 : rte_intr_instance_free(intr_handle);
418 : 0 : intr_handle = NULL;
419 : : ret = 0;
420 : :
421 : 0 : monitor_refcount--;
422 : :
423 : 0 : exit:
424 : : rte_rwlock_write_unlock(&monitor_lock);
425 : :
426 : 0 : return ret;
427 : : }
428 : :
429 : : static int
430 : 0 : dev_sigbus_handler_register(void)
431 : : {
432 : : sigset_t mask;
433 : : struct sigaction action;
434 : :
435 : 0 : rte_errno = 0;
436 : :
437 [ # # ]: 0 : if (sigbus_need_recover)
438 : : return 0;
439 : :
440 : 0 : sigemptyset(&mask);
441 : 0 : sigaddset(&mask, SIGBUS);
442 : 0 : action.sa_flags = SA_SIGINFO;
443 : 0 : action.sa_mask = mask;
444 : 0 : action.sa_sigaction = sigbus_handler;
445 : 0 : sigbus_need_recover = !sigaction(SIGBUS, &action, &sigbus_action_old);
446 : :
447 : 0 : return rte_errno;
448 : : }
449 : :
450 : : static int
451 : 0 : dev_sigbus_handler_unregister(void)
452 : : {
453 [ # # ]: 0 : rte_errno = 0;
454 : :
455 : : sigbus_action_recover();
456 : :
457 : 0 : return rte_errno;
458 : : }
459 : :
460 : : RTE_EXPORT_SYMBOL(rte_dev_hotplug_handle_enable)
461 : : int
462 : 0 : rte_dev_hotplug_handle_enable(void)
463 : : {
464 : : int ret = 0;
465 : :
466 : 0 : ret = dev_sigbus_handler_register();
467 [ # # ]: 0 : if (ret < 0)
468 : 0 : EAL_LOG(ERR,
469 : : "fail to register sigbus handler for devices.");
470 : :
471 : 0 : hotplug_handle = true;
472 : :
473 : 0 : return ret;
474 : : }
475 : :
476 : : RTE_EXPORT_SYMBOL(rte_dev_hotplug_handle_disable)
477 : : int
478 : 0 : rte_dev_hotplug_handle_disable(void)
479 : : {
480 : : int ret = 0;
481 : :
482 : 0 : ret = dev_sigbus_handler_unregister();
483 [ # # ]: 0 : if (ret < 0)
484 : 0 : EAL_LOG(ERR,
485 : : "fail to unregister sigbus handler for devices.");
486 : :
487 : 0 : hotplug_handle = false;
488 : :
489 : 0 : return ret;
490 : : }
|