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 }