1
// Copyright 2023 The Inspektor Gadget authors
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
15
//go:build !withoutebpf
25
"github.com/cilium/ebpf"
27
"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
28
"github.com/inspektor-gadget/inspektor-gadget/pkg/logger"
29
"github.com/inspektor-gadget/inspektor-gadget/pkg/types"
32
// Delay between each garbage collection run.
33
const garbageCollectorInterval = 1 * time.Second
35
// Maximum size of a batch to lookup and delete.
36
// Setting this higher reduces the number of syscalls to GC a full map, but uses more memory.
37
const garbageCollectorBatchSize = 256
39
// startGarbageCollector runs a background goroutine to delete old query timestamps
40
// from the DNS query_map. This ensures that queries that never receive a response
41
// are deleted from the map.
43
// The garbage collector goroutine terminates when the context is done.
44
func startGarbageCollector(ctx context.Context, logger logger.Logger, dnsTimeout time.Duration, queryMap *ebpf.Map) {
45
if !gadgets.HasBpfKtimeGetBootNs() {
46
logger.Warnf("DNS latency will not be reported (requires Linux kernel 5.8 or later)")
50
logger.Debugf("starting garbage collection for DNS tracer with dnsTimeout %s", dnsTimeout)
52
// Allocate the keys/values arrays once and reuse for each iteration.
54
keysBatch [garbageCollectorBatchSize]dnsQueryKeyT
55
valuesBatch [garbageCollectorBatchSize]uint64
58
ticker := time.NewTicker(garbageCollectorInterval)
64
logger.Debugf("stopping garbage collection for DNS tracer")
68
logger.Debugf("executing DNS query map garbage collection")
69
numDeleted, err := collectGarbage(dnsTimeout, queryMap, keysBatch[:], valuesBatch[:])
71
logger.Errorf("collecting garbage: %w", err)
72
} else if numDeleted > 0 {
73
logger.Debugf("deleted %d entries from DNS query map", numDeleted)
80
func collectGarbage(dnsTimeout time.Duration, queryMap *ebpf.Map, keysBatch []dnsQueryKeyT, valuesBatch []uint64) (int, error) {
82
keysToDelete []dnsQueryKeyT
83
prevKeyOut interface{}
84
nextKeyOut dnsQueryKeyT
87
// Nil means start from the beginning.
88
// Type of prevKeyOut is interface{}, not dnsQueryKeyT, to ensure that the first call
89
// to BatchLookup sees an untyped nil (not an interface with value nil); otherwise it crashes.
93
n, err := queryMap.BatchLookup(prevKeyOut, &nextKeyOut, keysBatch, valuesBatch, nil)
94
if err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) {
95
return 0, fmt.Errorf("looking up keys in query map: %w", err)
98
cutoffTs := types.Time(time.Now().Add(-1 * dnsTimeout).UnixNano())
99
for i := 0; i < n; i++ {
100
ts := gadgets.WallTimeFromBootTime(valuesBatch[i])
102
keysToDelete = append(keysToDelete, keysBatch[i])
106
if errors.Is(err, ebpf.ErrKeyNotExist) {
107
// This error means there are no more keys after the ones we just read.
111
prevKeyOut = nextKeyOut
114
if len(keysToDelete) == 0 {
118
n, err := queryMap.BatchDelete(keysToDelete, nil)
120
return 0, fmt.Errorf("deleting keys from query map: %w", err)