github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/marshalers.go (about)

     1  package ebpf
     2  
     3  import (
     4  	"encoding"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"slices"
     9  	"unsafe"
    10  
    11  	"github.com/cilium/ebpf/internal"
    12  	"github.com/cilium/ebpf/internal/sys"
    13  	"github.com/cilium/ebpf/internal/sysenc"
    14  )
    15  
    16  // marshalMapSyscallInput converts an arbitrary value into a pointer suitable
    17  // to be passed to the kernel.
    18  //
    19  // As an optimization, it returns the original value if it is an
    20  // unsafe.Pointer.
    21  func marshalMapSyscallInput(data any, length int) (sys.Pointer, error) {
    22  	if ptr, ok := data.(unsafe.Pointer); ok {
    23  		return sys.NewPointer(ptr), nil
    24  	}
    25  
    26  	buf, err := sysenc.Marshal(data, length)
    27  	if err != nil {
    28  		return sys.Pointer{}, err
    29  	}
    30  
    31  	return buf.Pointer(), nil
    32  }
    33  
    34  func makeMapSyscallOutput(dst any, length int) sysenc.Buffer {
    35  	if ptr, ok := dst.(unsafe.Pointer); ok {
    36  		return sysenc.UnsafeBuffer(ptr)
    37  	}
    38  
    39  	_, ok := dst.(encoding.BinaryUnmarshaler)
    40  	if ok {
    41  		return sysenc.SyscallOutput(nil, length)
    42  	}
    43  
    44  	return sysenc.SyscallOutput(dst, length)
    45  }
    46  
    47  // appendPerCPUSlice encodes a slice containing one value per
    48  // possible CPU into a buffer of bytes.
    49  //
    50  // Values are initialized to zero if the slice has less elements than CPUs.
    51  func appendPerCPUSlice(buf []byte, slice any, possibleCPUs, elemLength, alignedElemLength int) ([]byte, error) {
    52  	sliceType := reflect.TypeOf(slice)
    53  	if sliceType.Kind() != reflect.Slice {
    54  		return nil, errors.New("per-CPU value requires slice")
    55  	}
    56  
    57  	sliceValue := reflect.ValueOf(slice)
    58  	sliceLen := sliceValue.Len()
    59  	if sliceLen > possibleCPUs {
    60  		return nil, fmt.Errorf("per-CPU value greater than number of CPUs")
    61  	}
    62  
    63  	// Grow increases the slice's capacity, _if_necessary_
    64  	buf = slices.Grow(buf, alignedElemLength*possibleCPUs)
    65  	for i := 0; i < sliceLen; i++ {
    66  		elem := sliceValue.Index(i).Interface()
    67  		elemBytes, err := sysenc.Marshal(elem, elemLength)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  
    72  		buf = elemBytes.AppendTo(buf)
    73  		buf = append(buf, make([]byte, alignedElemLength-elemLength)...)
    74  	}
    75  
    76  	// Ensure buf is zero-padded full size.
    77  	buf = append(buf, make([]byte, (possibleCPUs-sliceLen)*alignedElemLength)...)
    78  
    79  	return buf, nil
    80  }
    81  
    82  // marshalPerCPUValue encodes a slice containing one value per
    83  // possible CPU into a buffer of bytes.
    84  //
    85  // Values are initialized to zero if the slice has less elements than CPUs.
    86  func marshalPerCPUValue(slice any, elemLength int) (sys.Pointer, error) {
    87  	possibleCPUs, err := PossibleCPU()
    88  	if err != nil {
    89  		return sys.Pointer{}, err
    90  	}
    91  
    92  	alignedElemLength := internal.Align(elemLength, 8)
    93  	buf := make([]byte, 0, alignedElemLength*possibleCPUs)
    94  	buf, err = appendPerCPUSlice(buf, slice, possibleCPUs, elemLength, alignedElemLength)
    95  	if err != nil {
    96  		return sys.Pointer{}, err
    97  	}
    98  
    99  	return sys.NewSlicePointer(buf), nil
   100  }
   101  
   102  // marshalBatchPerCPUValue encodes a batch-sized slice of slices containing
   103  // one value per possible CPU into a buffer of bytes.
   104  func marshalBatchPerCPUValue(slice any, batchLen, elemLength int) ([]byte, error) {
   105  	sliceType := reflect.TypeOf(slice)
   106  	if sliceType.Kind() != reflect.Slice {
   107  		return nil, fmt.Errorf("batch value requires a slice")
   108  	}
   109  	sliceValue := reflect.ValueOf(slice)
   110  
   111  	possibleCPUs, err := PossibleCPU()
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	if sliceValue.Len() != batchLen*possibleCPUs {
   116  		return nil, fmt.Errorf("per-CPU slice has incorrect length, expected %d, got %d",
   117  			batchLen*possibleCPUs, sliceValue.Len())
   118  	}
   119  	alignedElemLength := internal.Align(elemLength, 8)
   120  	buf := make([]byte, 0, batchLen*alignedElemLength*possibleCPUs)
   121  	for i := 0; i < batchLen; i++ {
   122  		batch := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface()
   123  		buf, err = appendPerCPUSlice(buf, batch, possibleCPUs, elemLength, alignedElemLength)
   124  		if err != nil {
   125  			return nil, fmt.Errorf("batch %d: %w", i, err)
   126  		}
   127  	}
   128  	return buf, nil
   129  }
   130  
   131  // unmarshalPerCPUValue decodes a buffer into a slice containing one value per
   132  // possible CPU.
   133  //
   134  // slice must be a literal slice and not a pointer.
   135  func unmarshalPerCPUValue(slice any, elemLength int, buf []byte) error {
   136  	sliceType := reflect.TypeOf(slice)
   137  	if sliceType.Kind() != reflect.Slice {
   138  		return fmt.Errorf("per-CPU value requires a slice")
   139  	}
   140  
   141  	possibleCPUs, err := PossibleCPU()
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	sliceValue := reflect.ValueOf(slice)
   147  	if sliceValue.Len() != possibleCPUs {
   148  		return fmt.Errorf("per-CPU slice has incorrect length, expected %d, got %d",
   149  			possibleCPUs, sliceValue.Len())
   150  	}
   151  
   152  	sliceElemType := sliceType.Elem()
   153  	sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
   154  	stride := internal.Align(elemLength, 8)
   155  	for i := 0; i < possibleCPUs; i++ {
   156  		var elem any
   157  		v := sliceValue.Index(i)
   158  		if sliceElemIsPointer {
   159  			if !v.Elem().CanAddr() {
   160  				return fmt.Errorf("per-CPU slice elements cannot be nil")
   161  			}
   162  			elem = v.Elem().Addr().Interface()
   163  		} else {
   164  			elem = v.Addr().Interface()
   165  		}
   166  		err := sysenc.Unmarshal(elem, buf[:elemLength])
   167  		if err != nil {
   168  			return fmt.Errorf("cpu %d: %w", i, err)
   169  		}
   170  
   171  		buf = buf[stride:]
   172  	}
   173  	return nil
   174  }
   175  
   176  // unmarshalBatchPerCPUValue decodes a buffer into a batch-sized slice
   177  // containing one value per possible CPU.
   178  //
   179  // slice must have length batchLen * PossibleCPUs().
   180  func unmarshalBatchPerCPUValue(slice any, batchLen, elemLength int, buf []byte) error {
   181  	sliceType := reflect.TypeOf(slice)
   182  	if sliceType.Kind() != reflect.Slice {
   183  		return fmt.Errorf("batch requires a slice")
   184  	}
   185  
   186  	sliceValue := reflect.ValueOf(slice)
   187  	possibleCPUs, err := PossibleCPU()
   188  	if err != nil {
   189  		return err
   190  	}
   191  	if sliceValue.Len() != batchLen*possibleCPUs {
   192  		return fmt.Errorf("per-CPU slice has incorrect length, expected %d, got %d",
   193  			sliceValue.Len(), batchLen*possibleCPUs)
   194  	}
   195  
   196  	fullValueSize := possibleCPUs * internal.Align(elemLength, 8)
   197  	if len(buf) != batchLen*fullValueSize {
   198  		return fmt.Errorf("input buffer has incorrect length, expected %d, got %d",
   199  			len(buf), batchLen*fullValueSize)
   200  	}
   201  
   202  	for i := 0; i < batchLen; i++ {
   203  		elem := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface()
   204  		if err := unmarshalPerCPUValue(elem, elemLength, buf[:fullValueSize]); err != nil {
   205  			return fmt.Errorf("batch %d: %w", i, err)
   206  		}
   207  		buf = buf[fullValueSize:]
   208  	}
   209  	return nil
   210  }