LCOV - code coverage report
Current view: top level - root/contrail/vrouter/dpdk - vr_dpdk_fragment_assembler.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 73 89 82.0 %
Date: 2026-08-03 02:19:58 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2015 Semihalf. All rights reserved.
       3             :  */
       4             : 
       5             : #include <vr_dpdk.h>
       6             : #include <vr_fragment.h>
       7             : #include <vr_os.h>
       8             : #include <vr_packet.h>
       9             : 
      10             : /**
      11             :  * @name Private variables
      12             :  * @{
      13             :  */
      14             : 
      15             : /* Hash table buckets used by the assembler */
      16             : struct fragment_bucket {
      17             :     struct vr_fragment *frag_list;
      18             : };
      19             : static struct fragment_bucket **assembler_table;
      20             : 
      21             : /* Per CPU queues used to enqueue packets for the assembler */
      22             : struct per_cpu_fragment_queue {
      23             :     struct vr_fragment_queue queue;
      24             : };
      25             : static struct per_cpu_fragment_queue *per_cpu_queues;
      26             : 
      27             : static int assembler_scan_index;
      28             : static int assembler_scan_thresh = 1024;
      29             : 
      30             : /** @} */
      31             : 
      32             : /**
      33             :  * @name Private functions
      34             :  * @{
      35             :  */
      36             : static void
      37          21 : dpdk_fragment_assemble_queue(void *arg)
      38             : {
      39          21 :     struct per_cpu_fragment_queue *queue = (struct per_cpu_fragment_queue *)arg;
      40          21 :     struct vr_fragment_queue *fq = &queue->queue;
      41          21 :     vr_fragment_assemble_queue(fq);
      42          21 : }
      43             : 
      44             : static void
      45          53 : dpdk_assembler_table_exit(void)
      46             : {
      47             :     int i;
      48             : 
      49          53 :     vr_assembler_table_scan_exit();
      50             : 
      51          53 :     if (assembler_table) {
      52         159 :         for (i = 0; i < vr_dpdk.nb_fwd_lcores; ++i) {
      53         106 :             if (assembler_table[i] != NULL) {
      54         106 :                 vr_free(assembler_table[i], VR_ASSEMBLER_TABLE_OBJECT);
      55         106 :                 assembler_table[i] = NULL;
      56             :             }
      57             :         }
      58             : 
      59          53 :         vr_free(assembler_table, VR_ASSEMBLER_TABLE_OBJECT);
      60          53 :         assembler_table = NULL;
      61             :     }
      62             : 
      63          53 :     return;
      64             : }
      65             : 
      66             : static int
      67          53 : dpdk_assembler_table_init(void)
      68             : {
      69             :     unsigned int size;
      70             :     int i;
      71             : 
      72             :     /* Allocate array of pointers to the assembler tables for each fwd lcore */
      73          53 :     size = sizeof(struct fragment_bucket *) * vr_dpdk.nb_fwd_lcores;
      74          53 :     assembler_table = vr_zalloc(size, VR_ASSEMBLER_TABLE_OBJECT);
      75          53 :     if (!assembler_table) {
      76           0 :         RTE_LOG(ERR, VROUTER, "%s:%d Allocation for %u failed\n",
      77             :                 __FUNCTION__, __LINE__, size);
      78           0 :         return -ENOMEM;
      79             :     }
      80             : 
      81             :     /* Now allocate the assembler tables for each fwd lcore */
      82          53 :     size = sizeof(struct fragment_bucket) * VR_ASSEMBLER_BUCKET_COUNT;
      83         159 :     for (i = 0; i < vr_dpdk.nb_fwd_lcores; ++i) {
      84         106 :         assembler_table[i] = vr_zalloc(size, VR_ASSEMBLER_TABLE_OBJECT);
      85         106 :         if (!assembler_table[i]) {
      86           0 :             RTE_LOG(ERR, VROUTER, "%s:%d Allocation for %u failed\n",
      87             :                     __FUNCTION__, __LINE__, size);
      88           0 :             return -ENOMEM;
      89             :         }
      90             :     }
      91             : 
      92             :     /* Intentionally the vr_assembler_table_scan_init() is not called here as
      93             :      * it would set up timers on the timer lcore. For the timers the forwarding
      94             :      * lcores are used, therefore allowing for complete lock elimination. */
      95             : 
      96          53 :     return 0;
      97             : }
      98             : 
      99             : static int
     100          53 : dpdk_fragment_queue_init(void)
     101             : {
     102             :     unsigned int size;
     103             : 
     104          53 :     size = sizeof(struct per_cpu_fragment_queue) * vr_dpdk.nb_fwd_lcores;
     105          53 :     per_cpu_queues = vr_zalloc(size, VR_FRAGMENT_QUEUE_OBJECT);
     106             : 
     107          53 :     if (!per_cpu_queues) {
     108           0 :         RTE_LOG(ERR, VROUTER, "%s: Error allocating fragmentation queues\n",
     109             :                 __func__);
     110           0 :         return -ENOMEM;
     111             :     }
     112             : 
     113          53 :     return 0;
     114             : }
     115             : 
     116             : static void
     117          53 : dpdk_fragment_queue_exit(void)
     118             : {
     119             :     int i;
     120             : 
     121          53 :     if (!per_cpu_queues) {
     122           0 :         return;
     123             :     }
     124             : 
     125         159 :     for (i = 0; i < vr_dpdk.nb_fwd_lcores; ++i) {
     126         106 :         if (per_cpu_queues[i].queue.vfq_tail != NULL)
     127           0 :             vr_fragment_queue_free(&per_cpu_queues[i].queue);
     128             :     }
     129             : 
     130          53 :     vr_free(per_cpu_queues, VR_FRAGMENT_QUEUE_OBJECT);
     131          53 :     per_cpu_queues = NULL;
     132             : }
     133             : 
     134             : /** @} */
     135             : 
     136             : /**
     137             :  * @name Public functions
     138             :  * @{
     139             :  */
     140             : 
     141             : /**
     142             :  * Enqueue a packet to per cpu queue and schedule work to
     143             :  * enqueue/process it to assembler_table[]
     144             :  *
     145             :  * Executed only from the forwarding lcores.
     146             :  */
     147             : int
     148          21 : dpdk_fragment_assembler_enqueue(struct vrouter *router, struct vr_packet *pkt,
     149             :                                 struct vr_forwarding_md *fmd)
     150             : {
     151             :     int ret;
     152             :     unsigned int cpu;
     153             :     struct vr_dpdk_lcore *lcore;
     154             : 
     155          21 :     cpu = vr_get_cpu();
     156          21 :     if (cpu >= vr_num_cpus || cpu < VR_DPDK_FWD_LCORE_ID) {
     157           0 :         RTE_LOG(ERR, VROUTER, "%s:%d Enqueue to the assembler can only be "
     158             :                 "done on forwarding lcores, not on cpu %u\n",
     159             :                 __FUNCTION__, __LINE__, cpu);
     160           0 :         PKT_LOG(VP_DROP_FRAGMENTS, pkt, 0, VR_DPDK_FRAGMENT_ASSEMBLER_C, __LINE__);
     161           0 :         vr_pfree(pkt, VP_DROP_FRAGMENTS);
     162           0 :         return -EINVAL;
     163             :     }
     164             : 
     165             :     /* Enqueue the packet to per CPU queue */
     166          21 :     ret = vr_fragment_enqueue(router,
     167          21 :             &per_cpu_queues[cpu - VR_DPDK_FWD_LCORE_ID].queue, pkt, fmd);
     168             : 
     169          21 :     if (!ret) {
     170          21 :         lcore = vr_dpdk.lcores[cpu];
     171             :         /* Schedule work to enqueue/process by the assembler */
     172          21 :         vr_dpdk_lcore_schedule_assembler_work(lcore,
     173             :                 dpdk_fragment_assemble_queue,
     174          21 :                 &per_cpu_queues[cpu - VR_DPDK_FWD_LCORE_ID].queue);
     175             :     }
     176             : 
     177          21 :     return 0;
     178             : }
     179             : 
     180             : /**
     181             :  * Assemble packet
     182             :  */
     183             : void
     184          21 : dpdk_fragment_sync_assemble(struct vr_fragment_queue_element *vfqe)
     185             : {
     186             :     uint32_t hash, index;
     187             :     unsigned int cpu;
     188             :     struct fragment_bucket *bucket;
     189             : 
     190          21 :     cpu = vr_get_cpu() - VR_DPDK_FWD_LCORE_ID;
     191          21 :     assert(cpu >= 0 && cpu < (vr_num_cpus - VR_DPDK_FWD_LCORE_ID));
     192             : 
     193          21 :     hash = vr_fragment_get_hash(&vfqe->fqe_pnode);
     194          21 :     index = (hash % VR_ASSEMBLER_BUCKET_COUNT);
     195          21 :     bucket = &assembler_table[cpu][index];
     196             : 
     197          21 :     vr_fragment_assemble(&bucket->frag_list, vfqe);
     198          21 : }
     199             : 
     200             : /**
     201             :  * A callback for timeouts.
     202             :  * - Clean stale entries in assembler_table[][]
     203             :  *
     204             :  * Called on forwarding lcores only.
     205             :  */
     206             : void
     207     1621820 : dpdk_fragment_assembler_table_scan(void *arg)
     208             : {
     209     1621820 :     unsigned int i, j, scanned = 0;
     210             :     unsigned int cpu;
     211             :     struct fragment_bucket *vfb;
     212             : 
     213     1621820 :     cpu = vr_get_cpu() - VR_DPDK_FWD_LCORE_ID;
     214     1621176 :     assert(cpu >= 0 && cpu < (vr_num_cpus - VR_DPDK_FWD_LCORE_ID));
     215             : 
     216     1621180 :     i = assembler_scan_index;
     217  1648487869 :     for (j = 0; j < VR_ASSEMBLER_BUCKET_COUNT; j++) {
     218  1646866689 :         vfb = &assembler_table[cpu][(i + j) % VR_ASSEMBLER_BUCKET_COUNT];
     219             : 
     220  1646866689 :         if (vfb->frag_list)
     221        2369 :             scanned += vr_assembler_table_scan(&vfb->frag_list);
     222             : 
     223  1646866689 :         if (scanned > assembler_scan_thresh) {
     224           0 :             j++;
     225           0 :             break;
     226             :         }
     227             :     }
     228             : 
     229     1621180 :     assembler_scan_index = (i + j) % VR_ASSEMBLER_BUCKET_COUNT;
     230     1621180 :     return;
     231             : }
     232             : 
     233             : /**
     234             :  * Init the fragment assembler.
     235             :  *
     236             :  * Called only once during initialization on the master lcore only.
     237             :  */
     238             : int
     239          53 : dpdk_fragment_assembler_init(void)
     240             : {
     241             :     int ret;
     242             : 
     243          53 :     if ((ret = dpdk_fragment_queue_init()))
     244           0 :         return ret;
     245             : 
     246          53 :     if ((ret = dpdk_assembler_table_init()))
     247           0 :         return ret;
     248             : 
     249          53 :     return 0;
     250             : }
     251             : 
     252             : /**
     253             :  * Exit the fragment assembler and clean up all related data.
     254             :  *
     255             :  * Called only once on the master lcore after the forwarding lcores exited.
     256             :  * Therefore we can securely free everything inside dpdk_fragment_queue_exit()
     257             :  * and dpdk_assembler_table_exit(). If someone adds call to this in other
     258             :  * place, she/he has to take care of possible races between the master and
     259             :  * forwarding lcores as assembler tasks might be using the fragment table or
     260             :  * per cpu queues at the same time.
     261             :  */
     262             : void
     263          53 : dpdk_fragment_assembler_exit(void)
     264             : {
     265          53 :     dpdk_fragment_queue_exit();
     266          53 :     dpdk_assembler_table_exit();
     267          53 : }
     268             : 
     269             : /** @} */

Generated by: LCOV version 1.14