LCOV - code coverage report
Current view: top level - lib/eal/linux - eal_timer.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 16 29 55.2 %
Date: 2024-12-01 18:57:19 Functions: 2 3 66.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 5 20 25.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2010-2014 Intel Corporation.
       3                 :            :  * Copyright(c) 2012-2013 6WIND S.A.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include <stdio.h>
       7                 :            : #include <stdint.h>
       8                 :            : #include <inttypes.h>
       9                 :            : #ifdef RTE_LIBEAL_USE_HPET
      10                 :            : #include <fcntl.h>
      11                 :            : #include <sys/mman.h>
      12                 :            : #include <unistd.h>
      13                 :            : #endif
      14                 :            : 
      15                 :            : #include <rte_common.h>
      16                 :            : #include <rte_cycles.h>
      17                 :            : #include <rte_thread.h>
      18                 :            : 
      19                 :            : #include "eal_private.h"
      20                 :            : 
      21                 :            : enum timer_source eal_timer_source = EAL_TIMER_HPET;
      22                 :            : 
      23                 :            : #ifdef RTE_LIBEAL_USE_HPET
      24                 :            : 
      25                 :            : #define DEV_HPET "/dev/hpet"
      26                 :            : 
      27                 :            : /* Maximum number of counters. */
      28                 :            : #define HPET_TIMER_NUM 3
      29                 :            : 
      30                 :            : /* General capabilities register */
      31                 :            : #define CLK_PERIOD_SHIFT     32 /* Clock period shift. */
      32                 :            : #define CLK_PERIOD_MASK      0xffffffff00000000ULL /* Clock period mask. */
      33                 :            : 
      34                 :            : /**
      35                 :            :  * HPET timer registers. From the Intel IA-PC HPET (High Precision Event
      36                 :            :  * Timers) Specification.
      37                 :            :  */
      38                 :            : struct eal_hpet_regs {
      39                 :            :         /* Memory-mapped, software visible registers */
      40                 :            :         uint64_t capabilities;      /**< RO General Capabilities Register. */
      41                 :            :         uint64_t reserved0;         /**< Reserved for future use. */
      42                 :            :         uint64_t config;            /**< RW General Configuration Register. */
      43                 :            :         uint64_t reserved1;         /**< Reserved for future use. */
      44                 :            :         uint64_t isr;               /**< RW Clear General Interrupt Status. */
      45                 :            :         uint64_t reserved2[25];     /**< Reserved for future use. */
      46                 :            :         union {
      47                 :            :                 uint64_t counter;   /**< RW Main Counter Value Register. */
      48                 :            :                 struct {
      49                 :            :                         uint32_t counter_l; /**< RW Main Counter Low. */
      50                 :            :                         uint32_t counter_h; /**< RW Main Counter High. */
      51                 :            :                 };
      52                 :            :         };
      53                 :            :         uint64_t reserved3;         /**< Reserved for future use. */
      54                 :            :         struct {
      55                 :            :                 uint64_t config;    /**< RW Timer Config and Capability Reg. */
      56                 :            :                 uint64_t comp;      /**< RW Timer Comparator Value Register. */
      57                 :            :                 uint64_t fsb;       /**< RW FSB Interrupt Route Register. */
      58                 :            :                 uint64_t reserved4; /**< Reserved for future use. */
      59                 :            :         } timers[HPET_TIMER_NUM]; /**< Set of HPET timers. */
      60                 :            : };
      61                 :            : 
      62                 :            : /* Mmap'd hpet registers */
      63                 :            : static volatile struct eal_hpet_regs *eal_hpet = NULL;
      64                 :            : 
      65                 :            : /* Period at which the HPET counter increments in
      66                 :            :  * femtoseconds (10^-15 seconds). */
      67                 :            : static uint32_t eal_hpet_resolution_fs = 0;
      68                 :            : 
      69                 :            : /* Frequency of the HPET counter in Hz */
      70                 :            : static uint64_t eal_hpet_resolution_hz = 0;
      71                 :            : 
      72                 :            : /* Incremented 4 times during one 32bits hpet full count */
      73                 :            : static uint32_t eal_hpet_msb;
      74                 :            : 
      75                 :            : static rte_thread_t msb_inc_thread_id;
      76                 :            : 
      77                 :            : /*
      78                 :            :  * This function runs on a specific thread to update a global variable
      79                 :            :  * containing used to process MSB of the HPET (unfortunately, we need
      80                 :            :  * this because hpet is 32 bits by default under linux).
      81                 :            :  */
      82                 :            : static uint32_t
      83                 :            : hpet_msb_inc(__rte_unused void *arg)
      84                 :            : {
      85                 :            :         uint32_t t;
      86                 :            : 
      87                 :            :         while (1) {
      88                 :            :                 t = (eal_hpet->counter_l >> 30);
      89                 :            :                 if (t != (eal_hpet_msb & 3))
      90                 :            :                         eal_hpet_msb ++;
      91                 :            :                 sleep(10);
      92                 :            :         }
      93                 :            :         return 0;
      94                 :            : }
      95                 :            : 
      96                 :            : uint64_t
      97                 :            : rte_get_hpet_hz(void)
      98                 :            : {
      99                 :            :         const struct internal_config *internal_conf =
     100                 :            :                 eal_get_internal_configuration();
     101                 :            : 
     102                 :            :         if (internal_conf->no_hpet)
     103                 :            :                 rte_panic("Error, HPET called, but no HPET present\n");
     104                 :            : 
     105                 :            :         return eal_hpet_resolution_hz;
     106                 :            : }
     107                 :            : 
     108                 :            : uint64_t
     109                 :            : rte_get_hpet_cycles(void)
     110                 :            : {
     111                 :            :         uint32_t t, msb;
     112                 :            :         uint64_t ret;
     113                 :            :         const struct internal_config *internal_conf =
     114                 :            :                 eal_get_internal_configuration();
     115                 :            : 
     116                 :            :         if (internal_conf->no_hpet)
     117                 :            :                 rte_panic("Error, HPET called, but no HPET present\n");
     118                 :            : 
     119                 :            :         t = eal_hpet->counter_l;
     120                 :            :         msb = eal_hpet_msb;
     121                 :            :         ret = (msb + 2 - (t >> 30)) / 4;
     122                 :            :         ret <<= 32;
     123                 :            :         ret += t;
     124                 :            :         return ret;
     125                 :            : }
     126                 :            : 
     127                 :            : #endif
     128                 :            : 
     129                 :            : #ifdef RTE_LIBEAL_USE_HPET
     130                 :            : /*
     131                 :            :  * Open and mmap /dev/hpet (high precision event timer) that will
     132                 :            :  * provide our time reference.
     133                 :            :  */
     134                 :            : int
     135                 :            : rte_eal_hpet_init(int make_default)
     136                 :            : {
     137                 :            :         int fd, ret;
     138                 :            :         struct internal_config *internal_conf =
     139                 :            :                 eal_get_internal_configuration();
     140                 :            : 
     141                 :            :         if (internal_conf->no_hpet) {
     142                 :            :                 EAL_LOG(NOTICE, "HPET is disabled");
     143                 :            :                 return -1;
     144                 :            :         }
     145                 :            : 
     146                 :            :         fd = open(DEV_HPET, O_RDONLY);
     147                 :            :         if (fd < 0) {
     148                 :            :                 EAL_LOG(ERR, "ERROR: Cannot open "DEV_HPET": %s!",
     149                 :            :                         strerror(errno));
     150                 :            :                 internal_conf->no_hpet = 1;
     151                 :            :                 return -1;
     152                 :            :         }
     153                 :            :         eal_hpet = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0);
     154                 :            :         if (eal_hpet == MAP_FAILED) {
     155                 :            :                 EAL_LOG(ERR, "ERROR: Cannot mmap "DEV_HPET"!");
     156                 :            :                 close(fd);
     157                 :            :                 internal_conf->no_hpet = 1;
     158                 :            :                 return -1;
     159                 :            :         }
     160                 :            :         close(fd);
     161                 :            : 
     162                 :            :         eal_hpet_resolution_fs = (uint32_t)((eal_hpet->capabilities &
     163                 :            :                                         CLK_PERIOD_MASK) >>
     164                 :            :                                         CLK_PERIOD_SHIFT);
     165                 :            : 
     166                 :            :         eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) /
     167                 :            :                 (uint64_t)eal_hpet_resolution_fs;
     168                 :            : 
     169                 :            :         EAL_LOG(INFO, "HPET frequency is ~%"PRIu64" kHz",
     170                 :            :                         eal_hpet_resolution_hz/1000);
     171                 :            : 
     172                 :            :         eal_hpet_msb = (eal_hpet->counter_l >> 30);
     173                 :            : 
     174                 :            :         /* create a thread that will increment a global variable for
     175                 :            :          * msb (hpet is 32 bits by default under linux) */
     176                 :            :         ret = rte_thread_create_internal_control(&msb_inc_thread_id, "hpet-msb",
     177                 :            :                         hpet_msb_inc, NULL);
     178                 :            :         if (ret != 0) {
     179                 :            :                 EAL_LOG(ERR, "ERROR: Cannot create HPET timer thread!");
     180                 :            :                 internal_conf->no_hpet = 1;
     181                 :            :                 return -1;
     182                 :            :         }
     183                 :            : 
     184                 :            :         if (make_default)
     185                 :            :                 eal_timer_source = EAL_TIMER_HPET;
     186                 :            :         return 0;
     187                 :            : }
     188                 :            : #endif
     189                 :            : 
     190                 :            : /* Check if the kernel deems the arch provided TSC frequency trustworthy. */
     191                 :            : 
     192                 :            : static bool
     193                 :          0 : is_tsc_known_freq(void)
     194                 :            : {
     195                 :            :         bool ret = true; /* Assume tsc_known_freq */
     196                 :            : 
     197                 :            : #if defined(RTE_ARCH_X86)
     198                 :            :         char line[2048];
     199                 :            :         FILE *stream;
     200                 :            : 
     201                 :          0 :         stream = fopen("/proc/cpuinfo", "r");
     202         [ #  # ]:          0 :         if (!stream) {
     203                 :          0 :                 EAL_LOG(WARNING, "Unable to open /proc/cpuinfo");
     204                 :          0 :                 return ret;
     205                 :            :         }
     206                 :            : 
     207         [ #  # ]:          0 :         while (fgets(line, sizeof(line), stream)) {
     208         [ #  # ]:          0 :                 if (strncmp(line, "flags", 5) != 0)
     209                 :          0 :                         continue;
     210                 :            : 
     211         [ #  # ]:          0 :                 if (!strstr(line, "tsc_known_freq"))
     212                 :            :                         ret = false;
     213                 :            : 
     214                 :            :                 break;
     215                 :            :         }
     216                 :            : 
     217                 :          0 :         fclose(stream);
     218                 :            : #endif
     219                 :            : 
     220                 :          0 :         return ret;
     221                 :            : }
     222                 :            : 
     223                 :            : uint64_t
     224                 :        154 : get_tsc_freq(uint64_t arch_hz)
     225                 :            : {
     226                 :            : #ifdef CLOCK_MONOTONIC_RAW
     227                 :            : #define NS_PER_SEC 1E9
     228                 :            : #define CYC_PER_100KHZ 1E5
     229                 :            : 
     230                 :        154 :         struct timespec sleeptime = {.tv_nsec = NS_PER_SEC / 10 }; /* 1/10 second */
     231                 :            : 
     232                 :            :         struct timespec t_start, t_end;
     233                 :            :         uint64_t tsc_hz;
     234                 :            : 
     235   [ -  +  -  - ]:        154 :         if (arch_hz && is_tsc_known_freq())
     236                 :            :                 return arch_hz;
     237                 :            : 
     238         [ +  - ]:        154 :         if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) {
     239                 :            :                 uint64_t ns, end, start = rte_rdtsc();
     240                 :        154 :                 nanosleep(&sleeptime,NULL);
     241                 :        154 :                 clock_gettime(CLOCK_MONOTONIC_RAW, &t_end);
     242                 :            :                 end = rte_rdtsc();
     243                 :        154 :                 ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC);
     244                 :        154 :                 ns += (t_end.tv_nsec - t_start.tv_nsec);
     245                 :            : 
     246                 :        154 :                 double secs = (double)ns/NS_PER_SEC;
     247                 :        154 :                 tsc_hz = (uint64_t)((end - start)/secs);
     248                 :            : 
     249         [ -  + ]:        154 :                 if (arch_hz) {
     250                 :            :                         /* Make sure we're within 1% for sanity check */
     251         [ #  # ]:          0 :                         if (RTE_MAX(arch_hz, tsc_hz) - RTE_MIN(arch_hz, tsc_hz) > arch_hz / 100)
     252                 :            :                                 return arch_hz;
     253                 :            : 
     254                 :          0 :                         EAL_LOG(DEBUG,
     255                 :            :                                 "Refined arch frequency %"PRIu64" to measured frequency %"PRIu64,
     256                 :            :                                 arch_hz, tsc_hz);
     257                 :            :                 }
     258                 :            : 
     259                 :            :                 /* Round up to 100Khz. 1E5 ~ 100Khz */
     260         [ +  + ]:        276 :                 return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_100KHZ);
     261                 :            :         }
     262                 :            : #endif
     263                 :            :         return arch_hz;
     264                 :            : }
     265                 :            : 
     266                 :            : int
     267                 :        179 : rte_eal_timer_init(void)
     268                 :            : {
     269                 :            : 
     270                 :        179 :         eal_timer_source = EAL_TIMER_TSC;
     271                 :            : 
     272                 :        179 :         set_tsc_freq();
     273                 :        179 :         return 0;
     274                 :            : }

Generated by: LCOV version 1.14