...

Source file src/github.com/cilium/ebpf/features/map.go

Documentation: github.com/cilium/ebpf/features

     1  package features
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  	"unsafe"
     9  
    10  	"github.com/cilium/ebpf"
    11  	"github.com/cilium/ebpf/internal/sys"
    12  	"github.com/cilium/ebpf/internal/unix"
    13  )
    14  
    15  func init() {
    16  	mc.mapTypes = make(map[ebpf.MapType]error)
    17  }
    18  
    19  var (
    20  	mc mapCache
    21  )
    22  
    23  type mapCache struct {
    24  	sync.Mutex
    25  	mapTypes map[ebpf.MapType]error
    26  }
    27  
    28  func createMapTypeAttr(mt ebpf.MapType) *sys.MapCreateAttr {
    29  	var (
    30  		keySize        uint32 = 4
    31  		valueSize      uint32 = 4
    32  		maxEntries     uint32 = 1
    33  		innerMapFd     uint32
    34  		flags          uint32
    35  		btfKeyTypeID   uint32
    36  		btfValueTypeID uint32
    37  		btfFd          uint32
    38  	)
    39  
    40  	// switch on map types to generate correct MapCreateAttr
    41  	switch mt {
    42  	case ebpf.StackTrace:
    43  		// valueSize needs to be sizeof(uint64)
    44  		valueSize = 8
    45  	case ebpf.LPMTrie:
    46  		// keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8
    47  		// BPF_F_NO_PREALLOC needs to be set
    48  		// checked at allocation time for lpm_trie maps
    49  		keySize = 8
    50  		valueSize = 8
    51  		flags = unix.BPF_F_NO_PREALLOC
    52  	case ebpf.ArrayOfMaps, ebpf.HashOfMaps:
    53  		// assign invalid innerMapFd to pass validation check
    54  		// will return EBADF
    55  		innerMapFd = ^uint32(0)
    56  	case ebpf.CGroupStorage, ebpf.PerCPUCGroupStorage:
    57  		// keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16)
    58  		// by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs
    59  		// checked at allocation time
    60  		var align int
    61  		keySize = uint32(8 + unsafe.Sizeof(align))
    62  		maxEntries = 0
    63  	case ebpf.Queue, ebpf.Stack:
    64  		// keySize needs to be 0, see alloc_check for queue and stack maps
    65  		keySize = 0
    66  	case ebpf.RingBuf:
    67  		// keySize and valueSize need to be 0
    68  		// maxEntries needs to be power of 2 and PAGE_ALIGNED
    69  		// checked at allocation time
    70  		keySize = 0
    71  		valueSize = 0
    72  		maxEntries = uint32(os.Getpagesize())
    73  	case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage:
    74  		// maxEntries needs to be 0
    75  		// BPF_F_NO_PREALLOC needs to be set
    76  		// btf* fields need to be set
    77  		// see alloc_check for local_storage map types
    78  		maxEntries = 0
    79  		flags = unix.BPF_F_NO_PREALLOC
    80  		btfKeyTypeID = 1   // BTF_KIND_INT
    81  		btfValueTypeID = 3 // BTF_KIND_ARRAY
    82  		btfFd = ^uint32(0)
    83  	}
    84  
    85  	return &sys.MapCreateAttr{
    86  		MapType:        sys.MapType(mt),
    87  		KeySize:        keySize,
    88  		ValueSize:      valueSize,
    89  		MaxEntries:     maxEntries,
    90  		InnerMapFd:     innerMapFd,
    91  		MapFlags:       flags,
    92  		BtfKeyTypeId:   btfKeyTypeID,
    93  		BtfValueTypeId: btfValueTypeID,
    94  		BtfFd:          btfFd,
    95  	}
    96  }
    97  
    98  // HaveMapType probes the running kernel for the availability of the specified map type.
    99  //
   100  // See the package documentation for the meaning of the error return value.
   101  func HaveMapType(mt ebpf.MapType) error {
   102  	if err := validateMaptype(mt); err != nil {
   103  		return err
   104  	}
   105  
   106  	return haveMapType(mt)
   107  }
   108  
   109  func validateMaptype(mt ebpf.MapType) error {
   110  	if mt > mt.Max() {
   111  		return os.ErrInvalid
   112  	}
   113  
   114  	if mt == ebpf.StructOpsMap {
   115  		// A probe for StructOpsMap has vmlinux BTF requirements we currently
   116  		// cannot meet. Once we figure out how to add a working probe in this
   117  		// package, we can remove this check.
   118  		return errors.New("a probe for MapType StructOpsMap isn't implemented")
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  func haveMapType(mt ebpf.MapType) error {
   125  	mc.Lock()
   126  	defer mc.Unlock()
   127  	err, ok := mc.mapTypes[mt]
   128  	if ok {
   129  		return err
   130  	}
   131  
   132  	fd, err := sys.MapCreate(createMapTypeAttr(mt))
   133  
   134  	switch {
   135  	// For nested and storage map types we accept EBADF as indicator that these maps are supported
   136  	case errors.Is(err, unix.EBADF):
   137  		if isMapOfMaps(mt) || isStorageMap(mt) {
   138  			err = nil
   139  		}
   140  
   141  	// EINVAL occurs when attempting to create a map with an unknown type.
   142  	// E2BIG occurs when MapCreateAttr contains non-zero bytes past the end
   143  	// of the struct known by the running kernel, meaning the kernel is too old
   144  	// to support the given map type.
   145  	case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
   146  		err = ebpf.ErrNotSupported
   147  
   148  	// EPERM is kept as-is and is not converted or wrapped.
   149  	case errors.Is(err, unix.EPERM):
   150  		break
   151  
   152  	// Wrap unexpected errors.
   153  	case err != nil:
   154  		err = fmt.Errorf("unexpected error during feature probe: %w", err)
   155  
   156  	default:
   157  		fd.Close()
   158  	}
   159  
   160  	mc.mapTypes[mt] = err
   161  
   162  	return err
   163  }
   164  
   165  func isMapOfMaps(mt ebpf.MapType) bool {
   166  	switch mt {
   167  	case ebpf.ArrayOfMaps, ebpf.HashOfMaps:
   168  		return true
   169  	}
   170  	return false
   171  }
   172  
   173  func isStorageMap(mt ebpf.MapType) bool {
   174  	switch mt {
   175  	case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage:
   176  		return true
   177  	}
   178  
   179  	return false
   180  }
   181  

View as plain text