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