xref: /dpdk/lib/eal/linux/eal_dev.c (revision fcfb19cda4f6439f260cdeabb6c44d3c6f0d5fc4)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2018 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
572b452c5SDmitry Kozlyuk #include <stdlib.h>
699a2dd95SBruce Richardson #include <string.h>
799a2dd95SBruce Richardson #include <unistd.h>
899a2dd95SBruce Richardson #include <signal.h>
999a2dd95SBruce Richardson #include <sys/socket.h>
1099a2dd95SBruce Richardson #include <linux/netlink.h>
1199a2dd95SBruce Richardson 
1299a2dd95SBruce Richardson #include <rte_string_fns.h>
1399a2dd95SBruce Richardson #include <rte_log.h>
1499a2dd95SBruce Richardson #include <rte_dev.h>
1599a2dd95SBruce Richardson #include <rte_interrupts.h>
1699a2dd95SBruce Richardson #include <rte_alarm.h>
17a04322f6SDavid Marchand #include <bus_driver.h>
1899a2dd95SBruce Richardson #include <rte_spinlock.h>
1999a2dd95SBruce Richardson #include <rte_errno.h>
2099a2dd95SBruce Richardson 
2199a2dd95SBruce Richardson #include "eal_private.h"
2299a2dd95SBruce Richardson 
23c2bd9367SHarman Kalra static struct rte_intr_handle *intr_handle;
2499a2dd95SBruce Richardson static rte_rwlock_t monitor_lock = RTE_RWLOCK_INITIALIZER;
2599a2dd95SBruce Richardson static uint32_t monitor_refcount;
2699a2dd95SBruce Richardson static bool hotplug_handle;
2799a2dd95SBruce Richardson 
2899a2dd95SBruce Richardson #define EAL_UEV_MSG_LEN 4096
2999a2dd95SBruce Richardson #define EAL_UEV_MSG_ELEM_LEN 128
3099a2dd95SBruce Richardson 
3199a2dd95SBruce Richardson /*
3299a2dd95SBruce Richardson  * spinlock for device hot-unplug failure handling. If it try to access bus or
3399a2dd95SBruce Richardson  * device, such as handle sigbus on bus or handle memory failure for device
3499a2dd95SBruce Richardson  * just need to use this lock. It could protect the bus and the device to avoid
3599a2dd95SBruce Richardson  * race condition.
3699a2dd95SBruce Richardson  */
3799a2dd95SBruce Richardson static rte_spinlock_t failure_handle_lock = RTE_SPINLOCK_INITIALIZER;
3899a2dd95SBruce Richardson 
3999a2dd95SBruce Richardson static struct sigaction sigbus_action_old;
4099a2dd95SBruce Richardson 
4199a2dd95SBruce Richardson static int sigbus_need_recover;
4299a2dd95SBruce Richardson 
4399a2dd95SBruce Richardson static void dev_uev_handler(__rte_unused void *param);
4499a2dd95SBruce Richardson 
4599a2dd95SBruce Richardson /* identify the system layer which reports this event. */
4699a2dd95SBruce Richardson enum eal_dev_event_subsystem {
4799a2dd95SBruce Richardson 	EAL_DEV_EVENT_SUBSYSTEM_PCI, /* PCI bus device event */
4899a2dd95SBruce Richardson 	EAL_DEV_EVENT_SUBSYSTEM_UIO, /* UIO driver device event */
4999a2dd95SBruce Richardson 	EAL_DEV_EVENT_SUBSYSTEM_VFIO, /* VFIO driver device event */
5099a2dd95SBruce Richardson 	EAL_DEV_EVENT_SUBSYSTEM_MAX
5199a2dd95SBruce Richardson };
5299a2dd95SBruce Richardson 
5399a2dd95SBruce Richardson static void
sigbus_action_recover(void)5499a2dd95SBruce Richardson sigbus_action_recover(void)
5599a2dd95SBruce Richardson {
5699a2dd95SBruce Richardson 	if (sigbus_need_recover) {
5799a2dd95SBruce Richardson 		sigaction(SIGBUS, &sigbus_action_old, NULL);
5899a2dd95SBruce Richardson 		sigbus_need_recover = 0;
5999a2dd95SBruce Richardson 	}
6099a2dd95SBruce Richardson }
6199a2dd95SBruce Richardson 
sigbus_handler(int signum,siginfo_t * info,void * ctx __rte_unused)6299a2dd95SBruce Richardson static void sigbus_handler(int signum, siginfo_t *info,
6399a2dd95SBruce Richardson 				void *ctx __rte_unused)
6499a2dd95SBruce Richardson {
6599a2dd95SBruce Richardson 	int ret;
6699a2dd95SBruce Richardson 
67ae67895bSDavid Marchand 	EAL_LOG(DEBUG, "Thread catch SIGBUS, fault address:%p",
6899a2dd95SBruce Richardson 		info->si_addr);
6999a2dd95SBruce Richardson 
7099a2dd95SBruce Richardson 	rte_spinlock_lock(&failure_handle_lock);
7199a2dd95SBruce Richardson 	ret = rte_bus_sigbus_handler(info->si_addr);
7299a2dd95SBruce Richardson 	rte_spinlock_unlock(&failure_handle_lock);
7399a2dd95SBruce Richardson 	if (ret == -1) {
7499a2dd95SBruce Richardson 		rte_exit(EXIT_FAILURE,
7599a2dd95SBruce Richardson 			 "Failed to handle SIGBUS for hot-unplug, "
7699a2dd95SBruce Richardson 			 "(rte_errno: %s)!", strerror(rte_errno));
7799a2dd95SBruce Richardson 	} else if (ret == 1) {
7899a2dd95SBruce Richardson 		if (sigbus_action_old.sa_flags == SA_SIGINFO
7999a2dd95SBruce Richardson 		    && sigbus_action_old.sa_sigaction) {
8099a2dd95SBruce Richardson 			(*(sigbus_action_old.sa_sigaction))(signum,
8199a2dd95SBruce Richardson 							    info, ctx);
8299a2dd95SBruce Richardson 		} else if (sigbus_action_old.sa_flags != SA_SIGINFO
8399a2dd95SBruce Richardson 			   && sigbus_action_old.sa_handler) {
8499a2dd95SBruce Richardson 			(*(sigbus_action_old.sa_handler))(signum);
8599a2dd95SBruce Richardson 		} else {
8699a2dd95SBruce Richardson 			rte_exit(EXIT_FAILURE,
8799a2dd95SBruce Richardson 				 "Failed to handle generic SIGBUS!");
8899a2dd95SBruce Richardson 		}
8999a2dd95SBruce Richardson 	}
9099a2dd95SBruce Richardson 
91ae67895bSDavid Marchand 	EAL_LOG(DEBUG, "Success to handle SIGBUS for hot-unplug!");
9299a2dd95SBruce Richardson }
9399a2dd95SBruce Richardson 
cmp_dev_name(const struct rte_device * dev,const void * _name)9499a2dd95SBruce Richardson static int cmp_dev_name(const struct rte_device *dev,
9599a2dd95SBruce Richardson 	const void *_name)
9699a2dd95SBruce Richardson {
9799a2dd95SBruce Richardson 	const char *name = _name;
9899a2dd95SBruce Richardson 
9999a2dd95SBruce Richardson 	return strcmp(dev->name, name);
10099a2dd95SBruce Richardson }
10199a2dd95SBruce Richardson 
10299a2dd95SBruce Richardson static int
dev_uev_socket_fd_create(void)10399a2dd95SBruce Richardson dev_uev_socket_fd_create(void)
10499a2dd95SBruce Richardson {
10599a2dd95SBruce Richardson 	struct sockaddr_nl addr;
106c2bd9367SHarman Kalra 	int ret, fd;
10799a2dd95SBruce Richardson 
108c2bd9367SHarman Kalra 	fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
10999a2dd95SBruce Richardson 		    NETLINK_KOBJECT_UEVENT);
110c2bd9367SHarman Kalra 	if (fd < 0) {
111ae67895bSDavid Marchand 		EAL_LOG(ERR, "create uevent fd failed.");
11299a2dd95SBruce Richardson 		return -1;
11399a2dd95SBruce Richardson 	}
11499a2dd95SBruce Richardson 
11599a2dd95SBruce Richardson 	memset(&addr, 0, sizeof(addr));
11699a2dd95SBruce Richardson 	addr.nl_family = AF_NETLINK;
11799a2dd95SBruce Richardson 	addr.nl_pid = 0;
11899a2dd95SBruce Richardson 	addr.nl_groups = 0xffffffff;
11999a2dd95SBruce Richardson 
120c2bd9367SHarman Kalra 	ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
12199a2dd95SBruce Richardson 	if (ret < 0) {
122ae67895bSDavid Marchand 		EAL_LOG(ERR, "Failed to bind uevent socket.");
12399a2dd95SBruce Richardson 		goto err;
12499a2dd95SBruce Richardson 	}
12599a2dd95SBruce Richardson 
126c2bd9367SHarman Kalra 	if (rte_intr_fd_set(intr_handle, fd))
127c2bd9367SHarman Kalra 		goto err;
128c2bd9367SHarman Kalra 
12999a2dd95SBruce Richardson 	return 0;
13099a2dd95SBruce Richardson err:
131c2bd9367SHarman Kalra 	close(fd);
132c2bd9367SHarman Kalra 	fd = -1;
13399a2dd95SBruce Richardson 	return ret;
13499a2dd95SBruce Richardson }
13599a2dd95SBruce Richardson 
13699a2dd95SBruce Richardson struct rte_dev_event {
13799a2dd95SBruce Richardson 	enum rte_dev_event_type type;	/**< device event type */
13899a2dd95SBruce Richardson 	int subsystem;			/**< subsystem id */
13999a2dd95SBruce Richardson 	char *devname;			/**< device name */
14099a2dd95SBruce Richardson };
14199a2dd95SBruce Richardson 
14299a2dd95SBruce Richardson static int
dev_uev_parse(const char * buf,struct rte_dev_event * event,int length)14399a2dd95SBruce Richardson dev_uev_parse(const char *buf, struct rte_dev_event *event, int length)
14499a2dd95SBruce Richardson {
14599a2dd95SBruce Richardson 	char action[EAL_UEV_MSG_ELEM_LEN];
14699a2dd95SBruce Richardson 	char subsystem[EAL_UEV_MSG_ELEM_LEN];
14799a2dd95SBruce Richardson 	char pci_slot_name[EAL_UEV_MSG_ELEM_LEN];
14899a2dd95SBruce Richardson 	int i = 0;
14999a2dd95SBruce Richardson 
15099a2dd95SBruce Richardson 	memset(action, 0, EAL_UEV_MSG_ELEM_LEN);
15199a2dd95SBruce Richardson 	memset(subsystem, 0, EAL_UEV_MSG_ELEM_LEN);
15299a2dd95SBruce Richardson 	memset(pci_slot_name, 0, EAL_UEV_MSG_ELEM_LEN);
15399a2dd95SBruce Richardson 
15499a2dd95SBruce Richardson 	while (i < length) {
15599a2dd95SBruce Richardson 		for (; i < length; i++) {
15699a2dd95SBruce Richardson 			if (*buf)
15799a2dd95SBruce Richardson 				break;
15899a2dd95SBruce Richardson 			buf++;
15999a2dd95SBruce Richardson 		}
1604847122aSDavid Marchand 		if (i >= length)
1614847122aSDavid Marchand 			break;
1624847122aSDavid Marchand 
16399a2dd95SBruce Richardson 		/**
16499a2dd95SBruce Richardson 		 * check device uevent from kernel side, no need to check
16599a2dd95SBruce Richardson 		 * uevent from udev.
16699a2dd95SBruce Richardson 		 */
16799a2dd95SBruce Richardson 		if (!strncmp(buf, "libudev", 7)) {
16899a2dd95SBruce Richardson 			buf += 7;
16999a2dd95SBruce Richardson 			i += 7;
17099a2dd95SBruce Richardson 			return -1;
17199a2dd95SBruce Richardson 		}
17299a2dd95SBruce Richardson 		if (!strncmp(buf, "ACTION=", 7)) {
17399a2dd95SBruce Richardson 			buf += 7;
17499a2dd95SBruce Richardson 			i += 7;
17599a2dd95SBruce Richardson 			strlcpy(action, buf, sizeof(action));
17699a2dd95SBruce Richardson 		} else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
17799a2dd95SBruce Richardson 			buf += 10;
17899a2dd95SBruce Richardson 			i += 10;
17999a2dd95SBruce Richardson 			strlcpy(subsystem, buf, sizeof(subsystem));
18099a2dd95SBruce Richardson 		} else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
18199a2dd95SBruce Richardson 			buf += 14;
18299a2dd95SBruce Richardson 			i += 14;
18399a2dd95SBruce Richardson 			strlcpy(pci_slot_name, buf, sizeof(subsystem));
18499a2dd95SBruce Richardson 			event->devname = strdup(pci_slot_name);
1855821a384SChengwen Feng 			if (event->devname == NULL)
1865821a384SChengwen Feng 				return -1;
18799a2dd95SBruce Richardson 		}
18899a2dd95SBruce Richardson 		for (; i < length; i++) {
18999a2dd95SBruce Richardson 			if (*buf == '\0')
19099a2dd95SBruce Richardson 				break;
19199a2dd95SBruce Richardson 			buf++;
19299a2dd95SBruce Richardson 		}
19399a2dd95SBruce Richardson 	}
19499a2dd95SBruce Richardson 
19599a2dd95SBruce Richardson 	/* parse the subsystem layer */
19699a2dd95SBruce Richardson 	if (!strncmp(subsystem, "uio", 3))
19799a2dd95SBruce Richardson 		event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_UIO;
19899a2dd95SBruce Richardson 	else if (!strncmp(subsystem, "pci", 3))
19999a2dd95SBruce Richardson 		event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_PCI;
20099a2dd95SBruce Richardson 	else if (!strncmp(subsystem, "vfio", 4))
20199a2dd95SBruce Richardson 		event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_VFIO;
20299a2dd95SBruce Richardson 	else
20399a2dd95SBruce Richardson 		goto err;
20499a2dd95SBruce Richardson 
20599a2dd95SBruce Richardson 	/* parse the action type */
20699a2dd95SBruce Richardson 	if (!strncmp(action, "add", 3))
20799a2dd95SBruce Richardson 		event->type = RTE_DEV_EVENT_ADD;
20899a2dd95SBruce Richardson 	else if (!strncmp(action, "remove", 6))
20999a2dd95SBruce Richardson 		event->type = RTE_DEV_EVENT_REMOVE;
21099a2dd95SBruce Richardson 	else
21199a2dd95SBruce Richardson 		goto err;
21299a2dd95SBruce Richardson 	return 0;
21399a2dd95SBruce Richardson err:
21499a2dd95SBruce Richardson 	free(event->devname);
21599a2dd95SBruce Richardson 	return -1;
21699a2dd95SBruce Richardson }
21799a2dd95SBruce Richardson 
21899a2dd95SBruce Richardson static void
dev_delayed_unregister(void * param)21999a2dd95SBruce Richardson dev_delayed_unregister(void *param)
22099a2dd95SBruce Richardson {
221c2bd9367SHarman Kalra 	rte_intr_callback_unregister(intr_handle, dev_uev_handler, param);
2227e2083e4SHarman Kalra 	if (rte_intr_fd_get(intr_handle) >= 0) {
223c2bd9367SHarman Kalra 		close(rte_intr_fd_get(intr_handle));
224c2bd9367SHarman Kalra 		rte_intr_fd_set(intr_handle, -1);
22599a2dd95SBruce Richardson 	}
2267e2083e4SHarman Kalra }
22799a2dd95SBruce Richardson 
22899a2dd95SBruce Richardson static void
dev_uev_handler(__rte_unused void * param)22999a2dd95SBruce Richardson dev_uev_handler(__rte_unused void *param)
23099a2dd95SBruce Richardson {
23199a2dd95SBruce Richardson 	struct rte_dev_event uevent;
23299a2dd95SBruce Richardson 	int ret;
2331a287fc9SSteve Yang 	char buf[EAL_UEV_MSG_LEN + 1];
23499a2dd95SBruce Richardson 	struct rte_bus *bus;
23599a2dd95SBruce Richardson 	struct rte_device *dev;
23699a2dd95SBruce Richardson 	const char *busname = "";
23799a2dd95SBruce Richardson 
23899a2dd95SBruce Richardson 	memset(&uevent, 0, sizeof(struct rte_dev_event));
2391a287fc9SSteve Yang 	memset(buf, 0, EAL_UEV_MSG_LEN + 1);
24099a2dd95SBruce Richardson 
2417e2083e4SHarman Kalra 	if (rte_intr_fd_get(intr_handle) < 0)
2427e2083e4SHarman Kalra 		return;
2437e2083e4SHarman Kalra 
244c2bd9367SHarman Kalra 	ret = recv(rte_intr_fd_get(intr_handle), buf, EAL_UEV_MSG_LEN,
245c2bd9367SHarman Kalra 		   MSG_DONTWAIT);
24699a2dd95SBruce Richardson 	if (ret < 0 && errno == EAGAIN)
24799a2dd95SBruce Richardson 		return;
24899a2dd95SBruce Richardson 	else if (ret <= 0) {
24999a2dd95SBruce Richardson 		/* connection is closed or broken, can not up again. */
250ae67895bSDavid Marchand 		EAL_LOG(ERR, "uevent socket connection is broken.");
25199a2dd95SBruce Richardson 		rte_eal_alarm_set(1, dev_delayed_unregister, NULL);
25299a2dd95SBruce Richardson 		return;
25399a2dd95SBruce Richardson 	}
25499a2dd95SBruce Richardson 
25599a2dd95SBruce Richardson 	ret = dev_uev_parse(buf, &uevent, EAL_UEV_MSG_LEN);
25699a2dd95SBruce Richardson 	if (ret < 0) {
257ae67895bSDavid Marchand 		EAL_LOG(DEBUG, "Ignoring uevent '%s'", buf);
25899a2dd95SBruce Richardson 		return;
25999a2dd95SBruce Richardson 	}
26099a2dd95SBruce Richardson 
261ae67895bSDavid Marchand 	EAL_LOG(DEBUG, "receive uevent(name:%s, type:%d, subsystem:%d)",
26299a2dd95SBruce Richardson 		uevent.devname, uevent.type, uevent.subsystem);
26399a2dd95SBruce Richardson 
26499a2dd95SBruce Richardson 	switch (uevent.subsystem) {
26599a2dd95SBruce Richardson 	case EAL_DEV_EVENT_SUBSYSTEM_PCI:
26699a2dd95SBruce Richardson 	case EAL_DEV_EVENT_SUBSYSTEM_UIO:
26799a2dd95SBruce Richardson 		busname = "pci";
26899a2dd95SBruce Richardson 		break;
26999a2dd95SBruce Richardson 	default:
27099a2dd95SBruce Richardson 		break;
27199a2dd95SBruce Richardson 	}
27299a2dd95SBruce Richardson 
27399a2dd95SBruce Richardson 	if (uevent.devname) {
27499a2dd95SBruce Richardson 		if (uevent.type == RTE_DEV_EVENT_REMOVE && hotplug_handle) {
27599a2dd95SBruce Richardson 			rte_spinlock_lock(&failure_handle_lock);
27699a2dd95SBruce Richardson 			bus = rte_bus_find_by_name(busname);
27799a2dd95SBruce Richardson 			if (bus == NULL) {
278ae67895bSDavid Marchand 				EAL_LOG(ERR, "Cannot find bus (%s)",
27999a2dd95SBruce Richardson 					busname);
28099a2dd95SBruce Richardson 				goto failure_handle_err;
28199a2dd95SBruce Richardson 			}
28299a2dd95SBruce Richardson 
28399a2dd95SBruce Richardson 			dev = bus->find_device(NULL, cmp_dev_name,
28499a2dd95SBruce Richardson 					       uevent.devname);
28599a2dd95SBruce Richardson 			if (dev == NULL) {
286ae67895bSDavid Marchand 				EAL_LOG(ERR, "Cannot find device (%s) on "
287ae67895bSDavid Marchand 					"bus (%s)", uevent.devname, busname);
28899a2dd95SBruce Richardson 				goto failure_handle_err;
28999a2dd95SBruce Richardson 			}
29099a2dd95SBruce Richardson 
29199a2dd95SBruce Richardson 			ret = bus->hot_unplug_handler(dev);
29299a2dd95SBruce Richardson 			if (ret) {
293ae67895bSDavid Marchand 				EAL_LOG(ERR, "Can not handle hot-unplug "
294ae67895bSDavid Marchand 					"for device (%s)", dev->name);
29599a2dd95SBruce Richardson 			}
29699a2dd95SBruce Richardson 			rte_spinlock_unlock(&failure_handle_lock);
29799a2dd95SBruce Richardson 		}
29899a2dd95SBruce Richardson 		rte_dev_event_callback_process(uevent.devname, uevent.type);
29999a2dd95SBruce Richardson 		free(uevent.devname);
30099a2dd95SBruce Richardson 	}
30199a2dd95SBruce Richardson 
30299a2dd95SBruce Richardson 	return;
30399a2dd95SBruce Richardson 
30499a2dd95SBruce Richardson failure_handle_err:
30599a2dd95SBruce Richardson 	rte_spinlock_unlock(&failure_handle_lock);
30699a2dd95SBruce Richardson 	free(uevent.devname);
30799a2dd95SBruce Richardson }
30899a2dd95SBruce Richardson 
30999a2dd95SBruce Richardson int
rte_dev_event_monitor_start(void)31099a2dd95SBruce Richardson rte_dev_event_monitor_start(void)
31199a2dd95SBruce Richardson {
31299a2dd95SBruce Richardson 	int ret = 0;
31399a2dd95SBruce Richardson 
31499a2dd95SBruce Richardson 	rte_rwlock_write_lock(&monitor_lock);
31599a2dd95SBruce Richardson 
31699a2dd95SBruce Richardson 	if (monitor_refcount) {
31799a2dd95SBruce Richardson 		monitor_refcount++;
31899a2dd95SBruce Richardson 		goto exit;
31999a2dd95SBruce Richardson 	}
32099a2dd95SBruce Richardson 
321c2bd9367SHarman Kalra 	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
322c2bd9367SHarman Kalra 	if (intr_handle == NULL) {
323ae67895bSDavid Marchand 		EAL_LOG(ERR, "Fail to allocate intr_handle");
324c2bd9367SHarman Kalra 		goto exit;
325c2bd9367SHarman Kalra 	}
326c2bd9367SHarman Kalra 
32756331733SDavid Marchand 	ret = rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_DEV_EVENT);
32856331733SDavid Marchand 	if (ret)
329c2bd9367SHarman Kalra 		goto exit;
330c2bd9367SHarman Kalra 
33156331733SDavid Marchand 	ret = rte_intr_fd_set(intr_handle, -1);
33256331733SDavid Marchand 	if (ret)
333c2bd9367SHarman Kalra 		goto exit;
334c2bd9367SHarman Kalra 
33599a2dd95SBruce Richardson 	ret = dev_uev_socket_fd_create();
33699a2dd95SBruce Richardson 	if (ret) {
337ae67895bSDavid Marchand 		EAL_LOG(ERR, "error create device event fd.");
33899a2dd95SBruce Richardson 		goto exit;
33999a2dd95SBruce Richardson 	}
34099a2dd95SBruce Richardson 
341c2bd9367SHarman Kalra 	ret = rte_intr_callback_register(intr_handle, dev_uev_handler, NULL);
34299a2dd95SBruce Richardson 
34399a2dd95SBruce Richardson 	if (ret) {
344c2bd9367SHarman Kalra 		close(rte_intr_fd_get(intr_handle));
34599a2dd95SBruce Richardson 		goto exit;
34699a2dd95SBruce Richardson 	}
34799a2dd95SBruce Richardson 
34899a2dd95SBruce Richardson 	monitor_refcount++;
34999a2dd95SBruce Richardson 
35099a2dd95SBruce Richardson exit:
35156331733SDavid Marchand 	if (ret) {
352c2bd9367SHarman Kalra 		rte_intr_instance_free(intr_handle);
35356331733SDavid Marchand 		intr_handle = NULL;
35456331733SDavid Marchand 	}
35599a2dd95SBruce Richardson 	rte_rwlock_write_unlock(&monitor_lock);
35699a2dd95SBruce Richardson 	return ret;
35799a2dd95SBruce Richardson }
35899a2dd95SBruce Richardson 
35999a2dd95SBruce Richardson int
rte_dev_event_monitor_stop(void)36099a2dd95SBruce Richardson rte_dev_event_monitor_stop(void)
36199a2dd95SBruce Richardson {
36299a2dd95SBruce Richardson 	int ret = 0;
36399a2dd95SBruce Richardson 
36499a2dd95SBruce Richardson 	rte_rwlock_write_lock(&monitor_lock);
36599a2dd95SBruce Richardson 
36699a2dd95SBruce Richardson 	if (!monitor_refcount) {
367ae67895bSDavid Marchand 		EAL_LOG(ERR, "device event monitor already stopped");
36899a2dd95SBruce Richardson 		goto exit;
36999a2dd95SBruce Richardson 	}
37099a2dd95SBruce Richardson 
37199a2dd95SBruce Richardson 	if (monitor_refcount > 1) {
37299a2dd95SBruce Richardson 		monitor_refcount--;
37399a2dd95SBruce Richardson 		goto exit;
37499a2dd95SBruce Richardson 	}
37599a2dd95SBruce Richardson 
376c2bd9367SHarman Kalra 	ret = rte_intr_callback_unregister(intr_handle, dev_uev_handler,
37799a2dd95SBruce Richardson 					   (void *)-1);
37899a2dd95SBruce Richardson 	if (ret < 0) {
379ae67895bSDavid Marchand 		EAL_LOG(ERR, "fail to unregister uevent callback.");
38099a2dd95SBruce Richardson 		goto exit;
38199a2dd95SBruce Richardson 	}
38299a2dd95SBruce Richardson 
383c2bd9367SHarman Kalra 	close(rte_intr_fd_get(intr_handle));
384c2bd9367SHarman Kalra 	rte_intr_instance_free(intr_handle);
38556331733SDavid Marchand 	intr_handle = NULL;
3864e3582abSWenxuan Wu 	ret = 0;
38799a2dd95SBruce Richardson 
38899a2dd95SBruce Richardson 	monitor_refcount--;
38999a2dd95SBruce Richardson 
39099a2dd95SBruce Richardson exit:
39199a2dd95SBruce Richardson 	rte_rwlock_write_unlock(&monitor_lock);
39299a2dd95SBruce Richardson 
39399a2dd95SBruce Richardson 	return ret;
39499a2dd95SBruce Richardson }
39599a2dd95SBruce Richardson 
396*fcfb19cdSStephen Hemminger static int
dev_sigbus_handler_register(void)39799a2dd95SBruce Richardson dev_sigbus_handler_register(void)
39899a2dd95SBruce Richardson {
39999a2dd95SBruce Richardson 	sigset_t mask;
40099a2dd95SBruce Richardson 	struct sigaction action;
40199a2dd95SBruce Richardson 
40299a2dd95SBruce Richardson 	rte_errno = 0;
40399a2dd95SBruce Richardson 
40499a2dd95SBruce Richardson 	if (sigbus_need_recover)
40599a2dd95SBruce Richardson 		return 0;
40699a2dd95SBruce Richardson 
40799a2dd95SBruce Richardson 	sigemptyset(&mask);
40899a2dd95SBruce Richardson 	sigaddset(&mask, SIGBUS);
40999a2dd95SBruce Richardson 	action.sa_flags = SA_SIGINFO;
41099a2dd95SBruce Richardson 	action.sa_mask = mask;
41199a2dd95SBruce Richardson 	action.sa_sigaction = sigbus_handler;
41299a2dd95SBruce Richardson 	sigbus_need_recover = !sigaction(SIGBUS, &action, &sigbus_action_old);
41399a2dd95SBruce Richardson 
41499a2dd95SBruce Richardson 	return rte_errno;
41599a2dd95SBruce Richardson }
41699a2dd95SBruce Richardson 
417*fcfb19cdSStephen Hemminger static int
dev_sigbus_handler_unregister(void)41899a2dd95SBruce Richardson dev_sigbus_handler_unregister(void)
41999a2dd95SBruce Richardson {
42099a2dd95SBruce Richardson 	rte_errno = 0;
42199a2dd95SBruce Richardson 
42299a2dd95SBruce Richardson 	sigbus_action_recover();
42399a2dd95SBruce Richardson 
42499a2dd95SBruce Richardson 	return rte_errno;
42599a2dd95SBruce Richardson }
42699a2dd95SBruce Richardson 
42799a2dd95SBruce Richardson int
rte_dev_hotplug_handle_enable(void)42899a2dd95SBruce Richardson rte_dev_hotplug_handle_enable(void)
42999a2dd95SBruce Richardson {
43099a2dd95SBruce Richardson 	int ret = 0;
43199a2dd95SBruce Richardson 
43299a2dd95SBruce Richardson 	ret = dev_sigbus_handler_register();
43399a2dd95SBruce Richardson 	if (ret < 0)
434ae67895bSDavid Marchand 		EAL_LOG(ERR,
435ae67895bSDavid Marchand 			"fail to register sigbus handler for devices.");
43699a2dd95SBruce Richardson 
43799a2dd95SBruce Richardson 	hotplug_handle = true;
43899a2dd95SBruce Richardson 
43999a2dd95SBruce Richardson 	return ret;
44099a2dd95SBruce Richardson }
44199a2dd95SBruce Richardson 
44299a2dd95SBruce Richardson int
rte_dev_hotplug_handle_disable(void)44399a2dd95SBruce Richardson rte_dev_hotplug_handle_disable(void)
44499a2dd95SBruce Richardson {
44599a2dd95SBruce Richardson 	int ret = 0;
44699a2dd95SBruce Richardson 
44799a2dd95SBruce Richardson 	ret = dev_sigbus_handler_unregister();
44899a2dd95SBruce Richardson 	if (ret < 0)
449ae67895bSDavid Marchand 		EAL_LOG(ERR,
450ae67895bSDavid Marchand 			"fail to unregister sigbus handler for devices.");
45199a2dd95SBruce Richardson 
45299a2dd95SBruce Richardson 	hotplug_handle = false;
45399a2dd95SBruce Richardson 
45499a2dd95SBruce Richardson 	return ret;
45599a2dd95SBruce Richardson }
456