-
Notifications
You must be signed in to change notification settings - Fork 390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cgroup tracking #3170
cgroup tracking #3170
Changes from all commits
4795c8c
cefb174
72ccbbd
71c4dad
ca8ee2e
5cca0a0
6bf1214
f85c8d1
fb204a6
91124a0
d0e826f
ed29ae0
c3bfb1a
088ba2a
6a4c54d
46bd871
51d282e
f2fd3ae
1f8af06
5e924fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
/* Copyright Authors of Tetragon */ | ||
|
||
#include "vmlinux.h" | ||
#include "api.h" | ||
#include "bpf_helpers.h" | ||
#include "bpf_cgroup.h" | ||
#include "bpf_tracing.h" | ||
#include "cgtracker.h" | ||
|
||
char _license[] __attribute__((section(("license")), used)) = "GPL"; | ||
#ifdef VMLINUX_KERNEL_VERSION | ||
int _version __attribute__((section(("version")), used)) = | ||
VMLINUX_KERNEL_VERSION; | ||
#endif | ||
|
||
/* new kernel cgroup definition */ | ||
struct cgroup___new { | ||
int level; | ||
struct cgroup *ancestors[]; | ||
} __attribute__((preserve_access_index)); | ||
|
||
FUNC_INLINE __u64 cgroup_get_parent_id(struct cgroup *cgrp) | ||
{ | ||
struct cgroup___new *cgrp_new = (struct cgroup___new *)cgrp; | ||
|
||
// for newer kernels, we can access use ->ancestors to retrieve the parent | ||
if (bpf_core_field_exists(cgrp_new->ancestors)) { | ||
int level = get_cgroup_level(cgrp); | ||
|
||
if (level <= 0) | ||
return 0; | ||
return BPF_CORE_READ(cgrp_new, ancestors[level - 1], kn, id); | ||
} | ||
|
||
// otherwise, go over the parent pointer | ||
struct cgroup_subsys_state *parent_css = BPF_CORE_READ(cgrp, self.parent); | ||
|
||
if (parent_css) { | ||
struct cgroup *parent = container_of(parent_css, struct cgroup, self); | ||
__u64 parent_cgid = get_cgroup_id(parent); | ||
return parent_cgid; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
__attribute__((section(("raw_tracepoint/cgroup_mkdir")), used)) int | ||
tg_cgtracker_cgroup_mkdir(struct bpf_raw_tracepoint_args *ctx) | ||
{ | ||
struct cgroup *cgrp; | ||
__u64 cgid, cgid_parent, *cgid_tracker; | ||
|
||
cgrp = (struct cgroup *)ctx->args[0]; | ||
cgid = get_cgroup_id(cgrp); | ||
if (cgid == 0) | ||
return 0; | ||
cgid_parent = cgroup_get_parent_id(cgrp); | ||
if (cgid_parent == 0) | ||
return 0; | ||
cgid_tracker = map_lookup_elem(&tg_cgtracker_map, &cgid_parent); | ||
if (cgid_tracker) | ||
map_update_elem(&tg_cgtracker_map, &cgid, cgid_tracker, BPF_ANY); | ||
|
||
return 0; | ||
} | ||
|
||
__attribute__((section(("raw_tracepoint/cgroup_release")), used)) int | ||
tg_cgtracker_cgroup_release(struct bpf_raw_tracepoint_args *ctx) | ||
{ | ||
struct cgroup *cgrp; | ||
__u64 cgid; | ||
|
||
cgrp = (struct cgroup *)ctx->args[0]; | ||
cgid = get_cgroup_id(cgrp); | ||
if (cgid) | ||
map_delete_elem(&tg_cgtracker_map, &cgid); | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | ||
/* Copyright Authors of Cilium */ | ||
|
||
#ifndef CGTRACKER_H__ | ||
#define CGTRACKER_H__ | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_HASH); | ||
__uint(max_entries, 1); | ||
__type(key, __u64); /* cgroup id */ | ||
__type(value, __u64); /* tracker cgroup id */ | ||
} tg_cgtracker_map SEC(".maps"); | ||
|
||
FUNC_INLINE __u64 cgrp_get_tracker_id(__u64 cgid) | ||
{ | ||
__u64 *ret; | ||
|
||
ret = map_lookup_elem(&tg_cgtracker_map, &cgid); | ||
return ret ? *ret : 0; | ||
} | ||
|
||
#endif /* CGTRACKER_H__ */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package cgtracker | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"path/filepath" | ||
|
||
"github.com/cilium/tetragon/pkg/cgidarg" | ||
"github.com/cilium/tetragon/pkg/cgtracker" | ||
"github.com/cilium/tetragon/pkg/defaults" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func New() *cobra.Command { | ||
ret := &cobra.Command{ | ||
Use: "cgtracker", | ||
Short: "manage cgtracker map (only for debugging)", | ||
Hidden: true, | ||
SilenceUsage: true, | ||
} | ||
|
||
ret.AddCommand( | ||
dumpCmd(), | ||
addCommand(), | ||
) | ||
|
||
return ret | ||
} | ||
|
||
func dumpCmd() *cobra.Command { | ||
mapFname := filepath.Join(defaults.DefaultMapRoot, defaults.DefaultMapPrefix, cgtracker.MapName) | ||
ret := &cobra.Command{ | ||
Use: "dump", | ||
Short: "dump cgtracker map state", | ||
Args: cobra.ExactArgs(0), | ||
RunE: func(_ *cobra.Command, _ []string) error { | ||
m, err := cgtracker.OpenMap(mapFname) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
defer m.Close() | ||
|
||
vals, err := m.Dump() | ||
if err != nil { | ||
return err | ||
} | ||
for tracker, tracked := range vals { | ||
fmt.Printf("%d: %v\n", tracker, tracked) | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
flags := ret.Flags() | ||
flags.StringVar(&mapFname, "map-fname", mapFname, "cgtracker map filename") | ||
return ret | ||
} | ||
|
||
func addCommand() *cobra.Command { | ||
mapFname := filepath.Join(defaults.DefaultMapRoot, defaults.DefaultMapPrefix, cgtracker.MapName) | ||
ret := &cobra.Command{ | ||
Use: "add cg_tracked cg_tracker", | ||
Short: "add cgtracker entry", | ||
Args: cobra.ExactArgs(2), | ||
RunE: func(_ *cobra.Command, args []string) error { | ||
tracked, err := cgidarg.Parse(args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
tracker, err := cgidarg.Parse(args[1]) | ||
if err != nil { | ||
return err | ||
} | ||
m, err := cgtracker.OpenMap(mapFname) | ||
if err != nil { | ||
return err | ||
} | ||
defer m.Close() | ||
return m.Add(tracked, tracker) | ||
|
||
}, | ||
} | ||
|
||
flags := ret.Flags() | ||
flags.StringVar(&mapFname, "map-fname", mapFname, "cgtracker map filename") | ||
return ret | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ package main | |
|
||
import ( | ||
"github.com/cilium/tetragon/cmd/tetra/bugtool" | ||
"github.com/cilium/tetragon/cmd/tetra/cgtracker" | ||
"github.com/cilium/tetragon/cmd/tetra/cri" | ||
"github.com/cilium/tetragon/cmd/tetra/debug" | ||
"github.com/cilium/tetragon/cmd/tetra/loglevel" | ||
|
@@ -24,4 +25,5 @@ func addCommands(rootCmd *cobra.Command) { | |
rootCmd.AddCommand(probe.New()) | ||
rootCmd.AddCommand(loglevel.New()) | ||
rootCmd.AddCommand(cri.New()) | ||
rootCmd.AddCommand(cgtracker.New()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since this is only for debugging could you put this behind the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's discuss? Not sure what the benefit of having everything under debug is. Can do as a followup if the consensus is that this is what we want. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,3 +24,4 @@ change-capabilities | |
direct-write-tester | ||
user-stacktrace | ||
pause | ||
/test-helper |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package main | ||
|
||
import ( | ||
"github.com/cilium/tetragon/pkg/testutils/progs" | ||
) | ||
|
||
func main() { | ||
progs.TestHelperMain() | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm didn't have time to review all, but want to point this is not cgroupv1 compatible, the mkdir and release delete will work on any cgoupv1 hierarchies, and if kernfs ids clash could lead to corruption?
The old branch has this check: https://github.com/cilium/tetragon/blob/pr/tixxdz/cgroup-bpf-full/bpf/cgroup/bpf_cgroup_mkdir.c#L33
So this could break in cgroupv1. Will check closely later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to have this only for group v2 at the moment. That being said, it's unclear what the problem is for v1. What are the kernfs IDs that could clash?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are following all hierarchies in cgroupv1! including the ones that we don't track, why?
The kernfs IDs are cgroup IDs if they are re-used you remove them from the tracking and it won't work anymore, see your cgrp release bpf program.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add a filter to tack only the configured hierarchy for v1. My first goal was to support cgroup v2 so that's what the first implementation does. Any cgroup v1 support might or might not work since we do not have tests for it.
Not sure I understand. Are you saying that cgroup ids are unique only within a specific hierarchy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly my point and with that linked bpf cgroup helper, it transparently handle it.
Yes cgroupv1 are separate cgroup mounts backed by kernfs, each kernfs node part of a mount has its unique ID that is the inode number. The allocation is predictable using IDR last time I checked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make sure I understand correctly, are you saying that it's possible for two cgroup nodes of different hierarchies to have the same kernfs id?