LCOV - code coverage report
Current view: top level - bpfilter/cgen - swich.c (source / functions) Coverage Total Hit
Test: lcov.out Lines: 100.0 % 72 72
Test Date: 2025-06-20 13:16:12 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-2.0-only */
       2              : /*
       3              :  * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
       4              :  */
       5              : 
       6              : #include "bpfilter/cgen/swich.h"
       7              : 
       8              : #include <linux/bpf.h>
       9              : #include <linux/bpf_common.h>
      10              : 
      11              : #include <errno.h>
      12              : #include <stddef.h>
      13              : #include <stdint.h>
      14              : #include <stdlib.h>
      15              : #include <string.h>
      16              : 
      17              : #include "bpfilter/cgen/jmp.h"
      18              : #include "bpfilter/cgen/program.h"
      19              : #include "core/helper.h"
      20              : #include "core/list.h"
      21              : #include "core/logger.h"
      22              : 
      23              : #include "external/filter.h"
      24              : 
      25              : /// Cleanup attribute for a @ref bf_swich_option variable.
      26              : #define _free_bf_swich_option_ __attribute__((cleanup(_bf_swich_option_free)))
      27              : 
      28              : /**
      29              :  * @struct bf_swich_option
      30              :  *
      31              :  * Represent a @c case of the switch: contains the instructions to execute if
      32              :  * the switch's register is equal to a specific value.
      33              :  */
      34              : struct bf_swich_option
      35              : {
      36              :     /// Immediate value to match against the switch's register.
      37              :     uint32_t imm;
      38              :     /// Jump context used to jump from the comparison to the bytecode to
      39              :     /// execute, then to the end of the switch.
      40              :     struct bf_jmpctx jmp;
      41              :     /// Number of instructions for this option.
      42              :     size_t insns_len;
      43              :     /// Instructions to execute, as a flexible array member.
      44              :     struct bpf_insn insns[];
      45              : };
      46              : 
      47              : static void _bf_swich_option_free(struct bf_swich_option **option);
      48              : 
      49              : /**
      50              :  * Allocate and initialize a new switch option (case).
      51              :  *
      52              :  * @param option Switch option to allocate and initialize. Can't be NULL.
      53              :  * @param imm Immediate value to match against the switch's register.
      54              :  * @param insns Instructions to execute if the option matches.
      55              :  * @param insns_len Number of instructions in @p insns .
      56              :  * @return 0 on success, or negative errno value on failure.
      57              :  */
      58           13 : static int _bf_swich_option_new(struct bf_swich_option **option, uint32_t imm,
      59              :                                 const struct bpf_insn *insns, size_t insns_len)
      60              : {
      61           11 :     _free_bf_swich_option_ struct bf_swich_option *_option = NULL;
      62              : 
      63           13 :     bf_assert(option);
      64           12 :     bf_assert(insns);
      65              : 
      66           11 :     _option = malloc(sizeof(*_option) + (sizeof(struct bpf_insn) * insns_len));
      67           11 :     if (!_option)
      68              :         return -ENOMEM;
      69              : 
      70           11 :     _option->imm = imm;
      71           11 :     _option->insns_len = insns_len;
      72           11 :     memcpy(_option->insns, insns, sizeof(struct bpf_insn) * insns_len);
      73              : 
      74           11 :     *option = TAKE_PTR(_option);
      75              : 
      76           11 :     return 0;
      77              : }
      78              : 
      79              : /**
      80              :  * Free a switch option.
      81              :  *
      82              :  * Free @p option is it points to valid memory. If @p option points to a NULL
      83              :  * pointer, nothing is done.
      84              :  *
      85              :  * @param option Option to free. Can't be NULL.
      86              :  */
      87           36 : static void _bf_swich_option_free(struct bf_swich_option **option)
      88              : {
      89           36 :     bf_assert(option);
      90              : 
      91           35 :     if (!*option)
      92              :         return;
      93              : 
      94           11 :     free(*option);
      95           11 :     *option = NULL;
      96              : }
      97              : 
      98            5 : int bf_swich_init(struct bf_swich *swich, struct bf_program *program, int reg)
      99              : {
     100            5 :     bf_assert(swich);
     101            4 :     bf_assert(program);
     102              : 
     103            3 :     swich->program = program;
     104            3 :     swich->reg = reg;
     105              : 
     106            3 :     bf_list_init(
     107              :         &swich->options,
     108            3 :         (bf_list_ops[]) {{.free = (bf_list_ops_free)_bf_swich_option_free}});
     109              : 
     110            3 :     swich->default_opt = NULL;
     111              : 
     112            3 :     return 0;
     113              : }
     114              : 
     115            7 : void bf_swich_cleanup(struct bf_swich *swich)
     116              : {
     117            7 :     bf_assert(swich);
     118              : 
     119            6 :     bf_list_clean(&swich->options);
     120            6 :     _bf_swich_option_free(&swich->default_opt);
     121            6 :     free(swich->default_opt);
     122            6 : }
     123              : 
     124            6 : int bf_swich_add_option(struct bf_swich *swich, uint32_t imm,
     125              :                         const struct bpf_insn *insns, size_t insns_len)
     126              : {
     127            6 :     _free_bf_swich_option_ struct bf_swich_option *option = NULL;
     128              :     int r;
     129              : 
     130            6 :     bf_assert(swich);
     131            6 :     bf_assert(insns);
     132              : 
     133            6 :     r = _bf_swich_option_new(&option, imm, insns, insns_len);
     134            6 :     if (r)
     135              :         return r;
     136              : 
     137            6 :     r = bf_list_add_tail(&swich->options, option);
     138            6 :     if (r)
     139              :         return r;
     140              : 
     141            6 :     TAKE_PTR(option);
     142              : 
     143            6 :     return 0;
     144              : }
     145              : 
     146            2 : int bf_swich_set_default(struct bf_swich *swich, const struct bpf_insn *insns,
     147              :                          size_t insns_len)
     148              : {
     149            2 :     _free_bf_swich_option_ struct bf_swich_option *option = NULL;
     150              :     int r;
     151              : 
     152            2 :     bf_assert(swich);
     153            2 :     bf_assert(insns);
     154              : 
     155            2 :     if (swich->default_opt) {
     156            1 :         bf_warn("default bf_swich option already exists, replacing it");
     157            1 :         _bf_swich_option_free(&swich->default_opt);
     158              :     }
     159              : 
     160            2 :     r = _bf_swich_option_new(&option, 0, insns, insns_len);
     161            2 :     if (r)
     162              :         return r;
     163              : 
     164            2 :     swich->default_opt = TAKE_PTR(option);
     165              : 
     166            2 :     return 0;
     167              : }
     168              : 
     169            2 : int bf_swich_generate(struct bf_swich *swich)
     170              : {
     171            2 :     struct bf_program *program = swich->program;
     172              : 
     173              :     // Match an option against the value and jump to the related bytecode
     174           14 :     bf_list_foreach (&swich->options, option_node) {
     175            6 :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     176              : 
     177            6 :         option->jmp = bf_jmpctx_get(
     178              :             program, BPF_JMP_IMM(BPF_JEQ, swich->reg, option->imm, 0));
     179              :     }
     180              : 
     181              :     // Insert the default option if any
     182            2 :     if (swich->default_opt)
     183            1 :         swich->default_opt->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
     184              : 
     185              :     // Insert each option's bytecode
     186           14 :     bf_list_foreach (&swich->options, option_node) {
     187            6 :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     188              : 
     189            6 :         bf_jmpctx_cleanup(&option->jmp);
     190           18 :         for (size_t i = 0; i < option->insns_len; ++i)
     191           12 :             EMIT(program, option->insns[i]);
     192            6 :         option->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
     193              :     }
     194              : 
     195              :     // Insert the default instructions
     196            2 :     if (swich->default_opt) {
     197            1 :         bf_jmpctx_cleanup(&swich->default_opt->jmp);
     198            4 :         for (size_t i = 0; i < swich->default_opt->insns_len; ++i)
     199            3 :             EMIT(program, swich->default_opt->insns[i]);
     200              :     }
     201              : 
     202           16 :     bf_list_foreach (&swich->options, option_node) {
     203            6 :         struct bf_swich_option *option = bf_list_node_get_data(option_node);
     204              : 
     205            6 :         bf_jmpctx_cleanup(&option->jmp);
     206              :     }
     207              : 
     208              :     return 0;
     209              : }
        

Generated by: LCOV version 2.0-1