LCOV - code coverage report
Current view: top level - lib/vhost - fd_man.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 1 138 0.7 %
Date: 2025-03-01 20:23:48 Functions: 1 9 11.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 1 108 0.9 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2010-2014 Intel Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <errno.h>
       6                 :            : #include <stdio.h>
       7                 :            : #include <string.h>
       8                 :            : #include <sys/epoll.h>
       9                 :            : #include <unistd.h>
      10                 :            : 
      11                 :            : #include <rte_common.h>
      12                 :            : #include <rte_log.h>
      13                 :            : #include <rte_malloc.h>
      14                 :            : #include <rte_string_fns.h>
      15                 :            : #include <rte_thread.h>
      16                 :            : 
      17                 :            : #include "fd_man.h"
      18                 :            : 
      19         [ -  + ]:        252 : RTE_LOG_REGISTER_SUFFIX(vhost_fdset_logtype, fdset, INFO);
      20                 :            : #define RTE_LOGTYPE_VHOST_FDMAN vhost_fdset_logtype
      21                 :            : #define VHOST_FDMAN_LOG(level, ...) \
      22                 :            :         RTE_LOG_LINE(level, VHOST_FDMAN, "" __VA_ARGS__)
      23                 :            : 
      24                 :            : struct fdentry {
      25                 :            :         int fd;         /* -1 indicates this entry is empty */
      26                 :            :         fd_cb rcb;      /* callback when this fd is readable. */
      27                 :            :         fd_cb wcb;      /* callback when this fd is writeable.*/
      28                 :            :         void *dat;      /* fd context */
      29                 :            :         int busy;       /* whether this entry is being used in cb. */
      30                 :            :         LIST_ENTRY(fdentry) next;
      31                 :            : };
      32                 :            : 
      33                 :            : struct fdset {
      34                 :            :         char name[RTE_THREAD_NAME_SIZE];
      35                 :            :         int epfd;
      36                 :            :         struct fdentry fd[MAX_FDS];
      37                 :            :         LIST_HEAD(, fdentry) fdlist;
      38                 :            :         int next_free_idx;
      39                 :            :         rte_thread_t tid;
      40                 :            :         pthread_mutex_t fd_mutex;
      41                 :            :         bool destroy;
      42                 :            : };
      43                 :            : 
      44                 :            : #define MAX_FDSETS 8
      45                 :            : 
      46                 :            : static struct fdset *fdsets[MAX_FDSETS];
      47                 :            : static pthread_mutex_t fdsets_mutex = PTHREAD_MUTEX_INITIALIZER;
      48                 :            : 
      49                 :            : static uint32_t fdset_event_dispatch(void *arg);
      50                 :            : 
      51                 :            : static struct fdset *
      52                 :          0 : fdset_lookup(const char *name)
      53                 :            : {
      54                 :            :         int i;
      55                 :            : 
      56         [ #  # ]:          0 :         for (i = 0; i < MAX_FDSETS; i++) {
      57                 :          0 :                 struct fdset *fdset = fdsets[i];
      58         [ #  # ]:          0 :                 if (fdset == NULL)
      59                 :          0 :                         continue;
      60                 :            : 
      61         [ #  # ]:          0 :                 if (!strncmp(fdset->name, name, RTE_THREAD_NAME_SIZE))
      62                 :          0 :                         return fdset;
      63                 :            :         }
      64                 :            : 
      65                 :            :         return NULL;
      66                 :            : }
      67                 :            : 
      68                 :            : static int
      69                 :            : fdset_insert(struct fdset *fdset)
      70                 :            : {
      71                 :            :         int i;
      72                 :            : 
      73         [ #  # ]:          0 :         for (i = 0; i < MAX_FDSETS; i++) {
      74         [ #  # ]:          0 :                 if (fdsets[i] == NULL) {
      75                 :          0 :                         fdsets[i] = fdset;
      76                 :            :                         return 0;
      77                 :            :                 }
      78                 :            :         }
      79                 :            : 
      80                 :            :         return -1;
      81                 :            : }
      82                 :            : 
      83                 :            : struct fdset *
      84                 :          0 : fdset_init(const char *name)
      85                 :            : {
      86                 :            :         struct fdset *fdset;
      87                 :            :         uint32_t val;
      88                 :            :         int i;
      89                 :            : 
      90                 :          0 :         pthread_mutex_lock(&fdsets_mutex);
      91                 :          0 :         fdset = fdset_lookup(name);
      92         [ #  # ]:          0 :         if (fdset) {
      93                 :          0 :                 pthread_mutex_unlock(&fdsets_mutex);
      94                 :          0 :                 return fdset;
      95                 :            :         }
      96                 :            : 
      97                 :          0 :         fdset = rte_zmalloc(NULL, sizeof(*fdset), 0);
      98         [ #  # ]:          0 :         if (!fdset) {
      99                 :          0 :                 VHOST_FDMAN_LOG(ERR, "failed to alloc fdset %s", name);
     100                 :          0 :                 goto err_unlock;
     101                 :            :         }
     102                 :            : 
     103                 :          0 :         rte_strscpy(fdset->name, name, RTE_THREAD_NAME_SIZE);
     104                 :            : 
     105                 :          0 :         pthread_mutex_init(&fdset->fd_mutex, NULL);
     106                 :            : 
     107         [ #  # ]:          0 :         for (i = 0; i < (int)RTE_DIM(fdset->fd); i++) {
     108                 :          0 :                 fdset->fd[i].fd = -1;
     109                 :          0 :                 fdset->fd[i].dat = NULL;
     110                 :            :         }
     111                 :          0 :         LIST_INIT(&fdset->fdlist);
     112                 :            : 
     113                 :            :         /*
     114                 :            :          * Any non-zero value would work (see man epoll_create),
     115                 :            :          * but pass MAX_FDS for consistency.
     116                 :            :          */
     117                 :          0 :         fdset->epfd = epoll_create(MAX_FDS);
     118         [ #  # ]:          0 :         if (fdset->epfd < 0) {
     119                 :          0 :                 VHOST_FDMAN_LOG(ERR, "failed to create epoll for %s fdset", name);
     120                 :          0 :                 goto err_free;
     121                 :            :         }
     122                 :            : 
     123         [ #  # ]:          0 :         if (rte_thread_create_internal_control(&fdset->tid, fdset->name,
     124                 :            :                                         fdset_event_dispatch, fdset)) {
     125                 :          0 :                 VHOST_FDMAN_LOG(ERR, "Failed to create %s event dispatch thread",
     126                 :            :                                 fdset->name);
     127                 :          0 :                 goto err_epoll;
     128                 :            :         }
     129                 :            : 
     130                 :            :         if (fdset_insert(fdset)) {
     131                 :          0 :                 VHOST_FDMAN_LOG(ERR, "Failed to insert fdset %s", name);
     132                 :          0 :                 goto err_thread;
     133                 :            :         }
     134                 :            : 
     135                 :          0 :         pthread_mutex_unlock(&fdsets_mutex);
     136                 :            : 
     137                 :          0 :         return fdset;
     138                 :            : 
     139                 :            : err_thread:
     140                 :          0 :         fdset->destroy = true;
     141                 :          0 :         rte_thread_join(fdset->tid, &val);
     142                 :          0 : err_epoll:
     143                 :          0 :         close(fdset->epfd);
     144                 :          0 : err_free:
     145                 :          0 :         rte_free(fdset);
     146                 :          0 : err_unlock:
     147                 :          0 :         pthread_mutex_unlock(&fdsets_mutex);
     148                 :            : 
     149                 :          0 :         return NULL;
     150                 :            : }
     151                 :            : 
     152                 :            : static int
     153                 :          0 : fdset_insert_entry(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
     154                 :            : {
     155                 :            :         struct fdentry *pfdentry;
     156                 :            : 
     157         [ #  # ]:          0 :         if (pfdset->next_free_idx >= (int)RTE_DIM(pfdset->fd))
     158                 :            :                 return -1;
     159                 :            : 
     160                 :          0 :         pfdentry = &pfdset->fd[pfdset->next_free_idx];
     161                 :          0 :         pfdentry->fd  = fd;
     162                 :          0 :         pfdentry->rcb = rcb;
     163                 :          0 :         pfdentry->wcb = wcb;
     164                 :          0 :         pfdentry->dat = dat;
     165                 :            : 
     166         [ #  # ]:          0 :         LIST_INSERT_HEAD(&pfdset->fdlist, pfdentry, next);
     167                 :            : 
     168                 :            :         /* Find next free slot */
     169                 :          0 :         pfdset->next_free_idx++;
     170         [ #  # ]:          0 :         for (; pfdset->next_free_idx < (int)RTE_DIM(pfdset->fd); pfdset->next_free_idx++) {
     171         [ #  # ]:          0 :                 if (pfdset->fd[pfdset->next_free_idx].fd != -1)
     172                 :            :                         continue;
     173                 :            :                 break;
     174                 :            :         }
     175                 :            : 
     176                 :            :         return 0;
     177                 :            : }
     178                 :            : 
     179                 :            : static void
     180                 :            : fdset_remove_entry(struct fdset *pfdset, struct fdentry *pfdentry)
     181                 :            : {
     182                 :            :         int entry_idx;
     183                 :            : 
     184                 :          0 :         pfdentry->fd = -1;
     185                 :          0 :         pfdentry->rcb = pfdentry->wcb = NULL;
     186                 :          0 :         pfdentry->dat = NULL;
     187                 :            : 
     188                 :          0 :         entry_idx = pfdentry - pfdset->fd;
     189   [ #  #  #  # ]:          0 :         if (entry_idx < pfdset->next_free_idx)
     190                 :          0 :                 pfdset->next_free_idx = entry_idx;
     191                 :            : 
     192   [ #  #  #  # ]:          0 :         LIST_REMOVE(pfdentry, next);
     193                 :          0 : }
     194                 :            : 
     195                 :            : static struct fdentry *
     196                 :            : fdset_find_entry_locked(struct fdset *pfdset, int fd)
     197                 :            : {
     198                 :            :         struct fdentry *pfdentry;
     199                 :            : 
     200   [ #  #  #  #  :          0 :         LIST_FOREACH(pfdentry, &pfdset->fdlist, next) {
             #  #  #  # ]
     201   [ #  #  #  #  :          0 :                 if (pfdentry->fd != fd)
             #  #  #  # ]
     202                 :            :                         continue;
     203                 :            :                 return pfdentry;
     204                 :            :         }
     205                 :            : 
     206                 :            :         return NULL;
     207                 :            : }
     208                 :            : 
     209                 :            : /**
     210                 :            :  * Register the fd in the fdset with read/write handler and context.
     211                 :            :  */
     212                 :            : int
     213                 :          0 : fdset_add(struct fdset *pfdset, int fd, fd_cb rcb, fd_cb wcb, void *dat)
     214                 :            : {
     215                 :            :         struct epoll_event ev;
     216                 :            :         struct fdentry *pfdentry;
     217                 :            :         int ret = 0;
     218                 :            : 
     219         [ #  # ]:          0 :         if (pfdset == NULL || fd == -1) {
     220                 :            :                 ret = -1;
     221                 :          0 :                 goto out;
     222                 :            :         }
     223                 :            : 
     224                 :          0 :         pthread_mutex_lock(&pfdset->fd_mutex);
     225                 :          0 :         ret = fdset_insert_entry(pfdset, fd, rcb, wcb, dat);
     226         [ #  # ]:          0 :         if (ret < 0) {
     227                 :          0 :                 VHOST_FDMAN_LOG(ERR, "failed to insert fdset entry");
     228                 :          0 :                 pthread_mutex_unlock(&pfdset->fd_mutex);
     229                 :          0 :                 goto out;
     230                 :            :         }
     231                 :          0 :         pthread_mutex_unlock(&pfdset->fd_mutex);
     232                 :            : 
     233                 :            :         ev.events = EPOLLERR;
     234         [ #  # ]:          0 :         ev.events |= rcb ? EPOLLIN : 0;
     235         [ #  # ]:          0 :         ev.events |= wcb ? EPOLLOUT : 0;
     236                 :          0 :         ev.data.fd = fd;
     237                 :            : 
     238                 :          0 :         ret = epoll_ctl(pfdset->epfd, EPOLL_CTL_ADD, fd, &ev);
     239         [ #  # ]:          0 :         if (ret < 0) {
     240                 :          0 :                 VHOST_FDMAN_LOG(ERR, "could not add %d fd to %d epfd: %s",
     241                 :            :                         fd, pfdset->epfd, strerror(errno));
     242                 :          0 :                 goto out_remove;
     243                 :            :         }
     244                 :            : 
     245                 :            :         return 0;
     246                 :            : out_remove:
     247                 :          0 :         pthread_mutex_lock(&pfdset->fd_mutex);
     248                 :            :         pfdentry = fdset_find_entry_locked(pfdset, fd);
     249         [ #  # ]:          0 :         if (pfdentry)
     250                 :            :                 fdset_remove_entry(pfdset, pfdentry);
     251                 :          0 :         pthread_mutex_unlock(&pfdset->fd_mutex);
     252                 :            : out:
     253                 :            :         return ret;
     254                 :            : }
     255                 :            : 
     256                 :            : static void
     257                 :          0 : fdset_del_locked(struct fdset *pfdset, struct fdentry *pfdentry)
     258                 :            : {
     259         [ #  # ]:          0 :         if (epoll_ctl(pfdset->epfd, EPOLL_CTL_DEL, pfdentry->fd, NULL) == -1) {
     260         [ #  # ]:          0 :                 if (errno == EBADF) /* File might have already been closed. */
     261                 :          0 :                         VHOST_FDMAN_LOG(DEBUG, "could not remove %d fd from %d epfd: %s",
     262                 :            :                                 pfdentry->fd, pfdset->epfd, strerror(errno));
     263                 :            :                 else
     264                 :          0 :                         VHOST_FDMAN_LOG(ERR, "could not remove %d fd from %d epfd: %s",
     265                 :            :                                 pfdentry->fd, pfdset->epfd, strerror(errno));
     266                 :            :         }
     267                 :            : 
     268                 :            :         fdset_remove_entry(pfdset, pfdentry);
     269                 :          0 : }
     270                 :            : 
     271                 :            : void
     272                 :          0 : fdset_del(struct fdset *pfdset, int fd)
     273                 :            : {
     274                 :            :         struct fdentry *pfdentry;
     275                 :            : 
     276         [ #  # ]:          0 :         if (pfdset == NULL || fd == -1)
     277                 :            :                 return;
     278                 :            : 
     279                 :            :         do {
     280                 :          0 :                 pthread_mutex_lock(&pfdset->fd_mutex);
     281                 :            :                 pfdentry = fdset_find_entry_locked(pfdset, fd);
     282   [ #  #  #  # ]:          0 :                 if (pfdentry != NULL && pfdentry->busy == 0) {
     283                 :          0 :                         fdset_del_locked(pfdset, pfdentry);
     284                 :            :                         pfdentry = NULL;
     285                 :            :                 }
     286                 :          0 :                 pthread_mutex_unlock(&pfdset->fd_mutex);
     287         [ #  # ]:          0 :         } while (pfdentry != NULL);
     288                 :            : }
     289                 :            : 
     290                 :            : /**
     291                 :            :  *  Unregister the fd from the fdset.
     292                 :            :  *
     293                 :            :  *  If parameters are invalid, return directly -2.
     294                 :            :  *  And check whether fd is busy, if yes, return -1.
     295                 :            :  *  Otherwise, try to delete the fd from fdset and
     296                 :            :  *  return true.
     297                 :            :  */
     298                 :            : int
     299                 :          0 : fdset_try_del(struct fdset *pfdset, int fd)
     300                 :            : {
     301                 :            :         struct fdentry *pfdentry;
     302                 :            : 
     303         [ #  # ]:          0 :         if (pfdset == NULL || fd == -1)
     304                 :            :                 return -2;
     305                 :            : 
     306                 :          0 :         pthread_mutex_lock(&pfdset->fd_mutex);
     307                 :            :         pfdentry = fdset_find_entry_locked(pfdset, fd);
     308   [ #  #  #  # ]:          0 :         if (pfdentry != NULL && pfdentry->busy != 0) {
     309                 :          0 :                 pthread_mutex_unlock(&pfdset->fd_mutex);
     310                 :          0 :                 return -1;
     311                 :            :         }
     312                 :            : 
     313         [ #  # ]:          0 :         if (pfdentry != NULL)
     314                 :          0 :                 fdset_del_locked(pfdset, pfdentry);
     315                 :            : 
     316                 :          0 :         pthread_mutex_unlock(&pfdset->fd_mutex);
     317                 :          0 :         return 0;
     318                 :            : }
     319                 :            : 
     320                 :            : /**
     321                 :            :  * This functions runs in infinite blocking loop until there is no fd in
     322                 :            :  * pfdset. It calls corresponding r/w handler if there is event on the fd.
     323                 :            :  *
     324                 :            :  * Before the callback is called, we set the flag to busy status; If other
     325                 :            :  * thread(now rte_vhost_driver_unregister) calls fdset_del concurrently, it
     326                 :            :  * will wait until the flag is reset to zero(which indicates the callback is
     327                 :            :  * finished), then it could free the context after fdset_del.
     328                 :            :  */
     329                 :            : static uint32_t
     330                 :          0 : fdset_event_dispatch(void *arg)
     331                 :            : {
     332                 :            :         int i;
     333                 :            :         fd_cb rcb, wcb;
     334                 :            :         void *dat;
     335                 :            :         int fd, numfds;
     336                 :            :         int close1, close2;
     337                 :            :         struct fdset *pfdset = arg;
     338                 :            : 
     339         [ #  # ]:          0 :         if (pfdset == NULL)
     340                 :            :                 return 0;
     341                 :            : 
     342                 :            :         while (1) {
     343                 :            :                 struct epoll_event events[MAX_FDS];
     344                 :            :                 struct fdentry *pfdentry;
     345                 :            : 
     346                 :          0 :                 numfds = epoll_wait(pfdset->epfd, events, RTE_DIM(events), 1000);
     347         [ #  # ]:          0 :                 if (numfds < 0)
     348                 :          0 :                         continue;
     349                 :            : 
     350         [ #  # ]:          0 :                 for (i = 0; i < numfds; i++) {
     351                 :          0 :                         pthread_mutex_lock(&pfdset->fd_mutex);
     352                 :            : 
     353                 :          0 :                         fd = events[i].data.fd;
     354                 :            :                         pfdentry = fdset_find_entry_locked(pfdset, fd);
     355         [ #  # ]:          0 :                         if (pfdentry == NULL) {
     356                 :          0 :                                 pthread_mutex_unlock(&pfdset->fd_mutex);
     357                 :          0 :                                 continue;
     358                 :            :                         }
     359                 :            : 
     360                 :          0 :                         close1 = close2 = 0;
     361                 :            : 
     362                 :          0 :                         rcb = pfdentry->rcb;
     363                 :          0 :                         wcb = pfdentry->wcb;
     364                 :          0 :                         dat = pfdentry->dat;
     365                 :          0 :                         pfdentry->busy = 1;
     366                 :            : 
     367                 :          0 :                         pthread_mutex_unlock(&pfdset->fd_mutex);
     368                 :            : 
     369   [ #  #  #  # ]:          0 :                         if (rcb && events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP))
     370                 :          0 :                                 rcb(fd, dat, &close1);
     371   [ #  #  #  # ]:          0 :                         if (wcb && events[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP))
     372                 :          0 :                                 wcb(fd, dat, &close2);
     373                 :          0 :                         pfdentry->busy = 0;
     374                 :            :                         /*
     375                 :            :                          * fdset_del needs to check busy flag.
     376                 :            :                          * We don't allow fdset_del to be called in callback
     377                 :            :                          * directly.
     378                 :            :                          */
     379                 :            :                         /*
     380                 :            :                          * A concurrent fdset_del may have been waiting for the
     381                 :            :                          * fdentry not to be busy, so we can't call
     382                 :            :                          * fdset_del_locked().
     383                 :            :                          */
     384   [ #  #  #  # ]:          0 :                         if (close1 || close2) {
     385                 :          0 :                                 fdset_del(pfdset, fd);
     386                 :          0 :                                 close(fd);
     387                 :            :                         }
     388                 :            :                 }
     389                 :            : 
     390         [ #  # ]:          0 :                 if (pfdset->destroy)
     391                 :            :                         break;
     392                 :            :         }
     393                 :            : 
     394                 :          0 :         return 0;
     395                 :            : }

Generated by: LCOV version 1.14