Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/unit-test-on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ jobs:
# https://github.com/cilium/ci-kernels/pkgs/container/ci-kernels/versions?filters%5Bversion_type%5D=tagged

# AMD64
- { target_arch: amd64, kernel: 5.4.276 }
- { target_arch: amd64, kernel: 5.10.217 }
- { target_arch: amd64, kernel: 5.15.159 }
- { target_arch: amd64, kernel: 6.1.91 }
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ slack channel for discussions and questions.

## Pre-requisites

- Linux (5.4+ for x86-64, 5.5+ for ARM64) with eBPF enabled (the profiler currently only runs on Linux)
- Linux (5.10+) with eBPF enabled (the profiler currently only runs on Linux)
- Go as specified in [go.mod](https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/main/go.mod)
- docker
- Rust as specified in [Cargo.toml](https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/main/Cargo.toml)
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ Since the profiler is Linux-only, macOS and Windows users need to set up a Linux

## Supported Linux kernel version

[7ddc23ea](https://github.com/open-telemetry/opentelemetry-ebpf-profiler/commit/7ddc23ea135a2e00fffc17850ab90534e9b63108) is the last commit with support for 4.19. Changes after this commit may require a minimal Linux kernel version of 5.4.
The minimum required Linux kernel version has increased with certain commits. Specifically:

- Commit [8047150e](https://github.com/open-telemetry/opentelemetry-ebpf-profiler/commit/8047150e3f325f852874591356c69d0487b67d7c) was the last to support kernel version 5.4. Subsequent changes may require a minimal Linux kernel version of 5.10 or greater.
- Commit [7ddc23ea](https://github.com/open-telemetry/opentelemetry-ebpf-profiler/commit/7ddc23ea135a2e00fffc17850ab90534e9b63108) was the last to support kernel version 4.19. Subsequent changes may require a minimal Linux kernel version of at least 5.4.

### Updating the supported Linux kernel version

Expand Down
13 changes: 1 addition & 12 deletions collector/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package config // import "go.opentelemetry.io/ebpf-profiler/collector/config"
import (
"errors"
"fmt"
"runtime"
"strings"
"time"

Expand Down Expand Up @@ -122,17 +121,7 @@ func (cfg *Config) Validate() error {
}

var minMajor, minMinor uint32
switch runtime.GOARCH {
case "amd64":
minMajor, minMinor = 5, 2
case "arm64":
// Older ARM64 kernel versions have broken bpf_probe_read.
// https://github.com/torvalds/linux/commit/6ae08ae3dea2cfa03dd3665a3c8475c2d429ef47
minMajor, minMinor = 5, 5
default:
return fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
}

minMajor, minMinor = 5, 10
if major < minMajor || (major == minMajor && minor < minMinor) {
return fmt.Errorf("host Agent requires kernel version "+
"%d.%d or newer but got %d.%d.%d", minMajor, minMinor, major, minor, patch)
Expand Down
33 changes: 0 additions & 33 deletions doc/KNOWN_KERNEL_LIMITATIONS.md

This file was deleted.

8 changes: 1 addition & 7 deletions doc/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,10 @@ of these limitations are significantly relaxed in newer kernel versions, but we
still have to stick to the old limits because we wish to continue supporting
older kernels.

The minimum supported Linux kernel versions are
- 5.4 for amd64/x86_64
- 5.5 for arm64/aarch64
The minimum supported Linux kernel versions is 5.10 for amd64/x86_64 and arm64/aarch64.

The most notable limitations are the following two:

- **4096 instructions per program**\
A single BPF program can consist of a maximum of 4096 instructions, otherwise
older kernels will refuse to load it. Since BPF does not allow for loops, they
instead need to be unrolled.
- **32 tail-calls**\
Linux allows BPF programs to do a tail-call to another BPF program. A tail
call is essentially a `jmp` into another BPF program, ending execution of the
Expand Down
110 changes: 30 additions & 80 deletions processmanager/ebpf/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ type ebpfMapsImpl struct {
errCounterLock sync.Mutex
errCounter map[metrics.MetricID]int64

hasGenericBatchOperations bool
hasGenericBatchLookupAndDelete bool
hasLPMTrieBatchOperations bool
// Support for batch operations on LPM eBPF maps was only
// introduced with Linux kernel 5.13.
hasLPMTrieBatchOperations bool

updateWorkers *asyncMapUpdaterPool
}
Expand Down Expand Up @@ -117,16 +117,6 @@ func LoadMaps(ctx context.Context, includeTracers types.IncludedTracers,
impl.ExeIDToStackDeltaMaps[i-support.StackDeltaBucketSmallest] = deltasMap
}

if err := probeBatchOperations(cebpf.Hash); err == nil {
log.Infof("Supports generic eBPF map batch operations")
impl.hasGenericBatchOperations = true
}

if err := probeBatchLookupAndDelete(cebpf.Hash); err == nil {
log.Infof("Supports generic eBPF map batch lookup-and-delete")
impl.hasGenericBatchLookupAndDelete = true
}

if err := probeBatchOperations(cebpf.LPMTrie); err == nil {
log.Infof("Supports LPM trie eBPF map batch operations")
impl.hasLPMTrieBatchOperations = true
Expand Down Expand Up @@ -449,6 +439,12 @@ func probeBatchOperations(mapType cebpf.MapType) error {
return probeMapOperations(mapType, probeBatchOperationsInner[uint64])
}

// SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations
// on LPM trie maps.
func (impl *ebpfMapsImpl) SupportsLPMTrieBatchOperations() bool {
return impl.hasLPMTrieBatchOperations
}

// getMapID returns the mapID number to use for given number of stack deltas.
func getMapID(numDeltas uint32) (uint16, error) {
significantBits := 32 - bits.LeadingZeros32(numDeltas)
Expand Down Expand Up @@ -538,44 +534,26 @@ func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID,

impl.updateWorkers.EnqueueUpdate(outerMap, fileID, innerMapCloned)

if impl.hasGenericBatchOperations {
innerKeys := make([]uint32, numDeltas)
stackDeltas := make([]support.StackDelta, numDeltas)

// Prepare values for batch update.
for index, delta := range deltas {
innerKeys[index] = uint32(index)
stackDeltas[index].AddrLow = delta.AddressLow
stackDeltas[index].UnwindInfo = delta.UnwindInfo
}

_, err := innerMap.BatchUpdate(
ptrCastMarshaler[uint32](innerKeys),
ptrCastMarshaler[support.StackDelta](stackDeltas),
&cebpf.BatchOptions{Flags: uint64(cebpf.UpdateAny)})
if err != nil {
return 0, impl.trackMapError(metrics.IDExeIDToStackDeltasBatchUpdate,
fmt.Errorf("failed to batch insert %d elements for 0x%x "+
"into exeIDTostack_deltas: %v",
numDeltas, fileID, err))
}
return mapID, nil
}
innerKeys := make([]uint32, numDeltas)
stackDeltas := make([]support.StackDelta, numDeltas)

innerKey := uint32(0)
stackDelta := support.StackDelta{}
// Prepare values for batch update.
for index, delta := range deltas {
stackDelta.AddrLow = delta.AddressLow
stackDelta.UnwindInfo = delta.UnwindInfo
innerKey = uint32(index)
if err := innerMap.Update(unsafe.Pointer(&innerKey), unsafe.Pointer(&stackDelta),
cebpf.UpdateAny); err != nil {
return 0, impl.trackMapError(metrics.IDExeIDToStackDeltasUpdate, fmt.Errorf(
"failed to insert element %d for 0x%x into exeIDTostack_deltas: %v",
index, fileID, err))
}
innerKeys[index] = uint32(index)
stackDeltas[index].AddrLow = delta.AddressLow
stackDeltas[index].UnwindInfo = delta.UnwindInfo
}

_, err = innerMap.BatchUpdate(
ptrCastMarshaler[uint32](innerKeys),
ptrCastMarshaler[support.StackDelta](stackDeltas),
&cebpf.BatchOptions{Flags: uint64(cebpf.UpdateAny)})
if err != nil {
return 0, impl.trackMapError(metrics.IDExeIDToStackDeltasBatchUpdate,
fmt.Errorf("failed to batch insert %d elements for 0x%x "+
"into exeIDTostack_deltas: %v",
numDeltas, fileID, err))
}
return mapID, nil
}

Expand Down Expand Up @@ -617,22 +595,12 @@ func (impl *ebpfMapsImpl) UpdateStackDeltaPages(fileID host.FileID, numDeltasPer
firstDelta += uint32(numDeltas)
}

if impl.hasGenericBatchOperations {
_, err := impl.StackDeltaPageToInfo.BatchUpdate(
ptrCastMarshaler[support.StackDeltaPageKey](keys),
ptrCastMarshaler[support.StackDeltaPageInfo](values),
&cebpf.BatchOptions{Flags: uint64(cebpf.UpdateNoExist)})
return impl.trackMapError(metrics.IDStackDeltaPageToInfoBatchUpdate, err)
}
_, err := impl.StackDeltaPageToInfo.BatchUpdate(
ptrCastMarshaler[support.StackDeltaPageKey](keys),
ptrCastMarshaler[support.StackDeltaPageInfo](values),
&cebpf.BatchOptions{Flags: uint64(cebpf.UpdateNoExist)})
return impl.trackMapError(metrics.IDStackDeltaPageToInfoBatchUpdate, err)

for index := range keys {
if err := impl.trackMapError(metrics.IDStackDeltaPageToInfoUpdate,
impl.StackDeltaPageToInfo.Update(unsafe.Pointer(&keys[index]),
unsafe.Pointer(&values[index]), cebpf.UpdateNoExist)); err != nil {
return err
}
}
return nil
}

// DeleteStackDeltaPage removes the entry specified by fileID and page from the eBPF map.
Expand Down Expand Up @@ -737,24 +705,6 @@ func (impl *ebpfMapsImpl) LookupPidPageInformation(pid libpf.PID, page uint64) (
return host.FileID(cValue.File_id), bias, nil
}

// SupportsGenericBatchOperations returns true if the kernel supports eBPF batch operations
// on hash and array maps.
func (impl *ebpfMapsImpl) SupportsGenericBatchOperations() bool {
return impl.hasGenericBatchOperations
}

// SupportsGenericBatchLookupAndDelete returns true if the kernel supports eBPF batch
// lookup-and-delete operations on hash and array maps.
func (impl *ebpfMapsImpl) SupportsGenericBatchLookupAndDelete() bool {
return impl.hasGenericBatchLookupAndDelete
}

// SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations
// on LPM trie maps.
func (impl *ebpfMapsImpl) SupportsLPMTrieBatchOperations() bool {
return impl.hasLPMTrieBatchOperations
}

// ptrCastMarshaler is a small wrapper type intended to be used with cilium's BatchUpdate and
// BackDelete functions.
//
Expand Down
2 changes: 2 additions & 0 deletions processmanager/ebpf/ebpf_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/lpm"
"go.opentelemetry.io/ebpf-profiler/metrics"
"go.opentelemetry.io/ebpf-profiler/rlimit"
"go.opentelemetry.io/ebpf-profiler/support"
)
Expand All @@ -34,6 +35,7 @@ func loadTracers(t *testing.T) *ebpfMapsImpl {

return &ebpfMapsImpl{
PidPageToMappingInfo: pidPageToMappingInfo,
errCounter: make(map[metrics.MetricID]int64),
}
}

Expand Down
8 changes: 0 additions & 8 deletions processmanager/ebpfapi/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,6 @@ type EbpfHandler interface {
// CollectMetrics returns gathered errors for changes to eBPF maps.
CollectMetrics() []metrics.Metric

// SupportsGenericBatchOperations returns true if the kernel supports eBPF batch operations
// on hash and array maps.
SupportsGenericBatchOperations() bool

// SupportsGenericBatchLookupAndDelete returns true if the kernel supports eBPF batch
// lookup-and-delete operations on hash and array maps.
SupportsGenericBatchLookupAndDelete() bool

// SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations
// on LPM trie maps.
SupportsLPMTrieBatchOperations() bool
Expand Down
5 changes: 1 addition & 4 deletions support/ebpf/bpfdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,9 @@ static long (*bpf_probe_read_user)(void *dst, int size, const void *unsafe_ptr)
static long (*bpf_probe_read_kernel)(void *dst, int size, const void *unsafe_ptr) = (void *)
BPF_FUNC_probe_read_kernel;

// The sizeof in bpf_trace_printk() must include \0, else no output
// is generated. The \n is not needed on 5.8+ kernels, but definitely on
// 5.4 kernels.
#define printt(fmt, ...) \
({ \
const char ____fmt[] = fmt "\n"; \
const char ____fmt[] = fmt; \
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
})

Expand Down
Binary file modified support/ebpf/tracer.ebpf.amd64
Binary file not shown.
Binary file modified support/ebpf/tracer.ebpf.arm64
Binary file not shown.
Loading
Loading