xref: /spdk/lib/env_dpdk/pci_event.c (revision 2f5c602574a98ede645991abe279a96e19c50196)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 #include "spdk/string.h"
36 
37 #include "spdk/log.h"
38 #include "spdk/env.h"
39 
40 #ifdef __linux__
41 
42 #include <linux/netlink.h>
43 
44 #define SPDK_UEVENT_MSG_LEN 4096
45 #define SPDK_UEVENT_RECVBUF_SIZE 1024 * 1024
46 
47 int
48 spdk_pci_event_listen(void)
49 {
50 	struct sockaddr_nl addr;
51 	int netlink_fd;
52 	int size = SPDK_UEVENT_RECVBUF_SIZE;
53 	int flag, rc;
54 
55 	memset(&addr, 0, sizeof(addr));
56 	addr.nl_family = AF_NETLINK;
57 	addr.nl_pid = 0;
58 	addr.nl_groups = 0xffffffff;
59 
60 	netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
61 	if (netlink_fd < 0) {
62 		SPDK_ERRLOG("Failed to create netlink socket\n");
63 		return netlink_fd;
64 	}
65 
66 	if (setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)) < 0) {
67 		rc = errno;
68 		SPDK_ERRLOG("Failed to set socket option\n");
69 		close(netlink_fd);
70 		return -rc;
71 	}
72 
73 	flag = fcntl(netlink_fd, F_GETFL);
74 	if (flag < 0) {
75 		rc = errno;
76 		SPDK_ERRLOG("Failed to get socket flag, fd: %d\n", netlink_fd);
77 		close(netlink_fd);
78 		return -rc;
79 	}
80 
81 	if (fcntl(netlink_fd, F_SETFL, flag | O_NONBLOCK) < 0) {
82 		rc = errno;
83 		SPDK_ERRLOG("Fcntl can't set nonblocking mode for socket, fd: %d\n", netlink_fd);
84 		close(netlink_fd);
85 		return -rc;
86 	}
87 
88 	if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
89 		rc = errno;
90 		SPDK_ERRLOG("Failed to bind the netlink\n");
91 		close(netlink_fd);
92 		return -rc;
93 	}
94 
95 	return netlink_fd;
96 }
97 
98 /* Note: We parse the event from uio and vfio subsystem and will ignore
99  *       all the event from other subsystem. the event from uio subsystem
100  *       as below:
101  *       action: "add" or "remove"
102  *       subsystem: "uio"
103  *       dev_path: "/devices/pci0000:80/0000:80:01.0/0000:81:00.0/uio/uio0"
104  *       VFIO subsystem add event:
105  *       ACTION=bind
106  *       DRIVER=vfio-pci
107  *       PCI_SLOT_NAME=0000:d8:00.0
108  */
109 static int
110 parse_subsystem_event(const char *buf, struct spdk_pci_event *event)
111 {
112 	char subsystem[SPDK_UEVENT_MSG_LEN];
113 	char action[SPDK_UEVENT_MSG_LEN];
114 	char dev_path[SPDK_UEVENT_MSG_LEN];
115 	char driver[SPDK_UEVENT_MSG_LEN];
116 	char vfio_pci_addr[SPDK_UEVENT_MSG_LEN];
117 	char *pci_address, *tmp;
118 	int rc;
119 
120 	memset(subsystem, 0, SPDK_UEVENT_MSG_LEN);
121 	memset(action, 0, SPDK_UEVENT_MSG_LEN);
122 	memset(dev_path, 0, SPDK_UEVENT_MSG_LEN);
123 	memset(driver, 0, SPDK_UEVENT_MSG_LEN);
124 	memset(vfio_pci_addr, 0, SPDK_UEVENT_MSG_LEN);
125 
126 	while (*buf) {
127 		if (!strncmp(buf, "SUBSYSTEM=", 10)) {
128 			buf += 10;
129 			snprintf(subsystem, sizeof(subsystem), "%s", buf);
130 		} else if (!strncmp(buf, "ACTION=", 7)) {
131 			buf += 7;
132 			snprintf(action, sizeof(action), "%s", buf);
133 		} else if (!strncmp(buf, "DEVPATH=", 8)) {
134 			buf += 8;
135 			snprintf(dev_path, sizeof(dev_path), "%s", buf);
136 		} else if (!strncmp(buf, "DRIVER=", 7)) {
137 			buf += 7;
138 			snprintf(driver, sizeof(driver), "%s", buf);
139 		} else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
140 			buf += 14;
141 			snprintf(vfio_pci_addr, sizeof(vfio_pci_addr), "%s", buf);
142 		}
143 
144 		while (*buf++)
145 			;
146 	}
147 
148 	if (!strncmp(subsystem, "uio", 3)) {
149 		if (!strncmp(action, "remove", 6)) {
150 			event->action = SPDK_UEVENT_REMOVE;
151 		} else if (!strncmp(action, "add", 3)) {
152 			/* Support the ADD UEVENT for the device allow */
153 			event->action = SPDK_UEVENT_ADD;
154 		} else {
155 			return 0;
156 		}
157 
158 		tmp = strstr(dev_path, "/uio/");
159 		if (!tmp) {
160 			SPDK_ERRLOG("Invalid format of uevent: %s\n", dev_path);
161 			return -EBADMSG;
162 		}
163 		memset(tmp, 0, SPDK_UEVENT_MSG_LEN - (tmp - dev_path));
164 
165 		pci_address = strrchr(dev_path, '/');
166 		if (!pci_address) {
167 			SPDK_ERRLOG("Not found PCI device BDF in uevent: %s\n", dev_path);
168 			return -EBADMSG;
169 		}
170 		pci_address++;
171 
172 		rc = spdk_pci_addr_parse(&event->traddr, pci_address);
173 		if (rc != 0) {
174 			SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", pci_address);
175 			return rc;
176 		}
177 
178 		return 1;
179 	}
180 
181 	if (!strncmp(driver, "vfio-pci", 8)) {
182 		if (!strncmp(action, "bind", 4)) {
183 			/* Support the ADD UEVENT for the device allow */
184 			event->action = SPDK_UEVENT_ADD;
185 		} else {
186 			/* Only need to support add event.
187 			 * VFIO hotplug interface is "pci.c:pci_device_rte_dev_event".
188 			 * VFIO informs the userspace hotplug through vfio req notifier interrupt.
189 			 * The app needs to free the device userspace driver resource first then
190 			 * the OS remove the device VFIO driver and boardcast the VFIO uevent.
191 			 */
192 			return 0;
193 		}
194 
195 		rc = spdk_pci_addr_parse(&event->traddr, vfio_pci_addr);
196 		if (rc != 0) {
197 			SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", vfio_pci_addr);
198 			return rc;
199 		}
200 
201 		return 1;
202 	}
203 
204 	return 0;
205 }
206 
207 int
208 spdk_pci_get_event(int fd, struct spdk_pci_event *event)
209 {
210 	int ret;
211 	char buf[SPDK_UEVENT_MSG_LEN];
212 
213 	memset(buf, 0, SPDK_UEVENT_MSG_LEN);
214 	memset(event, 0, sizeof(*event));
215 
216 	ret = recv(fd, buf, SPDK_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
217 	if (ret > 0) {
218 		return parse_subsystem_event(buf, event);
219 	} else if (ret < 0) {
220 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
221 			return 0;
222 		} else {
223 			ret = errno;
224 			SPDK_ERRLOG("Socket read error %d\n", errno);
225 			return -ret;
226 		}
227 	} else {
228 		/* connection closed */
229 		return -ENOTCONN;
230 	}
231 
232 	return 0;
233 }
234 
235 #else /* Not Linux */
236 
237 int
238 spdk_pci_event_listen(void)
239 {
240 	SPDK_ERRLOG("Non-Linux does not support this operation\n");
241 	return -ENOTSUP;
242 }
243 
244 int
245 spdk_pci_get_event(int fd, struct spdk_pci_event *event)
246 {
247 	SPDK_ERRLOG("Non-Linux does not support this operation\n");
248 	return -ENOTSUP;
249 }
250 #endif
251