Skip to content

Commit d42cdc2

Browse files
committed
netstacklat: Add option to group by interface
Add the --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 <[email protected]>
1 parent 0dbcfc7 commit d42cdc2

File tree

4 files changed

+60
-10
lines changed

4 files changed

+60
-10
lines changed

netstacklat/netstacklat.bpf.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ volatile const struct netstacklat_bpf_config user_config = {
1818
.filter_pid = false,
1919
.filter_ifindex = false,
2020
.filter_cgroup = false,
21+
.groupby_ifindex = false,
2122
};
2223

2324
/*
@@ -35,7 +36,7 @@ struct sk_buff___old {
3536

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

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

144-
static void record_latency_since(ktime_t tstamp, enum netstacklat_hook hook)
144+
static void record_latency_since(ktime_t tstamp, const struct hist_key *key)
145145
{
146146
ktime_t latency = time_since(tstamp);
147147
if (latency >= 0)
148-
record_latency(latency, hook);
148+
record_latency(latency, key);
149149
}
150150

151151
static bool filter_ifindex(u32 ifindex)
@@ -186,6 +186,9 @@ static __u64 get_network_ns(struct sk_buff *skb, struct sock *sk)
186186

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

212-
if (!filter_ifindex(skb->skb_iif))
215+
ifindex = skb->skb_iif;
216+
if (!filter_ifindex(ifindex))
213217
return;
214218

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

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

221228
static bool filter_pid(u32 pid)
@@ -264,6 +271,7 @@ static bool filter_current_task(void)
264271
static void record_socket_latency(struct sock *sk, struct sk_buff *skb,
265272
ktime_t tstamp, enum netstacklat_hook hook)
266273
{
274+
struct hist_key key = { .hook = hook };
267275
u32 ifindex;
268276

269277
if (!filter_current_task())
@@ -276,7 +284,10 @@ static void record_socket_latency(struct sock *sk, struct sk_buff *skb,
276284
if (!filter_network_ns(get_network_ns(skb, sk)))
277285
return;
278286

279-
record_latency_since(tstamp, hook);
287+
if (user_config.groupby_ifindex)
288+
key.ifindex = ifindex;
289+
290+
record_latency_since(tstamp, &key);
280291
}
281292

282293
SEC("fentry/ip_rcv_core")

netstacklat/netstacklat.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ static const char *__doc__ =
5656
#define MAX_PARSED_PIDS 4096
5757
#define MAX_PARSED_IFACES 4096
5858

59+
#define ARG_GROUPBY_INTERFACE 256
60+
5961
typedef int (*t_parse_val_func)(const char *, void *);
6062

6163
struct hook_prog_collection {
@@ -96,6 +98,7 @@ static const struct option long_options[] = {
9698
{ "interfaces", required_argument, NULL, 'i' },
9799
{ "network-namespace", required_argument, NULL, 'n' },
98100
{ "cgroups", required_argument, NULL, 'c' },
101+
{ "groupby-interface", no_argument, NULL, ARG_GROUPBY_INTERFACE },
99102
{ 0, 0, 0, 0 }
100103
};
101104

@@ -560,6 +563,7 @@ static int parse_arguments(int argc, char *argv[],
560563
conf->nifindices = 0;
561564
conf->bpf_conf.filter_pid = false;
562565
conf->bpf_conf.filter_ifindex = false;
566+
conf->bpf_conf.groupby_ifindex = false;
563567

564568
conf->pids = calloc(MAX_PARSED_PIDS, sizeof(*conf->pids));
565569
conf->ifindices = calloc(MAX_PARSED_IFACES, sizeof(*conf->ifindices));
@@ -647,6 +651,9 @@ static int parse_arguments(int argc, char *argv[],
647651
conf->ncgroups += ret;
648652
conf->bpf_conf.filter_cgroup = true;
649653
break;
654+
case ARG_GROUPBY_INTERFACE:
655+
conf->bpf_conf.groupby_ifindex = true;
656+
break;
650657
case 'h': // help
651658
print_usage(stdout, argv[0]);
652659
exit(EXIT_SUCCESS);
@@ -817,13 +824,22 @@ static void print_log2hist(FILE *stream, size_t n, const __u64 hist[n],
817824
static void print_histkey(FILE *stream, const struct hist_key *key)
818825
{
819826
fprintf(stream, "%s", hook_to_str(key->hook));
827+
828+
if (key->ifindex)
829+
fprintf(stream, ", interface=%u", key->ifindex);
820830
}
821831

822832
static int cmp_histkey(const void *val1, const void *val2)
823833
{
824834
const struct hist_key *key1 = val1, *key2 = val2;
825835

826-
return key1->hook == key2->hook ? 0 : key1->hook > key2->hook ? 1 : -1;
836+
if (key1->hook != key2->hook)
837+
return key1->hook > key2->hook ? 1 : -1;
838+
839+
if (key1->ifindex != key2->ifindex)
840+
return key1->ifindex > key2->ifindex ? 1 : -1;
841+
842+
return 0;
827843
}
828844

829845
static int cmp_histentry(const void *val1, const void *val2)
@@ -1017,6 +1033,11 @@ static int init_histogram_buffer(struct histogram_buffer *buf,
10171033
max_hists++;
10181034
}
10191035

1036+
if (conf->bpf_conf.groupby_ifindex)
1037+
max_hists *= conf->bpf_conf.filter_ifindex ?
1038+
min(conf->nifindices, 64) :
1039+
32;
1040+
10201041
buf->hists = calloc(max_hists, sizeof(*buf->hists));
10211042
if (!buf->hists)
10221043
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
};
@@ -60,6 +70,7 @@ struct netstacklat_bpf_config {
6070
bool filter_pid;
6171
bool filter_ifindex;
6272
bool filter_cgroup;
73+
bool groupby_ifindex;
6374
};
6475

6576
#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)