Skip to content
Closed
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
39 changes: 38 additions & 1 deletion map.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ type MapOptions struct {
// The base path to pin maps in if requested via PinByName.
// Existing maps will be re-used if they are compatible, otherwise an
// error is returned.
PinPath string
PinPath string
// The full path to use for each map with PinByPath
// Existing maps will be re-used if they are compatible, otherwise an
// error is returned.
PinPathMapping map[string]string

LoadPinOptions LoadPinOptions
}

Expand Down Expand Up @@ -380,6 +385,28 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {

return m, nil

case PinByPath:
if spec.Name == "" {
return nil, fmt.Errorf("pin by path: missing Name")
}
path, ok := opts.PinPathMapping[spec.Name]
if !ok {
return nil, fmt.Errorf("pin by path: no path found for map %v", spec.Name)
}
m, err := LoadPinnedMap(path, &opts.LoadPinOptions)
if errors.Is(err, unix.ENOENT) {
break
}
if err != nil {
return nil, fmt.Errorf("load pinned map: %w", err)
}
defer closeOnError(m)

if err := spec.Compatible(m); err != nil {
return nil, fmt.Errorf("use pinned map %s: %w", spec.Name, err)
}

return m, nil
case PinNone:
// Nothing to do here

Expand Down Expand Up @@ -422,6 +449,16 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
}
}

if spec.Pinning == PinByPath {
path, ok := opts.PinPathMapping[spec.Name]
if !ok {
return nil, fmt.Errorf("pin by path: no path found for map %v", spec.Name)
}
if err := m.Pin(path); err != nil {
return nil, fmt.Errorf("pin map to %s: %w", path, err)
}
}

return m, nil
}

Expand Down
133 changes: 132 additions & 1 deletion map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,7 @@ func TestNewMapFromID(t *testing.T) {
}
}

func TestMapPinning(t *testing.T) {
func TestMapPinningPinPath(t *testing.T) {
tmp := testutils.TempBPFFS(t)

spec := &MapSpec{
Expand Down Expand Up @@ -1659,6 +1659,137 @@ func TestMapPinning(t *testing.T) {
qt.Assert(t, qt.StringContains(err.Error(), "ValueSize"))
}

func TestMapPinningPinPathMapping(t *testing.T) {
tmp := testutils.TempBPFFS(t)

spec := &MapSpec{
Name: "test",
Type: Hash,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
Pinning: PinByPath,
}

m1 := mustNewMap(t, spec, &MapOptions{
PinPathMapping: map[string]string{spec.Name: filepath.Join(tmp, "foo")},
})
pinned := m1.IsPinned()
qt.Assert(t, qt.IsTrue(pinned))

m1Info, err := m1.Info()
qt.Assert(t, qt.IsNil(err))

if err := m1.Put(uint32(0), uint32(42)); err != nil {
t.Fatal("Can't write value:", err)
}

m2 := mustNewMap(t, spec, &MapOptions{
PinPathMapping: map[string]string{spec.Name: filepath.Join(tmp, "foo")},
})

m2Info, err := m2.Info()
qt.Assert(t, qt.IsNil(err))

if m1ID, ok := m1Info.ID(); ok {
m2ID, _ := m2Info.ID()
qt.Assert(t, qt.Equals(m2ID, m1ID))
}

var value uint32
if err := m2.Lookup(uint32(0), &value); err != nil {
t.Fatal("Can't read from map:", err)
}

if value != 42 {
t.Fatal("Pinning doesn't use pinned maps")
}

spec.KeySize = 8
spec.ValueSize = 8
_, err = newMap(t, spec, &MapOptions{
PinPathMapping: map[string]string{spec.Name: filepath.Join(tmp, "foo")},
})
if err == nil {
t.Fatalf("Opening a pinned map with a mismatching spec did not fail")
}
if !errors.Is(err, ErrMapIncompatible) {
t.Fatalf("Opening a pinned map with a mismatching spec failed with the wrong error")
}

// Check if error string mentions both KeySize and ValueSize.
qt.Assert(t, qt.StringContains(err.Error(), "KeySize"))
qt.Assert(t, qt.StringContains(err.Error(), "ValueSize"))
}

func TestPinMapMappingMultiplePaths(t *testing.T) {
tmp1 := testutils.TempBPFFS(t)
tmp2 := testutils.TempBPFFS(t)

spec1 := &MapSpec{
Name: "test1",
Type: Hash,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
Pinning: PinByPath,
}

spec2 := &MapSpec{
Name: "test2",
Type: Hash,
KeySize: 8,
ValueSize: 8,
MaxEntries: 1,
Pinning: PinByPath,
}

pathMapping := map[string]string{
spec1.Name: filepath.Join(tmp1, "test1_target"),
spec2.Name: filepath.Join(tmp2, "test2_target"),
}

m1 := mustNewMap(t, spec1, &MapOptions{
PinPathMapping: pathMapping,
})
pinned := m1.IsPinned()
qt.Assert(t, qt.IsTrue(pinned))

if err := m1.Put(uint32(0), uint32(42)); err != nil {
t.Fatal("Can't write value in m1:", err)
}

m2 := mustNewMap(t, spec2, &MapOptions{
PinPathMapping: pathMapping,
})
pinned = m2.IsPinned()
qt.Assert(t, qt.IsTrue(pinned))

if err := m2.Put(uint64(0), uint64(42)); err != nil {
t.Fatal("Can't write value in m2:", err)
}

m1Loaded, err := LoadPinnedMap(filepath.Join(tmp1, "test1_target"), nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}

if err := m1Loaded.Close(); err != nil {
t.Fatalf("close loaded map: %v", err)
}

m2Loaded, err := LoadPinnedMap(filepath.Join(tmp2, "test2_target"), nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
if err := m2Loaded.Close(); err != nil {
t.Fatalf("close loaded map: %v", err)
}

}

func TestMapHandle(t *testing.T) {
kv := &btf.Int{Size: 4}
m := mustNewMap(t, &MapSpec{
Expand Down
2 changes: 2 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ const (
PinNone PinType = iota
// Pin an object by using its name as the filename.
PinByName
// Pin an object using its path as the full path.
PinByPath
)

// LoadPinOptions control how a pinned object is loaded.
Expand Down