9478a39a536ec56af3e75050cda79917b83a1d5c
[dpdk.git] / lib / librte_eal / linuxapp / eal / eal_dev.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <string.h>
6 #include <unistd.h>
7 #include <sys/socket.h>
8 #include <linux/netlink.h>
9
10 #include <rte_log.h>
11 #include <rte_compat.h>
12 #include <rte_dev.h>
13 #include <rte_malloc.h>
14 #include <rte_interrupts.h>
15 #include <rte_alarm.h>
16
17 #include "eal_private.h"
18
19 static struct rte_intr_handle intr_handle = {.fd = -1 };
20 static bool monitor_started;
21
22 #define EAL_UEV_MSG_LEN 4096
23 #define EAL_UEV_MSG_ELEM_LEN 128
24
25 static void dev_uev_handler(__rte_unused void *param);
26
27 /* identify the system layer which reports this event. */
28 enum eal_dev_event_subsystem {
29         EAL_DEV_EVENT_SUBSYSTEM_PCI, /* PCI bus device event */
30         EAL_DEV_EVENT_SUBSYSTEM_UIO, /* UIO driver device event */
31         EAL_DEV_EVENT_SUBSYSTEM_VFIO, /* VFIO driver device event */
32         EAL_DEV_EVENT_SUBSYSTEM_MAX
33 };
34
35 static int
36 dev_uev_socket_fd_create(void)
37 {
38         struct sockaddr_nl addr;
39         int ret;
40
41         intr_handle.fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC |
42                         SOCK_NONBLOCK,
43                         NETLINK_KOBJECT_UEVENT);
44         if (intr_handle.fd < 0) {
45                 RTE_LOG(ERR, EAL, "create uevent fd failed.\n");
46                 return -1;
47         }
48
49         memset(&addr, 0, sizeof(addr));
50         addr.nl_family = AF_NETLINK;
51         addr.nl_pid = 0;
52         addr.nl_groups = 0xffffffff;
53
54         ret = bind(intr_handle.fd, (struct sockaddr *) &addr, sizeof(addr));
55         if (ret < 0) {
56                 RTE_LOG(ERR, EAL, "Failed to bind uevent socket.\n");
57                 goto err;
58         }
59
60         return 0;
61 err:
62         close(intr_handle.fd);
63         intr_handle.fd = -1;
64         return ret;
65 }
66
67 static int
68 dev_uev_parse(const char *buf, struct rte_dev_event *event, int length)
69 {
70         char action[EAL_UEV_MSG_ELEM_LEN];
71         char subsystem[EAL_UEV_MSG_ELEM_LEN];
72         char pci_slot_name[EAL_UEV_MSG_ELEM_LEN];
73         int i = 0;
74
75         memset(action, 0, EAL_UEV_MSG_ELEM_LEN);
76         memset(subsystem, 0, EAL_UEV_MSG_ELEM_LEN);
77         memset(pci_slot_name, 0, EAL_UEV_MSG_ELEM_LEN);
78
79         while (i < length) {
80                 for (; i < length; i++) {
81                         if (*buf)
82                                 break;
83                         buf++;
84                 }
85                 /**
86                  * check device uevent from kernel side, no need to check
87                  * uevent from udev.
88                  */
89                 if (!strncmp(buf, "libudev", 7)) {
90                         buf += 7;
91                         i += 7;
92                         return -1;
93                 }
94                 if (!strncmp(buf, "ACTION=", 7)) {
95                         buf += 7;
96                         i += 7;
97                         snprintf(action, sizeof(action), "%s", buf);
98                 } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
99                         buf += 10;
100                         i += 10;
101                         snprintf(subsystem, sizeof(subsystem), "%s", buf);
102                 } else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
103                         buf += 14;
104                         i += 14;
105                         snprintf(pci_slot_name, sizeof(subsystem), "%s", buf);
106                         event->devname = strdup(pci_slot_name);
107                 }
108                 for (; i < length; i++) {
109                         if (*buf == '\0')
110                                 break;
111                         buf++;
112                 }
113         }
114
115         /* parse the subsystem layer */
116         if (!strncmp(subsystem, "uio", 3))
117                 event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_UIO;
118         else if (!strncmp(subsystem, "pci", 3))
119                 event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_PCI;
120         else if (!strncmp(subsystem, "vfio", 4))
121                 event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_VFIO;
122         else
123                 return -1;
124
125         /* parse the action type */
126         if (!strncmp(action, "add", 3))
127                 event->type = RTE_DEV_EVENT_ADD;
128         else if (!strncmp(action, "remove", 6))
129                 event->type = RTE_DEV_EVENT_REMOVE;
130         else
131                 return -1;
132         return 0;
133 }
134
135 static void
136 dev_delayed_unregister(void *param)
137 {
138         rte_intr_callback_unregister(&intr_handle, dev_uev_handler, param);
139         close(intr_handle.fd);
140         intr_handle.fd = -1;
141 }
142
143 static void
144 dev_uev_handler(__rte_unused void *param)
145 {
146         struct rte_dev_event uevent;
147         int ret;
148         char buf[EAL_UEV_MSG_LEN];
149
150         memset(&uevent, 0, sizeof(struct rte_dev_event));
151         memset(buf, 0, EAL_UEV_MSG_LEN);
152
153         ret = recv(intr_handle.fd, buf, EAL_UEV_MSG_LEN, MSG_DONTWAIT);
154         if (ret < 0 && errno == EAGAIN)
155                 return;
156         else if (ret <= 0) {
157                 /* connection is closed or broken, can not up again. */
158                 RTE_LOG(ERR, EAL, "uevent socket connection is broken.\n");
159                 rte_eal_alarm_set(1, dev_delayed_unregister, NULL);
160                 return;
161         }
162
163         ret = dev_uev_parse(buf, &uevent, EAL_UEV_MSG_LEN);
164         if (ret < 0) {
165                 RTE_LOG(DEBUG, EAL, "It is not an valid event "
166                         "that need to be handle.\n");
167                 return;
168         }
169
170         RTE_LOG(DEBUG, EAL, "receive uevent(name:%s, type:%d, subsystem:%d)\n",
171                 uevent.devname, uevent.type, uevent.subsystem);
172
173         if (uevent.devname)
174                 dev_callback_process(uevent.devname, uevent.type);
175 }
176
177 int __rte_experimental
178 rte_dev_event_monitor_start(void)
179 {
180         int ret;
181
182         if (monitor_started)
183                 return 0;
184
185         ret = dev_uev_socket_fd_create();
186         if (ret) {
187                 RTE_LOG(ERR, EAL, "error create device event fd.\n");
188                 return -1;
189         }
190
191         intr_handle.type = RTE_INTR_HANDLE_DEV_EVENT;
192         ret = rte_intr_callback_register(&intr_handle, dev_uev_handler, NULL);
193
194         if (ret) {
195                 RTE_LOG(ERR, EAL, "fail to register uevent callback.\n");
196                 return -1;
197         }
198
199         monitor_started = true;
200
201         return 0;
202 }
203
204 int __rte_experimental
205 rte_dev_event_monitor_stop(void)
206 {
207         int ret;
208
209         if (!monitor_started)
210                 return 0;
211
212         ret = rte_intr_callback_unregister(&intr_handle, dev_uev_handler,
213                                            (void *)-1);
214         if (ret < 0) {
215                 RTE_LOG(ERR, EAL, "fail to unregister uevent callback.\n");
216                 return ret;
217         }
218
219         close(intr_handle.fd);
220         intr_handle.fd = -1;
221         monitor_started = false;
222         return 0;
223 }