cgroup.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package link
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "github.com/cilium/ebpf"
  7. )
  8. type cgroupAttachFlags uint32
  9. // cgroup attach flags
  10. const (
  11. flagAllowOverride cgroupAttachFlags = 1 << iota
  12. flagAllowMulti
  13. flagReplace
  14. )
  15. type CgroupOptions struct {
  16. // Path to a cgroupv2 folder.
  17. Path string
  18. // One of the AttachCgroup* constants
  19. Attach ebpf.AttachType
  20. // Program must be of type CGroup*, and the attach type must match Attach.
  21. Program *ebpf.Program
  22. }
  23. // AttachCgroup links a BPF program to a cgroup.
  24. func AttachCgroup(opts CgroupOptions) (Link, error) {
  25. cgroup, err := os.Open(opts.Path)
  26. if err != nil {
  27. return nil, fmt.Errorf("can't open cgroup: %s", err)
  28. }
  29. clone, err := opts.Program.Clone()
  30. if err != nil {
  31. cgroup.Close()
  32. return nil, err
  33. }
  34. var cg Link
  35. cg, err = newLinkCgroup(cgroup, opts.Attach, clone)
  36. if errors.Is(err, ErrNotSupported) {
  37. cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti)
  38. }
  39. if errors.Is(err, ErrNotSupported) {
  40. cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride)
  41. }
  42. if err != nil {
  43. cgroup.Close()
  44. clone.Close()
  45. return nil, err
  46. }
  47. return cg, nil
  48. }
  49. type progAttachCgroup struct {
  50. cgroup *os.File
  51. current *ebpf.Program
  52. attachType ebpf.AttachType
  53. flags cgroupAttachFlags
  54. }
  55. var _ Link = (*progAttachCgroup)(nil)
  56. func (cg *progAttachCgroup) isLink() {}
  57. func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) {
  58. if flags&flagAllowMulti > 0 {
  59. if err := haveProgAttachReplace(); err != nil {
  60. return nil, fmt.Errorf("can't support multiple programs: %w", err)
  61. }
  62. }
  63. err := RawAttachProgram(RawAttachProgramOptions{
  64. Target: int(cgroup.Fd()),
  65. Program: prog,
  66. Flags: uint32(flags),
  67. Attach: attach,
  68. })
  69. if err != nil {
  70. return nil, fmt.Errorf("cgroup: %w", err)
  71. }
  72. return &progAttachCgroup{cgroup, prog, attach, flags}, nil
  73. }
  74. func (cg *progAttachCgroup) Close() error {
  75. defer cg.cgroup.Close()
  76. defer cg.current.Close()
  77. err := RawDetachProgram(RawDetachProgramOptions{
  78. Target: int(cg.cgroup.Fd()),
  79. Program: cg.current,
  80. Attach: cg.attachType,
  81. })
  82. if err != nil {
  83. return fmt.Errorf("close cgroup: %s", err)
  84. }
  85. return nil
  86. }
  87. func (cg *progAttachCgroup) Update(prog *ebpf.Program) error {
  88. new, err := prog.Clone()
  89. if err != nil {
  90. return err
  91. }
  92. args := RawAttachProgramOptions{
  93. Target: int(cg.cgroup.Fd()),
  94. Program: prog,
  95. Attach: cg.attachType,
  96. Flags: uint32(cg.flags),
  97. }
  98. if cg.flags&flagAllowMulti > 0 {
  99. // Atomically replacing multiple programs requires at least
  100. // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf
  101. // program in MULTI mode")
  102. args.Flags |= uint32(flagReplace)
  103. args.Replace = cg.current
  104. }
  105. if err := RawAttachProgram(args); err != nil {
  106. new.Close()
  107. return fmt.Errorf("can't update cgroup: %s", err)
  108. }
  109. cg.current.Close()
  110. cg.current = new
  111. return nil
  112. }
  113. func (cg *progAttachCgroup) Pin(string) error {
  114. return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
  115. }
  116. func (cg *progAttachCgroup) Unpin() error {
  117. return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
  118. }
  119. func (cg *progAttachCgroup) Info() (*Info, error) {
  120. return nil, fmt.Errorf("can't get cgroup info: %w", ErrNotSupported)
  121. }
  122. type linkCgroup struct {
  123. RawLink
  124. }
  125. var _ Link = (*linkCgroup)(nil)
  126. func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {
  127. link, err := AttachRawLink(RawLinkOptions{
  128. Target: int(cgroup.Fd()),
  129. Program: prog,
  130. Attach: attach,
  131. })
  132. if err != nil {
  133. return nil, err
  134. }
  135. return &linkCgroup{*link}, err
  136. }