xref: /spdk/lib/env_dpdk/pci_event.c (revision df902b1d2e0abbbdeb84c0972bad34d250227e26)
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 = getpid();
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 		return -rc;
70 	}
71 
72 	flag = fcntl(netlink_fd, F_GETFL);
73 	if (flag < 0) {
74 		rc = errno;
75 		SPDK_ERRLOG("Failed to get socket flag, fd: %d\n", netlink_fd);
76 		close(netlink_fd);
77 		return -rc;
78 	}
79 
80 	if (fcntl(netlink_fd, F_SETFL, flag | O_NONBLOCK) < 0) {
81 		rc = errno;
82 		SPDK_ERRLOG("Fcntl can't set nonblocking mode for socket, fd: %d\n", netlink_fd);
83 		close(netlink_fd);
84 		return -rc;
85 	}
86 
87 	if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
88 		rc = errno;
89 		SPDK_ERRLOG("Failed to bind the netlink\n");
90 		close(netlink_fd);
91 		return -rc;
92 	}
93 
94 	return netlink_fd;
95 }
96 
97 /* Note: We parse the event from uio and vfio subsystem and will ignore
98  *       all the event from other subsystem. the event from uio subsystem
99  *       as below:
100  *       action: "add" or "remove"
101  *       subsystem: "uio"
102  *       dev_path: "/devices/pci0000:80/0000:80:01.0/0000:81:00.0/uio/uio0"
103  *       VFIO subsystem add event:
104  *       ACTION=bind
105  *       DRIVER=vfio-pci
106  *       PCI_SLOT_NAME=0000:d8:00.0
107  */
108 static int
109 parse_subsystem_event(const char *buf, struct spdk_pci_event *event)
110 {
111 	char subsystem[SPDK_UEVENT_MSG_LEN];
112 	char action[SPDK_UEVENT_MSG_LEN];
113 	char dev_path[SPDK_UEVENT_MSG_LEN];
114 	char driver[SPDK_UEVENT_MSG_LEN];
115 	char vfio_pci_addr[SPDK_UEVENT_MSG_LEN];
116 	char *pci_address, *tmp;
117 	int rc;
118 
119 	memset(subsystem, 0, SPDK_UEVENT_MSG_LEN);
120 	memset(action, 0, SPDK_UEVENT_MSG_LEN);
121 	memset(dev_path, 0, SPDK_UEVENT_MSG_LEN);
122 	memset(driver, 0, SPDK_UEVENT_MSG_LEN);
123 	memset(vfio_pci_addr, 0, SPDK_UEVENT_MSG_LEN);
124 
125 	while (*buf) {
126 		if (!strncmp(buf, "SUBSYSTEM=", 10)) {
127 			buf += 10;
128 			snprintf(subsystem, sizeof(subsystem), "%s", buf);
129 		} else if (!strncmp(buf, "ACTION=", 7)) {
130 			buf += 7;
131 			snprintf(action, sizeof(action), "%s", buf);
132 		} else if (!strncmp(buf, "DEVPATH=", 8)) {
133 			buf += 8;
134 			snprintf(dev_path, sizeof(dev_path), "%s", buf);
135 		} else if (!strncmp(buf, "DRIVER=", 7)) {
136 			buf += 7;
137 			snprintf(driver, sizeof(driver), "%s", buf);
138 		} else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
139 			buf += 14;
140 			snprintf(vfio_pci_addr, sizeof(vfio_pci_addr), "%s", buf);
141 		}
142 
143 		while (*buf++)
144 			;
145 	}
146 
147 	if (!strncmp(subsystem, "uio", 3)) {
148 		if (!strncmp(action, "remove", 6)) {
149 			event->action = SPDK_UEVENT_REMOVE;
150 		} else if (!strncmp(action, "add", 3)) {
151 			/* Support the ADD UEVENT for the device allow */
152 			event->action = SPDK_UEVENT_ADD;
153 		} else {
154 			return 0;
155 		}
156 
157 		tmp = strstr(dev_path, "/uio/");
158 		if (!tmp) {
159 			SPDK_ERRLOG("Invalid format of uevent: %s\n", dev_path);
160 			return -EBADMSG;
161 		}
162 		memset(tmp, 0, SPDK_UEVENT_MSG_LEN - (tmp - dev_path));
163 
164 		pci_address = strrchr(dev_path, '/');
165 		if (!pci_address) {
166 			SPDK_ERRLOG("Not found PCI device BDF in uevent: %s\n", dev_path);
167 			return -EBADMSG;
168 		}
169 		pci_address++;
170 
171 		rc = spdk_pci_addr_parse(&event->traddr, pci_address);
172 		if (rc != 0) {
173 			SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", pci_address);
174 			return rc;
175 		}
176 
177 		return 1;
178 	}
179 
180 	if (!strncmp(driver, "vfio-pci", 8)) {
181 		if (!strncmp(action, "bind", 4)) {
182 			/* Support the ADD UEVENT for the device allow */
183 			event->action = SPDK_UEVENT_ADD;
184 		} else {
185 			/* Only need to support add event.
186 			 * VFIO hotplug interface is "pci.c:pci_device_rte_dev_event".
187 			 * VFIO informs the userspace hotplug through vfio req notifier interrupt.
188 			 * The app needs to free the device userspace driver resource first then
189 			 * the OS remove the device VFIO driver and boardcast the VFIO uevent.
190 			 */
191 			return 0;
192 		}
193 
194 		rc = spdk_pci_addr_parse(&event->traddr, vfio_pci_addr);
195 		if (rc != 0) {
196 			SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", vfio_pci_addr);
197 			return rc;
198 		}
199 
200 		return 1;
201 	}
202 
203 	return 0;
204 }
205 
206 int
207 spdk_pci_get_event(int fd, struct spdk_pci_event *event)
208 {
209 	int ret;
210 	char buf[SPDK_UEVENT_MSG_LEN];
211 
212 	memset(buf, 0, SPDK_UEVENT_MSG_LEN);
213 	memset(event, 0, sizeof(*event));
214 
215 	ret = recv(fd, buf, SPDK_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
216 	if (ret > 0) {
217 		return parse_subsystem_event(buf, event);
218 	} else if (ret < 0) {
219 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
220 			return 0;
221 		} else {
222 			ret = errno;
223 			SPDK_ERRLOG("Socket read error %d\n", errno);
224 			return -ret;
225 		}
226 	} else {
227 		/* connection closed */
228 		return -ENOTCONN;
229 	}
230 
231 	return 0;
232 }
233 
234 #else /* Not Linux */
235 
236 int
237 spdk_pci_event_listen(void)
238 {
239 	SPDK_ERRLOG("Non-Linux does not support this operation\n");
240 	return -ENOTSUP;
241 }
242 
243 int
244 spdk_pci_get_event(int fd, struct spdk_pci_event *event)
245 {
246 	SPDK_ERRLOG("Non-Linux does not support this operation\n");
247 	return -ENOTSUP;
248 }
249 #endif
250