Skip to content

Commit 42bc982

Browse files
authored
Merge pull request #531 from dqminh/cgroup_id
Feature: synchronize cgroup id with ebpf program via shared map
2 parents 1ef6204 + 5ae3ba7 commit 42bc982

File tree

14 files changed

+269
-34
lines changed

14 files changed

+269
-34
lines changed

.vscode/config-schema.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ properties:
7171
type: number
7272
labels:
7373
$ref: "#/definitions/labels"
74+
cgroup_id_map:
75+
type: object
76+
additionalProperties: false
77+
properties:
78+
name:
79+
type: string
80+
regexps:
81+
type: array
82+
items:
83+
type: string
7484
tracing:
7585
type: object
7686
additionalProperties: false

cgroup/fanotify.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,8 @@ func newFanotifyMonitor(path string) (*fanotifyMonitor, error) {
6363
}
6464

6565
go func() {
66-
if err := m.readFanotifyLoop(); err != nil {
67-
log.Fatalf("Error running fanotify loop: %v", err)
68-
}
66+
err := m.readFanotifyLoop()
67+
log.Fatalf("Fanotify loop terminated with err:%v", err)
6968
}()
7069

7170
return m, nil
@@ -199,6 +198,10 @@ func (m *fanotifyMonitor) Resolve(id int) string {
199198
return m.observer.lookup(id)
200199
}
201200

201+
func (m *fanotifyMonitor) SubscribeCgroupChange(ch chan<- ChangeNotification) error {
202+
return m.observer.subscribeCgroupChange(ch)
203+
}
204+
202205
// The following kernel patch is required to take advantage of this (included in v6.6-rc1):
203206
// * https://git.kernel.org/torvalds/c/0ce7c12e88cf ("kernfs: attach uuid for every kernfs and report it in fsid")
204207
func attachFanotify(path string) (io.Reader, error) {

cgroup/monitor.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
package cgroup
22

33
import (
4+
"errors"
45
"log"
56
)
67

8+
// ErrCgroupIDMapUnsupported is returned when cgroup id map is not available
9+
var ErrCgroupIDMapUnsupported = errors.New("cgroup change subscription failed (fanotify not available)")
10+
11+
// ChangeNotification is the notification returned by cgroup monitor when a subscribed
12+
// cgroup has been added or removed
13+
type ChangeNotification struct {
14+
ID int
15+
Path string
16+
Remove bool
17+
}
18+
719
type monitor interface {
820
Resolve(id int) string
21+
SubscribeCgroupChange(ch chan<- ChangeNotification) error
922
}
1023

1124
// Monitor resolves cgroup ids into their respective paths
@@ -34,3 +47,9 @@ func NewMonitor(path string) (*Monitor, error) {
3447
func (m *Monitor) Resolve(id int) string {
3548
return m.inner.Resolve(id)
3649
}
50+
51+
// SubscribeCgroupChange receives cgroup change notifications. This requires
52+
// kernel with fanotify support for cgroup
53+
func (m *Monitor) SubscribeCgroupChange(ch chan<- ChangeNotification) error {
54+
return m.inner.SubscribeCgroupChange(ch)
55+
}

cgroup/observer.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ type observer struct {
1919
lock sync.Mutex
2020
inodeToPath map[int]*resolved
2121
pathToInode map[string]int
22+
cgroupChans []chan<- ChangeNotification
2223
}
2324

2425
func newObserver(initial map[int]string) *observer {
2526
observer := observer{
2627
lock: sync.Mutex{},
2728
inodeToPath: map[int]*resolved{},
2829
pathToInode: map[string]int{},
30+
cgroupChans: []chan<- ChangeNotification{},
2931
}
3032

3133
for inode, name := range initial {
@@ -83,6 +85,12 @@ func (o *observer) add(inode int, path string) {
8385

8486
o.inodeToPath[inode] = r
8587
o.pathToInode[path] = inode
88+
for _, ch := range o.cgroupChans {
89+
ch <- ChangeNotification{
90+
ID: inode,
91+
Path: path,
92+
}
93+
}
8694
}
8795

8896
func (o *observer) remove(path string) {
@@ -96,6 +104,13 @@ func (o *observer) remove(path string) {
96104

97105
r := o.inodeToPath[inode]
98106
r.dead = time.Now()
107+
for _, ch := range o.cgroupChans {
108+
ch <- ChangeNotification{
109+
ID: inode,
110+
Path: path,
111+
Remove: true,
112+
}
113+
}
99114
}
100115

101116
func (o *observer) lookup(inode int) string {
@@ -113,3 +128,17 @@ func (o *observer) lookup(inode int) string {
113128

114129
return r.path
115130
}
131+
132+
func (o *observer) subscribeCgroupChange(ch chan<- ChangeNotification) error {
133+
o.cgroupChans = append(o.cgroupChans, ch)
134+
// send the initial cgroup mapping
135+
go func() {
136+
for path, inode := range o.pathToInode {
137+
ch <- ChangeNotification{
138+
ID: inode,
139+
Path: path,
140+
}
141+
}
142+
}()
143+
return nil
144+
}

cgroup/walker.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ func (m *walkerMonitor) Resolve(id int) string {
4545
return m.mapping[id]
4646
}
4747

48+
func (m *walkerMonitor) SubscribeCgroupChange(_ chan<- ChangeNotification) error {
49+
return ErrCgroupIDMapUnsupported
50+
}
51+
4852
func walk(dir string) (map[int]string, error) {
4953
mapping := map[int]string{}
5054

config/config.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import (
1111

1212
// Config describes how to configure and extract metrics
1313
type Config struct {
14-
Name string `yaml:"name"`
15-
Metrics Metrics `yaml:"metrics"`
16-
Tracing Tracing `yaml:"tracing"`
17-
Kaddrs []string `yaml:"kaddrs"`
18-
BPFPath string
14+
Name string `yaml:"name"`
15+
Metrics Metrics `yaml:"metrics"`
16+
Tracing Tracing `yaml:"tracing"`
17+
Kaddrs []string `yaml:"kaddrs"`
18+
CgroupIDMap CgroupIDMap `yaml:"cgroup_id_map"`
19+
BPFPath string
1920
}
2021

2122
// Metrics is a collection of metrics attached to a program
@@ -45,6 +46,14 @@ type Histogram struct {
4546
Labels []Label `yaml:"labels"`
4647
}
4748

49+
// CgroupIDMap describes the cgroup that the bpf programs are interested in.
50+
// The cgroups that match the provided regexps will be available to the bpf program
51+
// as a shared map with provided name.
52+
type CgroupIDMap struct {
53+
Name string `yaml:"name"`
54+
Regexps []string `yaml:"regexps"`
55+
}
56+
4857
// Tracing is a collection of spans attached to a program
4958
type Tracing struct {
5059
Spans []Span `yaml:"spans"`

decoder/cgroup.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,6 @@ type CGroup struct {
1313
monitor *cgroup.Monitor
1414
}
1515

16-
// NewCgroupDecoder creates a new cgroup decoder
17-
func NewCgroupDecoder() (*CGroup, error) {
18-
monitor, err := cgroup.NewMonitor("/sys/fs/cgroup")
19-
if err != nil {
20-
return nil, fmt.Errorf("error creating cgroup monitor: %w", err)
21-
}
22-
23-
return &CGroup{monitor}, nil
24-
}
25-
2616
// Decode transforms cgroup id to path in cgroupfs
2717
func (c *CGroup) Decode(in []byte, _ config.Decoder) ([]byte, error) {
2818
cgroupID, err := strconv.Atoi(string(in))

decoder/decoder.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"sync"
77

8+
"github.com/cloudflare/ebpf_exporter/v2/cgroup"
89
"github.com/cloudflare/ebpf_exporter/v2/config"
910
"github.com/cloudflare/ebpf_exporter/v2/kallsyms"
1011
lru "github.com/hashicorp/golang-lru/v2"
@@ -29,20 +30,15 @@ type Set struct {
2930
}
3031

3132
// NewSet creates a Set with all known decoders
32-
func NewSet(skipCacheSize int) (*Set, error) {
33-
cgroup, err := NewCgroupDecoder()
34-
if err != nil {
35-
return nil, fmt.Errorf("error creating cgroup decoder: %w", err)
36-
}
37-
33+
func NewSet(skipCacheSize int, monitor *cgroup.Monitor) (*Set, error) {
3834
ksym, err := kallsyms.NewDecoder("/proc/kallsyms")
3935
if err != nil {
4036
return nil, fmt.Errorf("error creating ksym decoder: %w", err)
4137
}
4238

4339
s := &Set{
4440
decoders: map[string]Decoder{
45-
"cgroup": cgroup,
41+
"cgroup": &CGroup{monitor},
4642
"dname": &Dname{},
4743
"errno": &Errno{},
4844
"hex": &Hex{},

decoder/decoder_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func TestDecodeLabels(t *testing.T) {
149149
}
150150

151151
for i, c := range cases {
152-
s, err := NewSet(0)
152+
s, err := NewSet(0, nil)
153153
if err != nil {
154154
t.Fatal(err)
155155
}
@@ -255,7 +255,7 @@ func TestDecodeSkipLabels(t *testing.T) {
255255
}
256256

257257
for i, c := range cases {
258-
s, err := NewSet(100)
258+
s, err := NewSet(100, nil)
259259
if err != nil {
260260
t.Fatal(err)
261261
}
@@ -322,7 +322,7 @@ func TestDecoderSetConcurrency(t *testing.T) {
322322
},
323323
}
324324

325-
s, err := NewSet(0)
325+
s, err := NewSet(0, nil)
326326
if err != nil {
327327
t.Fatal(err)
328328
}
@@ -387,7 +387,7 @@ func TestDecoderSetCache(t *testing.T) {
387387
},
388388
}
389389

390-
s, err := NewSet(0)
390+
s, err := NewSet(0, nil)
391391
if err != nil {
392392
t.Fatal(err)
393393
}
@@ -458,7 +458,7 @@ func BenchmarkCache(b *testing.B) {
458458
},
459459
}
460460

461-
s, err := NewSet(0)
461+
s, err := NewSet(0, nil)
462462
if err != nil {
463463
b.Fatal(err)
464464
}

examples/cgroup_id_map.bpf.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <vmlinux.h>
2+
#include <bpf/bpf_tracing.h>
3+
#include "maps.bpf.h"
4+
5+
struct {
6+
__uint(type, BPF_MAP_TYPE_LRU_HASH);
7+
__uint(max_entries, 1024);
8+
__type(key, u64);
9+
__type(value, u64);
10+
} cgroup_sched_migrations_total SEC(".maps");
11+
12+
struct {
13+
__uint(type, BPF_MAP_TYPE_LRU_HASH);
14+
__uint(max_entries, 1024);
15+
__type(key, u64);
16+
__type(value, u64);
17+
} cgroup_sched_migrations_not_match_total SEC(".maps");
18+
19+
struct {
20+
__uint(type, BPF_MAP_TYPE_LRU_HASH);
21+
__uint(max_entries, 1024);
22+
__type(key, u64);
23+
__type(value, u64);
24+
} cgroup_id_map SEC(".maps");
25+
26+
SEC("tp_btf/sched_migrate_task")
27+
int BPF_PROG(sched_migrate_task)
28+
{
29+
u64 *ok;
30+
u64 cgroup_id = bpf_get_current_cgroup_id();
31+
ok = bpf_map_lookup_elem(&cgroup_id_map, &cgroup_id);
32+
if (ok) {
33+
increment_map(&cgroup_sched_migrations_total, &cgroup_id, 1);
34+
} else {
35+
increment_map(&cgroup_sched_migrations_not_match_total, &cgroup_id, 1);
36+
}
37+
return 0;
38+
}
39+
40+
char LICENSE[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)