github.com/cilium/cilium@v1.16.2/pkg/ebpf/map.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ebpf
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	ciliumebpf "github.com/cilium/ebpf"
    13  
    14  	"github.com/cilium/cilium/api/v1/models"
    15  	"github.com/cilium/cilium/pkg/bpf"
    16  	"github.com/cilium/cilium/pkg/lock"
    17  	"github.com/cilium/cilium/pkg/metrics"
    18  )
    19  
    20  type MapSpec = ciliumebpf.MapSpec
    21  
    22  type PinType = ciliumebpf.PinType
    23  
    24  const (
    25  	Hash       = ciliumebpf.Hash
    26  	PerCPUHash = ciliumebpf.PerCPUHash
    27  	Array      = ciliumebpf.Array
    28  	HashOfMaps = ciliumebpf.HashOfMaps
    29  	LPMTrie    = ciliumebpf.LPMTrie
    30  	LRUHash    = ciliumebpf.LRUHash
    31  
    32  	PinNone   = ciliumebpf.PinNone
    33  	PinByName = ciliumebpf.PinByName
    34  )
    35  
    36  var (
    37  	ErrKeyNotExist = ciliumebpf.ErrKeyNotExist
    38  )
    39  
    40  // IterateCallback represents the signature of the callback function expected by
    41  // the IterateWithCallback method, which in turn is used to iterate all the
    42  // keys/values of a map.
    43  type IterateCallback func(key, value interface{})
    44  
    45  // Map represents an eBPF map.
    46  type Map struct {
    47  	lock lock.RWMutex
    48  	*ciliumebpf.Map
    49  
    50  	spec *MapSpec
    51  	path string
    52  }
    53  
    54  // NewMap creates a new Map object.
    55  func NewMap(spec *MapSpec) *Map {
    56  	return &Map{
    57  		spec: spec,
    58  	}
    59  }
    60  
    61  // LoadRegisterMap loads the specified map from a bpffs pin path and registers
    62  // its handle in the package-global map register.
    63  func LoadRegisterMap(mapName string) (*Map, error) {
    64  	path := bpf.MapPath(mapName)
    65  
    66  	m, err := LoadPinnedMap(path)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	registerMap(m)
    72  
    73  	return m, nil
    74  }
    75  
    76  // LoadPinnedMap wraps cilium/ebpf's LoadPinnedMap.
    77  func LoadPinnedMap(fileName string) (*Map, error) {
    78  	m, err := ciliumebpf.LoadPinnedMap(fileName, nil)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return &Map{
    84  		Map:  m,
    85  		path: fileName,
    86  	}, nil
    87  }
    88  
    89  func MapFromID(id int) (*Map, error) {
    90  	newMap, err := ciliumebpf.NewMapFromID(ciliumebpf.MapID(id))
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	return &Map{
    96  		Map: newMap,
    97  	}, nil
    98  }
    99  
   100  // OpenOrCreate tries to open or create the eBPF map identified by the spec in
   101  // the Map object.
   102  func (m *Map) OpenOrCreate() error {
   103  	m.lock.Lock()
   104  	defer m.lock.Unlock()
   105  
   106  	if m.Map != nil {
   107  		return nil
   108  	}
   109  
   110  	if m.spec == nil {
   111  		return fmt.Errorf("cannot create map: nil map spec")
   112  	}
   113  
   114  	opts := ciliumebpf.MapOptions{
   115  		PinPath: bpf.TCGlobalsPath(),
   116  	}
   117  
   118  	m.spec.Flags |= bpf.GetPreAllocateMapFlags(m.spec.Type)
   119  
   120  	path := bpf.MapPath(m.spec.Name)
   121  
   122  	if m.spec.Pinning == ciliumebpf.PinByName {
   123  		mapDir := filepath.Dir(path)
   124  
   125  		if _, err := os.Stat(mapDir); os.IsNotExist(err) {
   126  			if err = os.MkdirAll(mapDir, 0755); err != nil {
   127  				return &os.PathError{
   128  					Op:   "Unable create map base directory",
   129  					Path: path,
   130  					Err:  err,
   131  				}
   132  			}
   133  		}
   134  	}
   135  
   136  	newMap, err := ciliumebpf.NewMapWithOptions(m.spec, opts)
   137  	if err != nil {
   138  		if !errors.Is(err, ciliumebpf.ErrMapIncompatible) {
   139  			return fmt.Errorf("unable to create map: %w", err)
   140  		}
   141  
   142  		// There already exists a pinned map but it has a different
   143  		// configuration (e.g different type, k/v size or flags).
   144  		// Try to delete and recreate it.
   145  
   146  		log.WithField("map", m.spec.Name).
   147  			WithError(err).Warn("Removing map to allow for property upgrade (expect map data loss)")
   148  
   149  		oldMap, err := ciliumebpf.LoadPinnedMap(path, &opts.LoadPinOptions)
   150  		if err != nil {
   151  			return fmt.Errorf("cannot load pinned map %s: %w", m.spec.Name, err)
   152  		}
   153  		defer func() {
   154  			if err := oldMap.Close(); err != nil {
   155  				log.WithField("map", m.spec.Name).Warnf("Cannot close map: %v", err)
   156  			}
   157  		}()
   158  
   159  		if err = oldMap.Unpin(); err != nil {
   160  			return fmt.Errorf("cannot unpin map %s: %w", m.spec.Name, err)
   161  		}
   162  
   163  		newMap, err = ciliumebpf.NewMapWithOptions(m.spec, opts)
   164  		if err != nil {
   165  			return fmt.Errorf("unable to create map: %w", err)
   166  		}
   167  	}
   168  
   169  	m.Map = newMap
   170  	m.path = path
   171  
   172  	registerMap(m)
   173  	metrics.UpdateMapCapacity(m.spec.Name, m.spec.MaxEntries)
   174  	return nil
   175  }
   176  
   177  // IterateWithCallback iterates through all the keys/values of a map, passing
   178  // each key/value pair to the cb callback.
   179  func (m *Map) IterateWithCallback(key, value interface{}, cb IterateCallback) error {
   180  	if m.Map == nil {
   181  		if err := m.OpenOrCreate(); err != nil {
   182  			return err
   183  		}
   184  	}
   185  
   186  	m.lock.RLock()
   187  	defer m.lock.RUnlock()
   188  
   189  	entries := m.Iterate()
   190  	for entries.Next(key, value) {
   191  		cb(key, value)
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  // GetModel returns a BPF map in the representation served via the API.
   198  func (m *Map) GetModel() *models.BPFMap {
   199  	m.lock.RLock()
   200  	defer m.lock.RUnlock()
   201  
   202  	mapModel := &models.BPFMap{
   203  		Path: m.path,
   204  	}
   205  
   206  	// TODO: handle map cache. See pkg/bpf/map_linux.go:GetModel()
   207  
   208  	return mapModel
   209  }