Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map arguments optimization #421

Merged
merged 13 commits into from
Jul 17, 2024
29 changes: 13 additions & 16 deletions bpfprogs/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,23 +452,20 @@ func (b *BPF) Start(ifaceName, direction string, chain bool) error {

// UpdateBPFMaps - Update the config ebpf maps via map arguments
func (b *BPF) UpdateBPFMaps(ifaceName, direction string) error {
for k, val := range b.Program.MapArgs {

if v, ok := val.(string); !ok {
err := fmt.Errorf("update map args is not a string for the ebpf program %s", b.Program.Name)
log.Error().Err(err).Msgf("failed to convert map args value into string for program %s", b.Program.Name)
return err
} else {
log.Info().Msgf("Update map args key %s val %s", k, v)

bpfMap, ok := b.BpfMaps[k]
if !ok {
if err := b.AddBPFMap(k); err != nil {
return err
}
bpfMap = b.BpfMaps[k]
for _, val := range b.Program.MapArgs {
bpfMap, ok := b.BpfMaps[val.Name]
if !ok {
if err := b.AddBPFMap(val.Name); err != nil {
return err
}
bpfMap.Update(v)
bpfMap = b.BpfMaps[val.Name]
}
for _, v := range val.Args {
log.Info().Msgf("Update map args key %v val %v", v.Key, v.Value)
bpfMap.Update(v.Key, v.Value)
}
if err := bpfMap.RemoveMissingKeys(val.Args); err != nil {
return fmt.Errorf("failed to remove missing entries of map %s with err %w", val.Name, err)
}
}
stats.Incr(stats.BPFUpdateCount, b.Program.Name, direction, ifaceName)
Expand Down
91 changes: 38 additions & 53 deletions bpfprogs/bpfmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ package bpfprogs

import (
"container/ring"
"errors"
"fmt"
"math"
"strconv"
"strings"
"unsafe"

"github.com/cilium/ebpf"
"github.com/l3af-project/l3afd/v2/models"
"github.com/rs/zerolog/log"
)

Expand All @@ -33,66 +33,51 @@ type MetricsBPFMap struct {
lastValue float64
}

// The update function is used to update eBPF maps, which are used by network functions.
// Supported types are Array and Hash
// Multiple values are comma separated
// Hashmap can be multiple values or single values.
// If hash map entries then key will be values and value will be set to 1
// In case of Array then key will be index starting from 0 and values are stored.
// for e.g.
//
// HashMap scenario 1. --ports="80,443" values are stored in rl_ports_map BPF map
// key => 80 value => 1
// key => 443 value => 1
// HashMap scenario 2. --ports="443" value is stored in rl_ports_map BPF map
// key => 443 value => 1
// Array scenario 1. --ports="80,443" values are stored in rl_ports_map BPF map
// key => 0 value => 80
// key => 1 value => 443
// Array scenario 2. --rate="10000" value is stored in rl_config_map BPF map
// key => 0 value => 10000
func (b *BPFMap) Update(value string) error {

log.Debug().Msgf("update map name %s ID %d", b.Name, b.MapID)
// The RemoveMissingKeys function is used to delete missing entries of eBPF maps, which are used by eBPF Programs.
func (b *BPFMap) RemoveMissingKeys(args []models.KeyValue) error {
ebpfMap, err := ebpf.NewMapFromID(b.MapID)
if err != nil {
return fmt.Errorf("access new map from ID failed %w", err)
}
defer ebpfMap.Close()

// check values are single or multiple
s := strings.Split(value, ",")

if b.Type == ebpf.Hash {
// clear map elements
key := 0
val := 0
entries := ebpfMap.Iterate()
for entries.Next(unsafe.Pointer(&key), unsafe.Pointer(&val)) {
// Order of keys is non-deterministic due to randomized map seed
if err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {
log.Warn().Err(err).Msgf("delete hash map for key %d failed", key)
}
}

for key, val := range s {
v, _ := strconv.ParseInt(val, 10, 64)
x := 1
log.Info().Msgf("updating map %s key %d mapid %d", b.Name, v, b.MapID)
if err := ebpfMap.Update(unsafe.Pointer(&v), unsafe.Pointer(&x), 0); err != nil {
return fmt.Errorf("update hash map element failed for key %d error %w", key, err)
KeyValueMap := make(map[int]bool, len(args))
for _, k := range args {
KeyValueMap[k.Key] = true
}
var key, nextKey int
for {
err := ebpfMap.NextKey(unsafe.Pointer(&key), unsafe.Pointer(&nextKey))
if err != nil {
if errors.Is(err, ebpf.ErrKeyNotExist) {
break
} else {
return fmt.Errorf("get next key failed with error %w, mapid %d", err, b.MapID)
}
}
} else if b.Type == ebpf.Array {
for key, val := range s {
v, _ := strconv.ParseInt(val, 10, 64)
log.Info().Msgf("updating map %s key %d mapid %d", b.Name, v, b.MapID)
if err := ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&v), 0); err != nil {
return fmt.Errorf("update array map index %d %w", key, err)
key = nextKey
_, IsKeyExists := KeyValueMap[key]
if !IsKeyExists {
log.Info().Msgf("removing key %v because it is missing\n", key)
if err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {
return fmt.Errorf("delete key failed with error %w, mapid %d", err, b.MapID)
}
}
} else {
return fmt.Errorf("unsupported map type")
}
return nil
}

// The update function is used to update eBPF maps, which are used by network functions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The update function is used to update eBPF maps, which are used by network functions.
// The update function is used to update eBPF maps, which are used by ebpf programs

func (b *BPFMap) Update(key, value int) error {

log.Debug().Msgf("update map name %s ID %d", b.Name, b.MapID)
ebpfMap, err := ebpf.NewMapFromID(b.MapID)
if err != nil {
return fmt.Errorf("access new map from ID failed %w", err)
}
defer ebpfMap.Close()
log.Info().Msgf("updating map %s key %d mapid %d", b.Name, key, b.MapID)
if err := ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), 0); err != nil {
return fmt.Errorf("update hash map element failed for key %d error %w", key, err)
}
return nil
}
Expand Down
14 changes: 13 additions & 1 deletion models/l3afd.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type BPFProgram struct {
StopArgs L3afDNFArgs `json:"stop_args"` // Map of arguments to stop command
StatusArgs L3afDNFArgs `json:"status_args"` // Map of arguments to status command
UpdateArgs L3afDNFArgs `json:"update_args"` // Map of arguments to update command
MapArgs L3afDNFArgs `json:"map_args"` // Config BPF Map of arguments
MapArgs []L3afDMapArg `json:"map_args"` // Config BPF Map of arguments
ConfigArgs L3afDNFArgs `json:"config_args"` // Map of arguments to config command
MonitorMaps []L3afDNFMetricsMap `json:"monitor_maps"` // Metrics BPF maps
EPRURL string `json:"ebpf_package_repo_url"` // Download url for Program
Expand All @@ -65,6 +65,18 @@ type L3afDNFMetricsMap struct {
Aggregator string `json:"aggregator"` // Aggregation function names
}

// KeyValue defines struct for key and value
type KeyValue struct {
Key int `json:"key"` // Key
Value int `json:"value"` // Value
}

// L3afDMapArg defines map arg
type L3afDMapArg struct {
Name string `json:"name"` // BPF map name
Args []KeyValue `json:"args"` // BPF map arguments
}

// L3afBPFPrograms defines configs for a node
type L3afBPFPrograms struct {
HostName string `json:"host_name"` // Host name or pod name
Expand Down
Loading