LCOV - code coverage report
Current view: top level - lib/env_dpdk - pci_event.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 54 117 46.2 %
Date: 2024-12-08 02:23:54 Functions: 1 3 33.3 %

          Line data    Source code
       1             : /*   SPDX-License-Identifier: BSD-3-Clause
       2             :  *   Copyright (C) 2020 Intel Corporation.
       3             :  *   All rights reserved.
       4             :  */
       5             : 
       6             : #include "spdk/stdinc.h"
       7             : #include "spdk/string.h"
       8             : 
       9             : #include "spdk/log.h"
      10             : #include "spdk/env.h"
      11             : 
      12             : #ifdef __linux__
      13             : 
      14             : #include <linux/netlink.h>
      15             : 
      16             : #define SPDK_UEVENT_MSG_LEN 4096
      17             : #define SPDK_UEVENT_RECVBUF_SIZE 1024 * 1024
      18             : 
      19             : int
      20           0 : spdk_pci_event_listen(void)
      21             : {
      22           0 :         struct sockaddr_nl addr;
      23             :         int netlink_fd;
      24           0 :         int size = SPDK_UEVENT_RECVBUF_SIZE;
      25           0 :         int buf_size;
      26           0 :         socklen_t opt_size;
      27             :         int flag, rc;
      28             : 
      29           0 :         memset(&addr, 0, sizeof(addr));
      30           0 :         addr.nl_family = AF_NETLINK;
      31           0 :         addr.nl_pid = 0;
      32           0 :         addr.nl_groups = 0xffffffff;
      33             : 
      34           0 :         netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
      35           0 :         if (netlink_fd < 0) {
      36           0 :                 SPDK_ERRLOG("Failed to create netlink socket\n");
      37           0 :                 return netlink_fd;
      38             :         }
      39             : 
      40           0 :         if (setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)) < 0) {
      41           0 :                 if (setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) {
      42           0 :                         rc = errno;
      43           0 :                         SPDK_ERRLOG("Failed to set socket option SO_RCVBUF\n");
      44           0 :                         goto error;
      45             :                 }
      46           0 :                 opt_size = sizeof(buf_size);
      47           0 :                 if (getsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUF, &buf_size, &opt_size) < 0) {
      48           0 :                         rc = errno;
      49           0 :                         SPDK_ERRLOG("Failed to get socket option SO_RCVBUF\n");
      50           0 :                         goto error;
      51             :                 }
      52           0 :                 if (buf_size < SPDK_UEVENT_RECVBUF_SIZE) {
      53           0 :                         SPDK_ERRLOG("Socket recv buffer is too small (< %d), see SO_RCVBUF "
      54             :                                     "section in socket(7) man page for specifics on how to "
      55             :                                     "adjust the system setting.", SPDK_UEVENT_RECVBUF_SIZE);
      56           0 :                         rc = ENOSPC;
      57           0 :                         goto error;
      58             :                 }
      59             :         }
      60             : 
      61           0 :         flag = fcntl(netlink_fd, F_GETFL);
      62           0 :         if (flag < 0) {
      63           0 :                 rc = errno;
      64           0 :                 SPDK_ERRLOG("Failed to get socket flag, fd: %d\n", netlink_fd);
      65           0 :                 goto error;
      66             :         }
      67             : 
      68           0 :         if (fcntl(netlink_fd, F_SETFL, flag | O_NONBLOCK) < 0) {
      69           0 :                 rc = errno;
      70           0 :                 SPDK_ERRLOG("Fcntl can't set nonblocking mode for socket, fd: %d\n", netlink_fd);
      71           0 :                 goto error;
      72             :         }
      73             : 
      74           0 :         if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
      75           0 :                 rc = errno;
      76           0 :                 SPDK_ERRLOG("Failed to bind the netlink\n");
      77           0 :                 goto error;
      78             :         }
      79             : 
      80           0 :         return netlink_fd;
      81           0 : error:
      82           0 :         close(netlink_fd);
      83           0 :         return -rc;
      84             : }
      85             : 
      86             : /* Note: We parse the event from uio and vfio subsystem and will ignore
      87             :  *       all the event from other subsystem. the event from uio subsystem
      88             :  *       as below:
      89             :  *       action: "add" or "remove"
      90             :  *       subsystem: "uio"
      91             :  *       dev_path: "/devices/pci0000:80/0000:80:01.0/0000:81:00.0/uio/uio0"
      92             :  *       VFIO subsystem add event:
      93             :  *       ACTION=bind
      94             :  *       DRIVER=vfio-pci
      95             :  *       PCI_SLOT_NAME=0000:d8:00.0
      96             :  */
      97             : static int
      98           9 : parse_subsystem_event(const char *buf, struct spdk_pci_event *event)
      99             : {
     100           9 :         char subsystem[SPDK_UEVENT_MSG_LEN];
     101           9 :         char action[SPDK_UEVENT_MSG_LEN];
     102           9 :         char dev_path[SPDK_UEVENT_MSG_LEN];
     103           9 :         char driver[SPDK_UEVENT_MSG_LEN];
     104           9 :         char vfio_pci_addr[SPDK_UEVENT_MSG_LEN];
     105             :         char *pci_address, *tmp;
     106             :         int rc;
     107             : 
     108           9 :         memset(subsystem, 0, SPDK_UEVENT_MSG_LEN);
     109           9 :         memset(action, 0, SPDK_UEVENT_MSG_LEN);
     110           9 :         memset(dev_path, 0, SPDK_UEVENT_MSG_LEN);
     111           9 :         memset(driver, 0, SPDK_UEVENT_MSG_LEN);
     112           9 :         memset(vfio_pci_addr, 0, SPDK_UEVENT_MSG_LEN);
     113             : 
     114          54 :         while (*buf) {
     115          45 :                 if (!strncmp(buf, "SUBSYSTEM=", 10)) {
     116           9 :                         buf += 10;
     117           9 :                         snprintf(subsystem, sizeof(subsystem), "%s", buf);
     118          36 :                 } else if (!strncmp(buf, "ACTION=", 7)) {
     119           9 :                         buf += 7;
     120           9 :                         snprintf(action, sizeof(action), "%s", buf);
     121          27 :                 } else if (!strncmp(buf, "DEVPATH=", 8)) {
     122           9 :                         buf += 8;
     123           9 :                         snprintf(dev_path, sizeof(dev_path), "%s", buf);
     124          18 :                 } else if (!strncmp(buf, "DRIVER=", 7)) {
     125           9 :                         buf += 7;
     126           9 :                         snprintf(driver, sizeof(driver), "%s", buf);
     127           9 :                 } else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
     128           9 :                         buf += 14;
     129           9 :                         snprintf(vfio_pci_addr, sizeof(vfio_pci_addr), "%s", buf);
     130             :                 }
     131             : 
     132         454 :                 while (*buf++)
     133             :                         ;
     134             :         }
     135             : 
     136           9 :         if (!strncmp(subsystem, "uio", 3)) {
     137           3 :                 if (!strncmp(action, "remove", 6)) {
     138           1 :                         event->action = SPDK_UEVENT_REMOVE;
     139           2 :                 } else if (!strncmp(action, "add", 3)) {
     140             :                         /* Support the ADD UEVENT for the device allow */
     141           2 :                         event->action = SPDK_UEVENT_ADD;
     142             :                 } else {
     143           0 :                         return 0;
     144             :                 }
     145             : 
     146           3 :                 tmp = strstr(dev_path, "/uio/");
     147           3 :                 if (!tmp) {
     148           0 :                         SPDK_ERRLOG("Invalid format of uevent: %s\n", dev_path);
     149           0 :                         return -EBADMSG;
     150             :                 }
     151           3 :                 memset(tmp, 0, SPDK_UEVENT_MSG_LEN - (tmp - dev_path));
     152             : 
     153           3 :                 pci_address = strrchr(dev_path, '/');
     154           3 :                 if (!pci_address) {
     155           0 :                         SPDK_ERRLOG("Not found PCI device BDF in uevent: %s\n", dev_path);
     156           0 :                         return -EBADMSG;
     157             :                 }
     158           3 :                 pci_address++;
     159             : 
     160           3 :                 rc = spdk_pci_addr_parse(&event->traddr, pci_address);
     161           3 :                 if (rc != 0) {
     162           1 :                         SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", pci_address);
     163           1 :                         return rc;
     164             :                 }
     165             : 
     166           2 :                 return 1;
     167             :         }
     168             : 
     169           6 :         if (!strncmp(driver, "vfio-pci", 8)) {
     170           3 :                 if (!strncmp(action, "bind", 4)) {
     171             :                         /* Support the ADD UEVENT for the device allow */
     172           2 :                         event->action = SPDK_UEVENT_ADD;
     173             :                 } else {
     174             :                         /* Only need to support add event.
     175             :                          * VFIO hotplug interface is "pci.c:pci_device_rte_dev_event".
     176             :                          * VFIO informs the userspace hotplug through vfio req notifier interrupt.
     177             :                          * The app needs to free the device userspace driver resource first then
     178             :                          * the OS remove the device VFIO driver and broadcast the VFIO uevent.
     179             :                          */
     180           1 :                         return 0;
     181             :                 }
     182             : 
     183           2 :                 rc = spdk_pci_addr_parse(&event->traddr, vfio_pci_addr);
     184           2 :                 if (rc != 0) {
     185           1 :                         SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", vfio_pci_addr);
     186           1 :                         return rc;
     187             :                 }
     188             : 
     189           1 :                 return 1;
     190             :         }
     191             : 
     192           3 :         return 0;
     193             : }
     194             : 
     195             : int
     196           0 : spdk_pci_get_event(int fd, struct spdk_pci_event *event)
     197             : {
     198             :         int ret;
     199           0 :         char buf[SPDK_UEVENT_MSG_LEN];
     200             : 
     201           0 :         memset(buf, 0, SPDK_UEVENT_MSG_LEN);
     202           0 :         memset(event, 0, sizeof(*event));
     203             : 
     204           0 :         ret = recv(fd, buf, SPDK_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
     205           0 :         if (ret > 0) {
     206           0 :                 return parse_subsystem_event(buf, event);
     207           0 :         } else if (ret < 0) {
     208           0 :                 if (errno == EAGAIN || errno == EWOULDBLOCK) {
     209           0 :                         return 0;
     210             :                 } else {
     211           0 :                         ret = errno;
     212           0 :                         SPDK_ERRLOG("Socket read error %d\n", errno);
     213           0 :                         return -ret;
     214             :                 }
     215             :         } else {
     216             :                 /* connection closed */
     217           0 :                 return -ENOTCONN;
     218             :         }
     219             : 
     220             :         return 0;
     221             : }
     222             : 
     223             : #else /* Not Linux */
     224             : 
     225             : int
     226             : spdk_pci_event_listen(void)
     227             : {
     228             :         SPDK_ERRLOG("Non-Linux does not support this operation\n");
     229             :         return -ENOTSUP;
     230             : }
     231             : 
     232             : int
     233             : spdk_pci_get_event(int fd, struct spdk_pci_event *event)
     234             : {
     235             :         SPDK_ERRLOG("Non-Linux does not support this operation\n");
     236             :         return -ENOTSUP;
     237             : }
     238             : #endif

Generated by: LCOV version 1.15