diff --git a/pping/pping.c b/pping/pping.c index 1fd2732..63ce904 100644 --- a/pping/pping.c +++ b/pping/pping.c @@ -821,6 +821,15 @@ static void print_event_json(const union pping_event *e) jsonw_end_object(json_ctx); } +static void warn_map_full(const struct map_full_event *e) +{ + print_ns_datetime(stderr, e->timestamp); + fprintf(stderr, " Warning: Unable to create %s entry for flow ", + e->map == PPING_MAP_FLOWSTATE ? "flow" : "timestamp"); + print_flow_ppvizformat(stderr, &e->flow); + fprintf(stderr, "\n"); +} + static void handle_event(void *ctx, int cpu, void *data, __u32 data_size) { const union pping_event *e = data; @@ -829,6 +838,9 @@ static void handle_event(void *ctx, int cpu, void *data, __u32 data_size) return; switch (e->event_type) { + case EVENT_TYPE_MAP_FULL: + warn_map_full(&e->map_event); + break; case EVENT_TYPE_RTT: case EVENT_TYPE_FLOW: print_event_func(e); diff --git a/pping/pping.h b/pping/pping.h index d8bd240..32ac428 100644 --- a/pping/pping.h +++ b/pping/pping.h @@ -14,6 +14,7 @@ typedef __u64 fixpoint64; /* For the event_type members of rtt_event and flow_event */ #define EVENT_TYPE_FLOW 1 #define EVENT_TYPE_RTT 2 +#define EVENT_TYPE_MAP_FULL 3 enum __attribute__((__packed__)) flow_event_type { FLOW_EVENT_NONE, @@ -37,6 +38,11 @@ enum __attribute__((__packed__)) flow_event_source { EVENT_SOURCE_USERSPACE }; +enum __attribute__((__packed__)) pping_map { + PPING_MAP_FLOWSTATE = 0, + PPING_MAP_PACKETTS +}; + struct bpf_config { __u64 rate_limit; fixpoint64 rtt_rate; @@ -133,10 +139,23 @@ struct flow_event { __u8 reserved; }; +/* + * An event indicating that a new entry could not be created the map due to the + * map being full. + */ +struct map_full_event { + __u64 event_type; + __u64 timestamp; + struct network_tuple flow; + enum pping_map map; + __u8 reserved[3]; +}; + union pping_event { __u64 event_type; struct rtt_event rtt_event; struct flow_event flow_event; + struct map_full_event map_event; }; /* diff --git a/pping/pping_kern.c b/pping/pping_kern.c index 90ed44b..da993cf 100644 --- a/pping/pping_kern.c +++ b/pping/pping_kern.c @@ -28,6 +28,9 @@ // Mask for IPv6 flowlabel + traffic class - used in fib lookup #define IPV6_FLOWINFO_MASK __cpu_to_be32(0x0FFFFFFF) +// Emit a warning max once per second when failing to add entry to map +#define WARN_MAP_FULL_INTERVAL 1000000000UL + /* * This struct keeps track of the data and data_end pointers from the xdp_md or * __skb_buff contexts, as well as a currently parsed to position kept in nh. @@ -78,6 +81,8 @@ struct packet_info { char _license[] SEC("license") = "GPL"; // Global config struct - set from userspace static volatile const struct bpf_config config = {}; +static volatile __u64 last_warn_time[2] = { 0 }; + // Map definitions struct { @@ -441,11 +446,35 @@ static void send_flow_event(void *ctx, struct packet_info *p_info, bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &fe, sizeof(fe)); } +/* + * Send a map-full event for the map. + * Will only trigger once every WARN_MAP_FULL_INTERVAL + */ +static void send_map_full_event(void *ctx, struct packet_info *p_info, + enum pping_map map) +{ + struct map_full_event me; + + if (p_info->time < last_warn_time[map] || + p_info->time - last_warn_time[map] < WARN_MAP_FULL_INTERVAL) + return; + + last_warn_time[map] = p_info->time; + + __builtin_memset(&me, 0, sizeof(me)); + me.event_type = EVENT_TYPE_MAP_FULL; + me.timestamp = p_info->time; + me.flow = p_info->pid.flow; + me.map = map; + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &me, sizeof(me)); +} + /* * Attempt to create a new flow-state. * Returns a pointer to the flow_state if successful, NULL otherwise */ -static struct flow_state *create_flow(struct packet_info *p_info) +static struct flow_state *create_flow(void *ctx, struct packet_info *p_info) { struct flow_state new_state = { 0 }; @@ -455,8 +484,10 @@ static struct flow_state *create_flow(struct packet_info *p_info) EVENT_REASON_FIRST_OBS_PCKT; if (bpf_map_update_elem(&flow_state, &p_info->pid.flow, &new_state, - BPF_NOEXIST) != 0) + BPF_NOEXIST) != 0) { + send_map_full_event(ctx, p_info, PPING_MAP_FLOWSTATE); return NULL; + } return bpf_map_lookup_elem(&flow_state, &p_info->pid.flow); } @@ -474,7 +505,7 @@ static struct flow_state *update_flow(void *ctx, struct packet_info *p_info, !(p_info->event_type == FLOW_EVENT_CLOSING || p_info->event_type == FLOW_EVENT_CLOSING_BOTH)) { *new_flow = true; - f_state = create_flow(p_info); + f_state = create_flow(ctx, p_info); } if (!f_state) @@ -610,8 +641,9 @@ static void pping_timestamp_packet(struct flow_state *f_state, void *ctx, */ f_state->last_timestamp = p_info->time; - bpf_map_update_elem(&packet_ts, &p_info->pid, &p_info->time, - BPF_NOEXIST); + if (bpf_map_update_elem(&packet_ts, &p_info->pid, &p_info->time, + BPF_NOEXIST) != 0) + send_map_full_event(ctx, p_info, PPING_MAP_PACKETTS); } /*