LCOV - code coverage report
Current view: top level - lib/eal/linux - eal_dev.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 0 185 0.0 %
Date: 2025-03-01 20:23:48 Functions: 0 12 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 104 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2018 Intel Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <stdlib.h>
       6                 :            : #include <string.h>
       7                 :            : #include <unistd.h>
       8                 :            : #include <signal.h>
       9                 :            : #include <sys/socket.h>
      10                 :            : #include <linux/netlink.h>
      11                 :            : 
      12                 :            : #include <rte_string_fns.h>
      13                 :            : #include <rte_log.h>
      14                 :            : #include <rte_dev.h>
      15                 :            : #include <rte_interrupts.h>
      16                 :            : #include <rte_alarm.h>
      17                 :            : #include <bus_driver.h>
      18                 :            : #include <rte_spinlock.h>
      19                 :            : #include <rte_errno.h>
      20                 :            : 
      21                 :            : #include "eal_private.h"
      22                 :            : 
      23                 :            : static struct rte_intr_handle *intr_handle;
      24                 :            : static rte_rwlock_t monitor_lock = RTE_RWLOCK_INITIALIZER;
      25                 :            : static uint32_t monitor_refcount;
      26                 :            : static bool hotplug_handle;
      27                 :            : 
      28                 :            : #define EAL_UEV_MSG_LEN 4096
      29                 :            : #define EAL_UEV_MSG_ELEM_LEN 128
      30                 :            : 
      31                 :            : /*
      32                 :            :  * spinlock for device hot-unplug failure handling. If it try to access bus or
      33                 :            :  * device, such as handle sigbus on bus or handle memory failure for device
      34                 :            :  * just need to use this lock. It could protect the bus and the device to avoid
      35                 :            :  * race condition.
      36                 :            :  */
      37                 :            : static rte_spinlock_t failure_handle_lock = RTE_SPINLOCK_INITIALIZER;
      38                 :            : 
      39                 :            : static struct sigaction sigbus_action_old;
      40                 :            : 
      41                 :            : static int sigbus_need_recover;
      42                 :            : 
      43                 :            : static void dev_uev_handler(__rte_unused void *param);
      44                 :            : 
      45                 :            : /* identify the system layer which reports this event. */
      46                 :            : enum eal_dev_event_subsystem {
      47                 :            :         EAL_DEV_EVENT_SUBSYSTEM_PCI, /* PCI bus device event */
      48                 :            :         EAL_DEV_EVENT_SUBSYSTEM_UIO, /* UIO driver device event */
      49                 :            :         EAL_DEV_EVENT_SUBSYSTEM_VFIO, /* VFIO driver device event */
      50                 :            :         EAL_DEV_EVENT_SUBSYSTEM_MAX
      51                 :            : };
      52                 :            : 
      53                 :            : static void
      54                 :            : sigbus_action_recover(void)
      55                 :            : {
      56                 :          0 :         if (sigbus_need_recover) {
      57                 :          0 :                 sigaction(SIGBUS, &sigbus_action_old, NULL);
      58                 :          0 :                 sigbus_need_recover = 0;
      59                 :            :         }
      60                 :            : }
      61                 :            : 
      62                 :          0 : static void sigbus_handler(int signum, siginfo_t *info,
      63                 :            :                                 void *ctx __rte_unused)
      64                 :            : {
      65                 :            :         int ret;
      66                 :            : 
      67                 :          0 :         EAL_LOG(DEBUG, "Thread catch SIGBUS, fault address:%p",
      68                 :            :                 info->si_addr);
      69                 :            : 
      70                 :            :         rte_spinlock_lock(&failure_handle_lock);
      71                 :          0 :         ret = rte_bus_sigbus_handler(info->si_addr);
      72                 :            :         rte_spinlock_unlock(&failure_handle_lock);
      73         [ #  # ]:          0 :         if (ret == -1) {
      74                 :          0 :                 rte_exit(EXIT_FAILURE,
      75                 :            :                          "Failed to handle SIGBUS for hot-unplug, "
      76                 :            :                          "(rte_errno: %s)!", strerror(rte_errno));
      77         [ #  # ]:          0 :         } else if (ret == 1) {
      78         [ #  # ]:          0 :                 if (sigbus_action_old.sa_flags == SA_SIGINFO
      79         [ #  # ]:          0 :                     && sigbus_action_old.sa_sigaction) {
      80                 :          0 :                         (*(sigbus_action_old.sa_sigaction))(signum,
      81                 :            :                                                             info, ctx);
      82         [ #  # ]:          0 :                 } else if (sigbus_action_old.sa_flags != SA_SIGINFO
      83         [ #  # ]:          0 :                            && sigbus_action_old.sa_handler) {
      84                 :          0 :                         (*(sigbus_action_old.sa_handler))(signum);
      85                 :            :                 } else {
      86                 :          0 :                         rte_exit(EXIT_FAILURE,
      87                 :            :                                  "Failed to handle generic SIGBUS!");
      88                 :            :                 }
      89                 :            :         }
      90                 :            : 
      91                 :          0 :         EAL_LOG(DEBUG, "Success to handle SIGBUS for hot-unplug!");
      92                 :          0 : }
      93                 :            : 
      94                 :          0 : static int cmp_dev_name(const struct rte_device *dev,
      95                 :            :         const void *_name)
      96                 :            : {
      97                 :            :         const char *name = _name;
      98                 :            : 
      99                 :          0 :         return strcmp(dev->name, name);
     100                 :            : }
     101                 :            : 
     102                 :            : static int
     103                 :          0 : dev_uev_socket_fd_create(void)
     104                 :            : {
     105                 :            :         struct sockaddr_nl addr;
     106                 :            :         int ret, fd;
     107                 :            : 
     108                 :          0 :         fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
     109                 :            :                     NETLINK_KOBJECT_UEVENT);
     110         [ #  # ]:          0 :         if (fd < 0) {
     111                 :          0 :                 EAL_LOG(ERR, "create uevent fd failed.");
     112                 :          0 :                 return -1;
     113                 :            :         }
     114                 :            : 
     115                 :            :         memset(&addr, 0, sizeof(addr));
     116                 :          0 :         addr.nl_family = AF_NETLINK;
     117                 :            :         addr.nl_pid = 0;
     118                 :          0 :         addr.nl_groups = 0xffffffff;
     119                 :            : 
     120                 :          0 :         ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
     121         [ #  # ]:          0 :         if (ret < 0) {
     122                 :          0 :                 EAL_LOG(ERR, "Failed to bind uevent socket.");
     123                 :          0 :                 goto err;
     124                 :            :         }
     125                 :            : 
     126         [ #  # ]:          0 :         if (rte_intr_fd_set(intr_handle, fd))
     127                 :          0 :                 goto err;
     128                 :            : 
     129                 :            :         return 0;
     130                 :          0 : err:
     131                 :          0 :         close(fd);
     132                 :          0 :         return ret;
     133                 :            : }
     134                 :            : 
     135                 :            : struct rte_dev_event {
     136                 :            :         enum rte_dev_event_type type;   /**< device event type */
     137                 :            :         int subsystem;                  /**< subsystem id */
     138                 :            :         char *devname;                  /**< device name */
     139                 :            : };
     140                 :            : 
     141                 :            : static int
     142                 :          0 : dev_uev_parse(const char *buf, struct rte_dev_event *event, int length)
     143                 :            : {
     144                 :            :         char action[EAL_UEV_MSG_ELEM_LEN];
     145                 :            :         char subsystem[EAL_UEV_MSG_ELEM_LEN];
     146                 :            :         char pci_slot_name[EAL_UEV_MSG_ELEM_LEN];
     147                 :            :         int i = 0;
     148                 :            : 
     149                 :            :         memset(action, 0, EAL_UEV_MSG_ELEM_LEN);
     150                 :            :         memset(subsystem, 0, EAL_UEV_MSG_ELEM_LEN);
     151                 :            :         memset(pci_slot_name, 0, EAL_UEV_MSG_ELEM_LEN);
     152                 :            : 
     153         [ #  # ]:          0 :         while (i < length) {
     154         [ #  # ]:          0 :                 for (; i < length; i++) {
     155         [ #  # ]:          0 :                         if (*buf)
     156                 :            :                                 break;
     157                 :          0 :                         buf++;
     158                 :            :                 }
     159         [ #  # ]:          0 :                 if (i >= length)
     160                 :            :                         break;
     161                 :            : 
     162                 :            :                 /**
     163                 :            :                  * check device uevent from kernel side, no need to check
     164                 :            :                  * uevent from udev.
     165                 :            :                  */
     166         [ #  # ]:          0 :                 if (!strncmp(buf, "libudev", 7)) {
     167                 :            :                         return -1;
     168                 :            :                 }
     169         [ #  # ]:          0 :                 if (!strncmp(buf, "ACTION=", 7)) {
     170                 :          0 :                         buf += 7;
     171                 :          0 :                         i += 7;
     172                 :            :                         strlcpy(action, buf, sizeof(action));
     173         [ #  # ]:          0 :                 } else if (!strncmp(buf, "SUBSYSTEM=", 10)) {
     174                 :          0 :                         buf += 10;
     175                 :          0 :                         i += 10;
     176                 :            :                         strlcpy(subsystem, buf, sizeof(subsystem));
     177         [ #  # ]:          0 :                 } else if (!strncmp(buf, "PCI_SLOT_NAME=", 14)) {
     178                 :          0 :                         buf += 14;
     179                 :          0 :                         i += 14;
     180                 :            :                         strlcpy(pci_slot_name, buf, sizeof(subsystem));
     181                 :          0 :                         event->devname = strdup(pci_slot_name);
     182         [ #  # ]:          0 :                         if (event->devname == NULL)
     183                 :            :                                 return -1;
     184                 :            :                 }
     185         [ #  # ]:          0 :                 for (; i < length; i++) {
     186         [ #  # ]:          0 :                         if (*buf == '\0')
     187                 :            :                                 break;
     188                 :          0 :                         buf++;
     189                 :            :                 }
     190                 :            :         }
     191                 :            : 
     192                 :            :         /* parse the subsystem layer */
     193         [ #  # ]:          0 :         if (!strncmp(subsystem, "uio", 3))
     194                 :          0 :                 event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_UIO;
     195         [ #  # ]:          0 :         else if (!strncmp(subsystem, "pci", 3))
     196                 :          0 :                 event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_PCI;
     197         [ #  # ]:          0 :         else if (!strncmp(subsystem, "vfio", 4))
     198                 :          0 :                 event->subsystem = EAL_DEV_EVENT_SUBSYSTEM_VFIO;
     199                 :            :         else
     200                 :          0 :                 goto err;
     201                 :            : 
     202                 :            :         /* parse the action type */
     203         [ #  # ]:          0 :         if (!strncmp(action, "add", 3))
     204                 :          0 :                 event->type = RTE_DEV_EVENT_ADD;
     205         [ #  # ]:          0 :         else if (!strncmp(action, "remove", 6))
     206                 :          0 :                 event->type = RTE_DEV_EVENT_REMOVE;
     207                 :            :         else
     208                 :          0 :                 goto err;
     209                 :            :         return 0;
     210                 :          0 : err:
     211                 :          0 :         free(event->devname);
     212                 :          0 :         return -1;
     213                 :            : }
     214                 :            : 
     215                 :            : static void
     216                 :          0 : dev_delayed_unregister(void *param)
     217                 :            : {
     218                 :          0 :         rte_intr_callback_unregister(intr_handle, dev_uev_handler, param);
     219         [ #  # ]:          0 :         if (rte_intr_fd_get(intr_handle) >= 0) {
     220                 :          0 :                 close(rte_intr_fd_get(intr_handle));
     221                 :          0 :                 rte_intr_fd_set(intr_handle, -1);
     222                 :            :         }
     223                 :          0 : }
     224                 :            : 
     225                 :            : static void
     226                 :          0 : dev_uev_handler(__rte_unused void *param)
     227                 :            : {
     228                 :            :         struct rte_dev_event uevent;
     229                 :            :         int ret;
     230                 :            :         char buf[EAL_UEV_MSG_LEN + 1];
     231                 :            :         struct rte_bus *bus;
     232                 :            :         struct rte_device *dev;
     233                 :            :         const char *busname = "";
     234                 :            : 
     235                 :            :         memset(&uevent, 0, sizeof(struct rte_dev_event));
     236                 :            :         memset(buf, 0, EAL_UEV_MSG_LEN + 1);
     237                 :            : 
     238         [ #  # ]:          0 :         if (rte_intr_fd_get(intr_handle) < 0)
     239                 :          0 :                 return;
     240                 :            : 
     241                 :          0 :         ret = recv(rte_intr_fd_get(intr_handle), buf, EAL_UEV_MSG_LEN,
     242                 :            :                    MSG_DONTWAIT);
     243   [ #  #  #  # ]:          0 :         if (ret < 0 && errno == EAGAIN)
     244                 :            :                 return;
     245         [ #  # ]:          0 :         else if (ret <= 0) {
     246                 :            :                 /* connection is closed or broken, can not up again. */
     247                 :          0 :                 EAL_LOG(ERR, "uevent socket connection is broken.");
     248                 :          0 :                 rte_eal_alarm_set(1, dev_delayed_unregister, NULL);
     249                 :          0 :                 return;
     250                 :            :         }
     251                 :            : 
     252                 :          0 :         ret = dev_uev_parse(buf, &uevent, EAL_UEV_MSG_LEN);
     253         [ #  # ]:          0 :         if (ret < 0) {
     254                 :          0 :                 EAL_LOG(DEBUG, "Ignoring uevent '%s'", buf);
     255                 :          0 :                 return;
     256                 :            :         }
     257                 :            : 
     258                 :          0 :         EAL_LOG(DEBUG, "receive uevent(name:%s, type:%d, subsystem:%d)",
     259                 :            :                 uevent.devname, uevent.type, uevent.subsystem);
     260                 :            : 
     261         [ #  # ]:          0 :         switch (uevent.subsystem) {
     262                 :          0 :         case EAL_DEV_EVENT_SUBSYSTEM_PCI:
     263                 :            :         case EAL_DEV_EVENT_SUBSYSTEM_UIO:
     264                 :            :                 busname = "pci";
     265                 :          0 :                 break;
     266                 :            :         default:
     267                 :            :                 break;
     268                 :            :         }
     269                 :            : 
     270         [ #  # ]:          0 :         if (uevent.devname) {
     271   [ #  #  #  # ]:          0 :                 if (uevent.type == RTE_DEV_EVENT_REMOVE && hotplug_handle) {
     272                 :            :                         rte_spinlock_lock(&failure_handle_lock);
     273                 :          0 :                         bus = rte_bus_find_by_name(busname);
     274         [ #  # ]:          0 :                         if (bus == NULL) {
     275                 :          0 :                                 EAL_LOG(ERR, "Cannot find bus (%s)",
     276                 :            :                                         busname);
     277                 :          0 :                                 goto failure_handle_err;
     278                 :            :                         }
     279                 :            : 
     280                 :          0 :                         dev = bus->find_device(NULL, cmp_dev_name,
     281                 :          0 :                                                uevent.devname);
     282         [ #  # ]:          0 :                         if (dev == NULL) {
     283                 :          0 :                                 EAL_LOG(ERR, "Cannot find device (%s) on "
     284                 :            :                                         "bus (%s)", uevent.devname, busname);
     285                 :          0 :                                 goto failure_handle_err;
     286                 :            :                         }
     287                 :            : 
     288                 :          0 :                         ret = bus->hot_unplug_handler(dev);
     289         [ #  # ]:          0 :                         if (ret) {
     290                 :          0 :                                 EAL_LOG(ERR, "Can not handle hot-unplug "
     291                 :            :                                         "for device (%s)", dev->name);
     292                 :            :                         }
     293                 :            :                         rte_spinlock_unlock(&failure_handle_lock);
     294                 :            :                 }
     295                 :          0 :                 rte_dev_event_callback_process(uevent.devname, uevent.type);
     296                 :          0 :                 free(uevent.devname);
     297                 :            :         }
     298                 :            : 
     299                 :            :         return;
     300                 :            : 
     301                 :          0 : failure_handle_err:
     302                 :            :         rte_spinlock_unlock(&failure_handle_lock);
     303                 :          0 :         free(uevent.devname);
     304                 :            : }
     305                 :            : 
     306                 :            : int
     307                 :          0 : rte_dev_event_monitor_start(void)
     308                 :            : {
     309                 :            :         int ret = 0;
     310                 :            : 
     311                 :          0 :         rte_rwlock_write_lock(&monitor_lock);
     312                 :            : 
     313         [ #  # ]:          0 :         if (monitor_refcount) {
     314                 :          0 :                 monitor_refcount++;
     315                 :          0 :                 goto exit;
     316                 :            :         }
     317                 :            : 
     318                 :          0 :         intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
     319         [ #  # ]:          0 :         if (intr_handle == NULL) {
     320                 :          0 :                 EAL_LOG(ERR, "Fail to allocate intr_handle");
     321                 :          0 :                 goto exit;
     322                 :            :         }
     323                 :            : 
     324                 :          0 :         ret = rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_DEV_EVENT);
     325         [ #  # ]:          0 :         if (ret)
     326                 :          0 :                 goto exit;
     327                 :            : 
     328                 :          0 :         ret = rte_intr_fd_set(intr_handle, -1);
     329         [ #  # ]:          0 :         if (ret)
     330                 :          0 :                 goto exit;
     331                 :            : 
     332                 :          0 :         ret = dev_uev_socket_fd_create();
     333         [ #  # ]:          0 :         if (ret) {
     334                 :          0 :                 EAL_LOG(ERR, "error create device event fd.");
     335                 :          0 :                 goto exit;
     336                 :            :         }
     337                 :            : 
     338                 :          0 :         ret = rte_intr_callback_register(intr_handle, dev_uev_handler, NULL);
     339                 :            : 
     340         [ #  # ]:          0 :         if (ret) {
     341                 :          0 :                 close(rte_intr_fd_get(intr_handle));
     342                 :          0 :                 goto exit;
     343                 :            :         }
     344                 :            : 
     345                 :          0 :         monitor_refcount++;
     346                 :            : 
     347                 :          0 : exit:
     348         [ #  # ]:          0 :         if (ret) {
     349                 :          0 :                 rte_intr_instance_free(intr_handle);
     350                 :          0 :                 intr_handle = NULL;
     351                 :            :         }
     352                 :            :         rte_rwlock_write_unlock(&monitor_lock);
     353                 :          0 :         return ret;
     354                 :            : }
     355                 :            : 
     356                 :            : int
     357                 :          0 : rte_dev_event_monitor_stop(void)
     358                 :            : {
     359                 :            :         int ret = 0;
     360                 :            : 
     361                 :          0 :         rte_rwlock_write_lock(&monitor_lock);
     362                 :            : 
     363         [ #  # ]:          0 :         if (!monitor_refcount) {
     364                 :          0 :                 EAL_LOG(ERR, "device event monitor already stopped");
     365                 :          0 :                 goto exit;
     366                 :            :         }
     367                 :            : 
     368         [ #  # ]:          0 :         if (monitor_refcount > 1) {
     369                 :          0 :                 monitor_refcount--;
     370                 :          0 :                 goto exit;
     371                 :            :         }
     372                 :            : 
     373                 :          0 :         ret = rte_intr_callback_unregister(intr_handle, dev_uev_handler,
     374                 :            :                                            (void *)-1);
     375         [ #  # ]:          0 :         if (ret < 0) {
     376                 :          0 :                 EAL_LOG(ERR, "fail to unregister uevent callback.");
     377                 :          0 :                 goto exit;
     378                 :            :         }
     379                 :            : 
     380                 :          0 :         close(rte_intr_fd_get(intr_handle));
     381                 :          0 :         rte_intr_instance_free(intr_handle);
     382                 :          0 :         intr_handle = NULL;
     383                 :            :         ret = 0;
     384                 :            : 
     385                 :          0 :         monitor_refcount--;
     386                 :            : 
     387                 :          0 : exit:
     388                 :            :         rte_rwlock_write_unlock(&monitor_lock);
     389                 :            : 
     390                 :          0 :         return ret;
     391                 :            : }
     392                 :            : 
     393                 :            : static int
     394                 :          0 : dev_sigbus_handler_register(void)
     395                 :            : {
     396                 :            :         sigset_t mask;
     397                 :            :         struct sigaction action;
     398                 :            : 
     399                 :          0 :         rte_errno = 0;
     400                 :            : 
     401         [ #  # ]:          0 :         if (sigbus_need_recover)
     402                 :            :                 return 0;
     403                 :            : 
     404                 :          0 :         sigemptyset(&mask);
     405                 :          0 :         sigaddset(&mask, SIGBUS);
     406                 :          0 :         action.sa_flags = SA_SIGINFO;
     407                 :          0 :         action.sa_mask = mask;
     408                 :          0 :         action.sa_sigaction = sigbus_handler;
     409                 :          0 :         sigbus_need_recover = !sigaction(SIGBUS, &action, &sigbus_action_old);
     410                 :            : 
     411                 :          0 :         return rte_errno;
     412                 :            : }
     413                 :            : 
     414                 :            : static int
     415                 :          0 : dev_sigbus_handler_unregister(void)
     416                 :            : {
     417         [ #  # ]:          0 :         rte_errno = 0;
     418                 :            : 
     419                 :            :         sigbus_action_recover();
     420                 :            : 
     421                 :          0 :         return rte_errno;
     422                 :            : }
     423                 :            : 
     424                 :            : int
     425                 :          0 : rte_dev_hotplug_handle_enable(void)
     426                 :            : {
     427                 :            :         int ret = 0;
     428                 :            : 
     429                 :          0 :         ret = dev_sigbus_handler_register();
     430         [ #  # ]:          0 :         if (ret < 0)
     431                 :          0 :                 EAL_LOG(ERR,
     432                 :            :                         "fail to register sigbus handler for devices.");
     433                 :            : 
     434                 :          0 :         hotplug_handle = true;
     435                 :            : 
     436                 :          0 :         return ret;
     437                 :            : }
     438                 :            : 
     439                 :            : int
     440                 :          0 : rte_dev_hotplug_handle_disable(void)
     441                 :            : {
     442                 :            :         int ret = 0;
     443                 :            : 
     444                 :          0 :         ret = dev_sigbus_handler_unregister();
     445         [ #  # ]:          0 :         if (ret < 0)
     446                 :          0 :                 EAL_LOG(ERR,
     447                 :            :                         "fail to unregister sigbus handler for devices.");
     448                 :            : 
     449                 :          0 :         hotplug_handle = false;
     450                 :            : 
     451                 :          0 :         return ret;
     452                 :            : }

Generated by: LCOV version 1.14