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: 0 6 0.0 %
Date: 2024-12-08 14:54:25 Functions: 0 2 0.0 %

          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             : spdk_pci_event_listen(void)
      21             : {
      22             :         struct sockaddr_nl addr;
      23             :         int netlink_fd;
      24             :         int size = SPDK_UEVENT_RECVBUF_SIZE;
      25             :         int buf_size;
      26             :         socklen_t opt_size;
      27             :         int flag, rc;
      28             : 
      29             :         memset(&addr, 0, sizeof(addr));
      30             :         addr.nl_family = AF_NETLINK;
      31             :         addr.nl_pid = 0;
      32             :         addr.nl_groups = 0xffffffff;
      33             : 
      34             :         netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
      35             :         if (netlink_fd < 0) {
      36             :                 SPDK_ERRLOG("Failed to create netlink socket\n");
      37             :                 return netlink_fd;
      38             :         }
      39             : 
      40             :         if (setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)) < 0) {
      41             :                 if (setsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) {
      42             :                         rc = errno;
      43             :                         SPDK_ERRLOG("Failed to set socket option SO_RCVBUF\n");
      44             :                         goto error;
      45             :                 }
      46             :                 opt_size = sizeof(buf_size);
      47             :                 if (getsockopt(netlink_fd, SOL_SOCKET, SO_RCVBUF, &buf_size, &opt_size) < 0) {
      48             :                         rc = errno;
      49             :                         SPDK_ERRLOG("Failed to get socket option SO_RCVBUF\n");
      50             :                         goto error;
      51             :                 }
      52             :                 if (buf_size < SPDK_UEVENT_RECVBUF_SIZE) {
      53             :                         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             :                         rc = ENOSPC;
      57             :                         goto error;
      58             :                 }
      59             :         }
      60             : 
      61             :         flag = fcntl(netlink_fd, F_GETFL);
      62             :         if (flag < 0) {
      63             :                 rc = errno;
      64             :                 SPDK_ERRLOG("Failed to get socket flag, fd: %d\n", netlink_fd);
      65             :                 goto error;
      66             :         }
      67             : 
      68             :         if (fcntl(netlink_fd, F_SETFL, flag | O_NONBLOCK) < 0) {
      69             :                 rc = errno;
      70             :                 SPDK_ERRLOG("Fcntl can't set nonblocking mode for socket, fd: %d\n", netlink_fd);
      71             :                 goto error;
      72             :         }
      73             : 
      74             :         if (bind(netlink_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
      75             :                 rc = errno;
      76             :                 SPDK_ERRLOG("Failed to bind the netlink\n");
      77             :                 goto error;
      78             :         }
      79             : 
      80             :         return netlink_fd;
      81             : error:
      82             :         close(netlink_fd);
      83             :         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             : parse_subsystem_event(const char *buf, struct spdk_pci_event *event)
      99             : {
     100             :         char subsystem[SPDK_UEVENT_MSG_LEN];
     101             :         char action[SPDK_UEVENT_MSG_LEN];
     102             :         char dev_path[SPDK_UEVENT_MSG_LEN];
     103             :         char driver[SPDK_UEVENT_MSG_LEN];
     104             :         char vfio_pci_addr[SPDK_UEVENT_MSG_LEN];
     105             :         char *pci_address, *tmp;
     106             :         int rc;
     107             : 
     108             :         memset(subsystem, 0, SPDK_UEVENT_MSG_LEN);
     109             :         memset(action, 0, SPDK_UEVENT_MSG_LEN);
     110             :         memset(dev_path, 0, SPDK_UEVENT_MSG_LEN);
     111             :         memset(driver, 0, SPDK_UEVENT_MSG_LEN);
     112             :         memset(vfio_pci_addr, 0, SPDK_UEVENT_MSG_LEN);
     113             : 
     114             :         while (*buf) {
     115             :                 if (!strncmp(buf, "SUBSYSTEM=", 10)) {
     116             :                         buf += 10;
     117             :                         snprintf(subsystem, sizeof(subsystem), "%s", buf);
     118             :                 } else if (!strncmp(buf, "ACTION=", 7)) {
     119             :                         buf += 7;
     120             :                         snprintf(action, sizeof(action), "%s", buf);
     121             :                 } else if (!strncmp(buf, "DEVPATH=", 8)) {
     122             :                         buf += 8;
     123             :                         snprintf(dev_path, sizeof(dev_path), "%s", buf);
     124             :                 } else if (!strncmp(buf, "DRIVER=", 7)) {
     125             :                         buf += 7;
     126             :                         snprintf(driver, sizeof(driver), "%s", buf);
     127             :                 } else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
     128             :                         buf += 14;
     129             :                         snprintf(vfio_pci_addr, sizeof(vfio_pci_addr), "%s", buf);
     130             :                 }
     131             : 
     132             :                 while (*buf++)
     133             :                         ;
     134             :         }
     135             : 
     136             :         if (!strncmp(subsystem, "uio", 3)) {
     137             :                 if (!strncmp(action, "remove", 6)) {
     138             :                         event->action = SPDK_UEVENT_REMOVE;
     139             :                 } else if (!strncmp(action, "add", 3)) {
     140             :                         /* Support the ADD UEVENT for the device allow */
     141             :                         event->action = SPDK_UEVENT_ADD;
     142             :                 } else {
     143             :                         return 0;
     144             :                 }
     145             : 
     146             :                 tmp = strstr(dev_path, "/uio/");
     147             :                 if (!tmp) {
     148             :                         SPDK_ERRLOG("Invalid format of uevent: %s\n", dev_path);
     149             :                         return -EBADMSG;
     150             :                 }
     151             :                 memset(tmp, 0, SPDK_UEVENT_MSG_LEN - (tmp - dev_path));
     152             : 
     153             :                 pci_address = strrchr(dev_path, '/');
     154             :                 if (!pci_address) {
     155             :                         SPDK_ERRLOG("Not found PCI device BDF in uevent: %s\n", dev_path);
     156             :                         return -EBADMSG;
     157             :                 }
     158             :                 pci_address++;
     159             : 
     160             :                 rc = spdk_pci_addr_parse(&event->traddr, pci_address);
     161             :                 if (rc != 0) {
     162             :                         SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", pci_address);
     163             :                         return rc;
     164             :                 }
     165             : 
     166             :                 return 1;
     167             :         }
     168             : 
     169             :         if (!strncmp(driver, "vfio-pci", 8)) {
     170             :                 if (!strncmp(action, "bind", 4)) {
     171             :                         /* Support the ADD UEVENT for the device allow */
     172             :                         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             :                         return 0;
     181             :                 }
     182             : 
     183             :                 rc = spdk_pci_addr_parse(&event->traddr, vfio_pci_addr);
     184             :                 if (rc != 0) {
     185             :                         SPDK_ERRLOG("Invalid format for PCI device BDF: %s\n", vfio_pci_addr);
     186             :                         return rc;
     187             :                 }
     188             : 
     189             :                 return 1;
     190             :         }
     191             : 
     192             :         return 0;
     193             : }
     194             : 
     195             : int
     196             : spdk_pci_get_event(int fd, struct spdk_pci_event *event)
     197             : {
     198             :         int ret;
     199             :         char buf[SPDK_UEVENT_MSG_LEN];
     200             : 
     201             :         memset(buf, 0, SPDK_UEVENT_MSG_LEN);
     202             :         memset(event, 0, sizeof(*event));
     203             : 
     204             :         ret = recv(fd, buf, SPDK_UEVENT_MSG_LEN - 1, MSG_DONTWAIT);
     205             :         if (ret > 0) {
     206             :                 return parse_subsystem_event(buf, event);
     207             :         } else if (ret < 0) {
     208             :                 if (errno == EAGAIN || errno == EWOULDBLOCK) {
     209             :                         return 0;
     210             :                 } else {
     211             :                         ret = errno;
     212             :                         SPDK_ERRLOG("Socket read error %d\n", errno);
     213             :                         return -ret;
     214             :                 }
     215             :         } else {
     216             :                 /* connection closed */
     217             :                 return -ENOTCONN;
     218             :         }
     219             : 
     220             :         return 0;
     221             : }
     222             : 
     223             : #else /* Not Linux */
     224             : 
     225             : int
     226           0 : spdk_pci_event_listen(void)
     227             : {
     228           0 :         SPDK_ERRLOG("Non-Linux does not support this operation\n");
     229           0 :         return -ENOTSUP;
     230             : }
     231             : 
     232             : int
     233           0 : spdk_pci_get_event(int fd, struct spdk_pci_event *event)
     234             : {
     235           0 :         SPDK_ERRLOG("Non-Linux does not support this operation\n");
     236           0 :         return -ENOTSUP;
     237             : }
     238             : #endif

Generated by: LCOV version 1.15