Skip to content

Commit 7c76838

Browse files
authored
Merge pull request #565 from dqminh/cgroup_id
Support for BPF_MAP_CGRP_STORAGE type
2 parents 30cb343 + d9ac973 commit 7c76838

File tree

6 files changed

+155
-17
lines changed

6 files changed

+155
-17
lines changed

.vscode/config-schema.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ properties:
7777
properties:
7878
name:
7979
type: string
80+
type:
81+
enum:
82+
- hash
83+
- cgrp_storage
8084
regexps:
8185
type: array
8286
items:

config/config.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,24 @@ type Histogram struct {
5050
// The cgroups that match the provided regexps will be available to the bpf program
5151
// as a shared map with provided name.
5252
type CgroupIDMap struct {
53-
Name string `yaml:"name"`
54-
Regexps []string `yaml:"regexps"`
53+
Name string `yaml:"name"`
54+
Type CgroupIDMapType `yaml:"type"`
55+
Regexps []string `yaml:"regexps"`
5556
}
5657

58+
// CgroupIDMapType describes the map type that stores the cgroups that the bpf
59+
// programs are interested in.
60+
type CgroupIDMapType string
61+
62+
const (
63+
// CgroupIDMapHashType is normal hash map such as BPF_MAP_TYPE_LRU_HASH
64+
CgroupIDMapHashType CgroupIDMapType = "hash"
65+
// CgroupIDMapCgrpStorageType is specialized BPF_MAP_TYPE_CGRP_STORAGE map
66+
// with data keyed on a cgroup. When a group is deleted, data is automatically
67+
// removed.
68+
CgroupIDMapCgrpStorageType CgroupIDMapType = "cgrp_storage"
69+
)
70+
5771
// Tracing is a collection of spans attached to a program
5872
type Tracing struct {
5973
Spans []Span `yaml:"spans"`

examples/cgroup_id_map.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ metrics:
2020

2121
cgroup_id_map:
2222
name: cgroup_id_map
23+
type: hash
2324
regexps:
2425
- ^.*(system.slice/.*)$
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <vmlinux.h>
2+
#include <bpf/bpf_tracing.h>
3+
#include <bpf/bpf_core_read.h>
4+
#include "maps.bpf.h"
5+
6+
struct {
7+
__uint(type, BPF_MAP_TYPE_LRU_HASH);
8+
__uint(max_entries, 1024);
9+
__type(key, u64);
10+
__type(value, u64);
11+
} cgroup_cgrp_storage_sched_migrations_total SEC(".maps");
12+
13+
struct {
14+
__uint(type, BPF_MAP_TYPE_LRU_HASH);
15+
__uint(max_entries, 1024);
16+
__type(key, u64);
17+
__type(value, u64);
18+
} cgroup_cgrp_storage_sched_migrations_not_match_total SEC(".maps");
19+
20+
struct {
21+
__uint(type, BPF_MAP_TYPE_CGRP_STORAGE);
22+
__uint(map_flags, BPF_F_NO_PREALLOC);
23+
__type(key, u32);
24+
__type(value, u64);
25+
} cgroup_id_map_cgrp_storage SEC(".maps");
26+
27+
SEC("tp_btf/sched_migrate_task")
28+
int BPF_PROG(sched_migrate_task, struct task_struct *task, int dest_cpu)
29+
{
30+
u64 *ok;
31+
struct cgroup *cgrp = task->cgroups->dfl_cgrp;
32+
ok = bpf_cgrp_storage_get(&cgroup_id_map_cgrp_storage, cgrp, 0, BPF_LOCAL_STORAGE_GET_F_CREATE);
33+
if (!ok) {
34+
return 1;
35+
}
36+
u64 cgroup_id = BPF_CORE_READ(cgrp, kn, id);
37+
if (*ok) {
38+
increment_map(&cgroup_cgrp_storage_sched_migrations_total, &cgroup_id, 1);
39+
} else {
40+
increment_map(&cgroup_cgrp_storage_sched_migrations_not_match_total, &cgroup_id, 1);
41+
}
42+
return 0;
43+
}
44+
45+
char LICENSE[] SEC("license") = "GPL";
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
metrics:
2+
counters:
3+
- name: cgroup_cgrp_storage_sched_migrations_total
4+
help: Number of sched:sched_migrate_task events per cgroup
5+
labels:
6+
- name: cgroup
7+
size: 8
8+
decoders:
9+
- name: uint
10+
- name: cgroup
11+
12+
- name: cgroup_cgrp_storage_sched_migrations_not_match_total
13+
help: Number of sched:sched_migrate_task events per cgroup not match cgroup id map
14+
labels:
15+
- name: cgroup
16+
size: 8
17+
decoders:
18+
- name: uint
19+
- name: cgroup
20+
21+
cgroup_id_map:
22+
name: cgroup_id_map_cgrp_storage
23+
type: cgrp_storage
24+
regexps:
25+
- ^.*(system.slice/.*)$

exporter/cgroup_id_map.go

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package exporter
22

33
import (
4+
"errors"
45
"fmt"
6+
"io/fs"
57
"log"
8+
"os"
69
"regexp"
710
"unsafe"
811

@@ -13,9 +16,10 @@ import (
1316

1417
// CgroupIDMap synchronises cgroup changes with the shared bpf map.
1518
type CgroupIDMap struct {
16-
bpfMap *libbpfgo.BPFMap
17-
ch chan cgroup.ChangeNotification
18-
cache map[string]*regexp.Regexp
19+
bpfMap *libbpfgo.BPFMap
20+
bpfMapType config.CgroupIDMapType
21+
ch chan cgroup.ChangeNotification
22+
cache map[string]*regexp.Regexp
1923
}
2024

2125
func newCgroupIDMap(module *libbpfgo.Module, cfg config.Config) (*CgroupIDMap, error) {
@@ -25,18 +29,26 @@ func newCgroupIDMap(module *libbpfgo.Module, cfg config.Config) (*CgroupIDMap, e
2529
}
2630

2731
keySize := m.KeySize()
28-
if keySize != 8 {
29-
return nil, fmt.Errorf("key size for map %q is not expected 8 bytes (u64), it is %d bytes", cfg.CgroupIDMap.Name, keySize)
32+
var expectedKeySize int
33+
switch cfg.CgroupIDMap.Type {
34+
case config.CgroupIDMapHashType:
35+
expectedKeySize = 8
36+
case config.CgroupIDMapCgrpStorageType:
37+
expectedKeySize = 4
38+
}
39+
if keySize != expectedKeySize {
40+
return nil, fmt.Errorf("key size for map %q is not expected %d bytes for map type %s, it is %d bytes", cfg.CgroupIDMap.Name, expectedKeySize, cfg.CgroupIDMap.Type, keySize)
3041
}
3142
valueSize := m.ValueSize()
3243
if valueSize != 8 {
3344
return nil, fmt.Errorf("value size for map %q is not expected 8 bytes (u64), it is %d bytes", cfg.CgroupIDMap.Name, valueSize)
3445
}
3546

3647
c := &CgroupIDMap{
37-
bpfMap: m,
38-
ch: make(chan cgroup.ChangeNotification, 10),
39-
cache: map[string]*regexp.Regexp{},
48+
bpfMap: m,
49+
bpfMapType: cfg.CgroupIDMap.Type,
50+
ch: make(chan cgroup.ChangeNotification, 10),
51+
cache: map[string]*regexp.Regexp{},
4052
}
4153

4254
for _, expr := range cfg.CgroupIDMap.Regexps {
@@ -59,20 +71,57 @@ func (c *CgroupIDMap) subscribe(m *cgroup.Monitor) error {
5971
func (c *CgroupIDMap) runLoop() {
6072
for update := range c.ch {
6173
if update.Remove {
62-
key := uint64(update.ID)
63-
err := c.bpfMap.DeleteKey(unsafe.Pointer(&key))
64-
log.Printf("Error deleting key from CgroupIDMap: %v", err)
74+
err := c.removeCgroup(uint64(update.ID))
75+
if err != nil {
76+
log.Printf("Error deleting key from CgroupIDMap %s: %v", c.bpfMap.Name(), err)
77+
}
6578
} else {
66-
key := uint64(update.ID)
67-
value := uint64(1)
68-
if c.checkMatch(update.Path) {
69-
err := c.bpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&value))
79+
err := c.updateCgroup(uint64(update.ID), update.Path)
80+
if err != nil {
7081
log.Printf("Error updating CgroupIDMap: %v", err)
7182
}
7283
}
7384
}
7485
}
7586

87+
func (c *CgroupIDMap) removeCgroup(id uint64) error {
88+
// we only need to delete cgroup id for normal hash map
89+
if c.bpfMapType == config.CgroupIDMapCgrpStorageType {
90+
return nil
91+
}
92+
err := c.bpfMap.DeleteKey(unsafe.Pointer(&id))
93+
if errors.Is(err, fs.ErrNotExist) {
94+
return nil
95+
}
96+
return err
97+
}
98+
99+
func (c *CgroupIDMap) updateCgroup(id uint64, cgroupPath string) error {
100+
if !c.checkMatch(cgroupPath) {
101+
return nil
102+
}
103+
value := uint64(1)
104+
var err error
105+
switch c.bpfMapType {
106+
case config.CgroupIDMapHashType:
107+
err = c.bpfMap.Update(unsafe.Pointer(&id), unsafe.Pointer(&value))
108+
case config.CgroupIDMapCgrpStorageType:
109+
var cgroupFile *os.File
110+
// https://docs.kernel.org/bpf/map_cgrp_storage.html
111+
// we need an open cgroup fd to update cgroup map
112+
cgroupFile, err = os.Open(cgroupPath)
113+
if err != nil {
114+
log.Printf("Error opening cgroup path %s: %v", cgroupPath, err)
115+
} else {
116+
fd := cgroupFile.Fd()
117+
err = c.bpfMap.Update(unsafe.Pointer(&fd), unsafe.Pointer(&value))
118+
cgroupFile.Close()
119+
}
120+
}
121+
122+
return err
123+
}
124+
76125
func (c *CgroupIDMap) checkMatch(path string) bool {
77126
for _, compiled := range c.cache {
78127
if compiled.MatchString(path) {

0 commit comments

Comments
 (0)