Repository: cilium/ebpf
Pull request #201 (2021-01-21 16:05:49)

info: expose map and program ID
Currently the only way to access IDs is via Map.FD() and Program.ID().
In our use case this leads to a bunch of extra calls to BPF_OBJ_GET_INFO_BY_FD.
Expose the IDs on the Info structs, and mark the ID() functions as
deprecated. We can remove them in a little bit.


Patch:
diff --git a/info.go b/info.go
index 588dfab..169bc07 100644
--- a/info.go
+++ b/info.go
@@ -16,6 +16,7 @@ import (
 // MapInfo describes a map.
 type MapInfo struct {
 	Type       MapType
+	id         MapID
 	KeySize    uint32
 	ValueSize  uint32
 	MaxEntries uint32
@@ -35,6 +36,7 @@ func newMapInfoFromFd(fd *internal.FD) (*MapInfo, error) {
 
 	return &MapInfo{
 		MapType(info.map_type),
+		MapID(info.id),
 		info.key_size,
 		info.value_size,
 		info.max_entries,
@@ -59,9 +61,19 @@ func newMapInfoFromProc(fd *internal.FD) (*MapInfo, error) {
 	return &mi, nil
 }
 
+// ID returns the map ID.
+//
+// Available from 4.13.
+//
+// The bool return value indicates whether this optional field is available.
+func (mi *MapInfo) ID() (MapID, bool) {
+	return mi.id, mi.id > 0
+}
+
 // ProgramInfo describes a program.
 type ProgramInfo struct {
 	Type ProgramType
+	id   ProgramID
 	// Truncated hash of the BPF bytecode.
 	Tag string
 	// Name as supplied by user space at load time.
@@ -79,6 +91,7 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
 
 	return &ProgramInfo{
 		ProgramType(info.prog_type),
+		ProgramID(info.id),
 		// tag is available if the kernel supports BPF_PROG_GET_INFO_BY_FD.
 		hex.EncodeToString(info.tag[:]),
 		// name is available from 4.15.
@@ -105,6 +118,15 @@ func newProgramInfoFromProc(fd *internal.FD) (*ProgramInfo, error) {
 	return &info, nil
 }
 
+// ID returns the program ID.
+//
+// Available from 4.13.
+//
+// The bool return value indicates whether this optional field is available.
+func (pi *ProgramInfo) ID() (ProgramID, bool) {
+	return pi.id, pi.id > 0
+}
+
 func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
 	raw, err := fd.Value()
 	if err != nil {
diff --git a/map.go b/map.go
index 71bae94..da56a22 100644
--- a/map.go
+++ b/map.go
@@ -910,7 +910,7 @@ func NewMapFromID(id MapID) (*Map, error) {
 
 // ID returns the systemwide unique ID of the map.
 //
-// Requires at least Linux 4.13.
+// Deprecated: use MapInfo.ID() instead.
 func (m *Map) ID() (MapID, error) {
 	info, err := bpfGetMapInfoByFD(m.fd)
 	if err != nil {
diff --git a/prog.go b/prog.go
index 6fa25b0..111672d 100644
--- a/prog.go
+++ b/prog.go
@@ -603,6 +603,8 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
 }
 
 // ID returns the systemwide unique ID of the program.
+//
+// Deprecated: use ProgramInfo.ID() instead.
 func (p *Program) ID() (ProgramID, error) {
 	info, err := bpfGetProgInfoByFD(p.fd)
 	if err != nil {


Test patch:
diff --git a/info_test.go b/info_test.go
index 823c523..ce1392b 100644
--- a/info_test.go
+++ b/info_test.go
@@ -52,6 +52,10 @@ func TestMapInfoFromProc(t *testing.T) {
 		t.Error("Expected name to be testing, got", info.Name)
 	}
 
+	if _, ok := info.ID(); ok {
+		t.Error("Expected ID to not be available")
+	}
+
 	nested, err := NewMap(&MapSpec{
 		Type:       ArrayOfMaps,
 		KeySize:    4,
@@ -101,6 +105,12 @@ func TestProgramInfo(t *testing.T) {
 			if want := "d7edec644f05498d"; info.Tag != want {
 				t.Errorf("Expected Tag to be %s, got %s", want, info.Tag)
 			}
+
+			if id, ok := info.ID(); ok && id == 0 {
+				t.Error("Expected a valid ID:", id)
+			} else if name == "proc" && ok {
+				t.Error("Expected ID to not be available")
+			}
 		})
 	}
 }