Skip to content

Commit d59c56f

Browse files
committed
netstacklat: Add option to group by interface
Add the -I/--groupby-interface option to collect and report the data on a per-interface (or rather ifindex) basis. Note that the network interfaces are tracked based on their ifindex, so if network namespace filtering has been disabled and there exists interfaces in different namespaces with a common ifindex, their data will be merged into the same histogram. Always write the interface index rather than the interface name in the output. While the interface name for the same network namespace as the user space agent runs in can easily be retrieved with e.g. if_indextoname(), that will only be valid if the user has configured netstacklat to only monitor its own network namespace. If a different network namespace is monitored, or filtration for network namespaces is disabled, translating to the interface names in the current namespace might produce misleading results. An alternative could be to print out the interface names in case the current network namespace is the one monitored (the default), or the index if there's a risk that the data might be from a different namespace. However, in addition to that added complexity, that will produce somewhat inconsistent output (i.e. you might get interface names or interface indices depending on how you configure netstacklat). Signed-off-by: Simon Sundberg <simon.sundberg@kau.se>
1 parent ebeb15e commit d59c56f

File tree

4 files changed

+59
-10
lines changed

4 files changed

+59
-10
lines changed

netstacklat/netstacklat.bpf.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ volatile const struct netstacklat_bpf_config user_config = {
1919
.filter_ifindex = false,
2020
.filter_cgroup = false,
2121
.filter_nonempty_sockqueue = false,
22+
.groupby_ifindex = false,
2223
};
2324

2425
/*
@@ -36,7 +37,7 @@ struct sk_buff___old {
3637

3738
struct {
3839
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
39-
__uint(max_entries, HIST_NBUCKETS * NETSTACKLAT_N_HOOKS);
40+
__uint(max_entries, HIST_NBUCKETS * NETSTACKLAT_N_HOOKS * 16);
4041
__type(key, struct hist_key);
4142
__type(value, u64);
4243
} netstack_latency_seconds SEC(".maps");
@@ -135,18 +136,17 @@ static ktime_t time_since(ktime_t tstamp)
135136
return now - tstamp;
136137
}
137138

138-
static void record_latency(ktime_t latency, enum netstacklat_hook hook)
139+
static void record_latency(ktime_t latency, const struct hist_key *key)
139140
{
140-
struct hist_key key = { .hook = hook };
141-
increment_exp2_histogram_nosync(&netstack_latency_seconds, key, latency,
141+
increment_exp2_histogram_nosync(&netstack_latency_seconds, *key, latency,
142142
HIST_MAX_LATENCY_SLOT);
143143
}
144144

145-
static void record_latency_since(ktime_t tstamp, enum netstacklat_hook hook)
145+
static void record_latency_since(ktime_t tstamp, const struct hist_key *key)
146146
{
147147
ktime_t latency = time_since(tstamp);
148148
if (latency >= 0)
149-
record_latency(latency, hook);
149+
record_latency(latency, key);
150150
}
151151

152152
static bool filter_ifindex(u32 ifindex)
@@ -187,6 +187,9 @@ static __u64 get_network_ns(struct sk_buff *skb, struct sock *sk)
187187

188188
static void record_skb_latency(struct sk_buff *skb, struct sock *sk, enum netstacklat_hook hook)
189189
{
190+
struct hist_key key = { .hook = hook };
191+
u32 ifindex;
192+
190193
if (bpf_core_field_exists(skb->tstamp_type)) {
191194
/*
192195
* For kernels >= v6.11 the tstamp_type being non-zero
@@ -210,13 +213,17 @@ static void record_skb_latency(struct sk_buff *skb, struct sock *sk, enum netsta
210213
return;
211214
}
212215

213-
if (!filter_ifindex(skb->skb_iif))
216+
ifindex = skb->skb_iif;
217+
if (!filter_ifindex(ifindex))
214218
return;
215219

216220
if (!filter_network_ns(get_network_ns(skb, sk)))
217221
return;
218222

219-
record_latency_since(skb->tstamp, hook);
223+
if (user_config.groupby_ifindex)
224+
key.ifindex = ifindex;
225+
226+
record_latency_since(skb->tstamp, &key);
220227
}
221228

222229
static bool filter_pid(u32 pid)
@@ -286,6 +293,7 @@ static bool filter_nonempty_sockqueue(struct sock *sk)
286293
static void record_socket_latency(struct sock *sk, struct sk_buff *skb,
287294
ktime_t tstamp, enum netstacklat_hook hook)
288295
{
296+
struct hist_key key = { .hook = hook };
289297
u32 ifindex;
290298

291299
if (!filter_nonempty_sockqueue(sk))
@@ -301,7 +309,10 @@ static void record_socket_latency(struct sock *sk, struct sk_buff *skb,
301309
if (!filter_network_ns(get_network_ns(skb, sk)))
302310
return;
303311

304-
record_latency_since(tstamp, hook);
312+
if (user_config.groupby_ifindex)
313+
key.ifindex = ifindex;
314+
315+
record_latency_since(tstamp, &key);
305316
}
306317

307318
SEC("fentry/ip_rcv_core")

netstacklat/netstacklat.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ static const struct option long_options[] = {
9797
{ "network-namespace", required_argument, NULL, 'n' },
9898
{ "cgroups", required_argument, NULL, 'c' },
9999
{ "nonempty-queue", no_argument, NULL, 'q' },
100+
{ "groupby-interface", no_argument, NULL, 'I' },
100101
{ 0, 0, 0, 0 }
101102
};
102103

@@ -562,6 +563,7 @@ static int parse_arguments(int argc, char *argv[],
562563
conf->bpf_conf.filter_pid = false;
563564
conf->bpf_conf.filter_ifindex = false;
564565
conf->bpf_conf.filter_nonempty_sockqueue = false;
566+
conf->bpf_conf.groupby_ifindex = false;
565567

566568
conf->pids = calloc(MAX_PARSED_PIDS, sizeof(*conf->pids));
567569
conf->ifindices = calloc(MAX_PARSED_IFACES, sizeof(*conf->ifindices));
@@ -649,9 +651,13 @@ static int parse_arguments(int argc, char *argv[],
649651
conf->ncgroups += ret;
650652
conf->bpf_conf.filter_cgroup = true;
651653
break;
654+
652655
case 'q': // nonempty-queue
653656
conf->bpf_conf.filter_nonempty_sockqueue = true;
654657
break;
658+
case 'I': // groupby-interface
659+
conf->bpf_conf.groupby_ifindex = true;
660+
break;
655661
case 'h': // help
656662
print_usage(stdout, argv[0]);
657663
exit(EXIT_SUCCESS);
@@ -822,13 +828,22 @@ static void print_log2hist(FILE *stream, size_t n, const __u64 hist[n],
822828
static void print_histkey(FILE *stream, const struct hist_key *key)
823829
{
824830
fprintf(stream, "%s", hook_to_str(key->hook));
831+
832+
if (key->ifindex)
833+
fprintf(stream, ", interface=%u", key->ifindex);
825834
}
826835

827836
static int cmp_histkey(const void *val1, const void *val2)
828837
{
829838
const struct hist_key *key1 = val1, *key2 = val2;
830839

831-
return key1->hook == key2->hook ? 0 : key1->hook > key2->hook ? 1 : -1;
840+
if (key1->hook != key2->hook)
841+
return key1->hook > key2->hook ? 1 : -1;
842+
843+
if (key1->ifindex != key2->ifindex)
844+
return key1->ifindex > key2->ifindex ? 1 : -1;
845+
846+
return 0;
832847
}
833848

834849
static int cmp_histentry(const void *val1, const void *val2)
@@ -1022,6 +1037,11 @@ static int init_histogram_buffer(struct histogram_buffer *buf,
10221037
max_hists++;
10231038
}
10241039

1040+
if (conf->bpf_conf.groupby_ifindex)
1041+
max_hists *= conf->bpf_conf.filter_ifindex ?
1042+
min(conf->nifindices, 64) :
1043+
32;
1044+
10251045
buf->hists = calloc(max_hists, sizeof(*buf->hists));
10261046
if (!buf->hists)
10271047
return -errno;

netstacklat/netstacklat.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@
3333
})
3434
#endif
3535

36+
#ifndef min
37+
#define min(a, b) \
38+
({ \
39+
typeof(a) _a = (a); \
40+
typeof(b) _b = (b); \
41+
_a < _b ? _a : _b; \
42+
})
43+
#endif
44+
3645
enum netstacklat_hook {
3746
NETSTACKLAT_HOOK_INVALID = 0,
3847
NETSTACKLAT_HOOK_IP_RCV,
@@ -51,6 +60,7 @@ enum netstacklat_hook {
5160
* member is named "bucket" and is the histogram bucket index.
5261
*/
5362
struct hist_key {
63+
__u32 ifindex;
5464
__u16 hook; // need well defined size for ebpf-exporter to decode
5565
__u16 bucket; // needs to be last to be compatible with ebpf-exporter
5666
};
@@ -61,6 +71,7 @@ struct netstacklat_bpf_config {
6171
bool filter_ifindex;
6272
bool filter_cgroup;
6373
bool filter_nonempty_sockqueue;
74+
bool groupby_ifindex;
6475
};
6576

6677
#endif

netstacklat/netstacklat.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ metrics:
77
bucket_max: 34
88
bucket_multiplier: 0.000000001 # nanoseconds to seconds
99
labels:
10+
- name: iface
11+
size: 4
12+
decoders:
13+
# If including output from a different network namespace than ebpf-exporter
14+
# you probably just want to decode as a uint (ifindex) instead
15+
# - name: uint # For the ifname decoder you apparently don't first need a uint decoder like the others
16+
- name: ifname
1017
- name: hook
1118
size: 2
1219
decoders:

0 commit comments

Comments
 (0)