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