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

Generated by: LCOV version 1.14