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

Generated by: LCOV version 2.0-1