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