xref: /openbmc/linux/samples/bpf/hbm_kern.h (revision 4f727ece)
1 /* SPDX-License-Identifier: GPL-2.0
2  *
3  * Copyright (c) 2019 Facebook
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU General Public
7  * License as published by the Free Software Foundation.
8  *
9  * Include file for sample Host Bandwidth Manager (HBM) BPF programs
10  */
11 #define KBUILD_MODNAME "foo"
12 #include <stddef.h>
13 #include <stdbool.h>
14 #include <uapi/linux/bpf.h>
15 #include <uapi/linux/if_ether.h>
16 #include <uapi/linux/if_packet.h>
17 #include <uapi/linux/ip.h>
18 #include <uapi/linux/ipv6.h>
19 #include <uapi/linux/in.h>
20 #include <uapi/linux/tcp.h>
21 #include <uapi/linux/filter.h>
22 #include <uapi/linux/pkt_cls.h>
23 #include <net/ipv6.h>
24 #include <net/inet_ecn.h>
25 #include "bpf_endian.h"
26 #include "bpf_helpers.h"
27 #include "hbm.h"
28 
29 #define DROP_PKT	0
30 #define ALLOW_PKT	1
31 #define TCP_ECN_OK	1
32 
33 #define HBM_DEBUG 0  // Set to 1 to enable debugging
34 #if HBM_DEBUG
35 #define bpf_printk(fmt, ...)					\
36 ({								\
37 	char ____fmt[] = fmt;					\
38 	bpf_trace_printk(____fmt, sizeof(____fmt),		\
39 			 ##__VA_ARGS__);			\
40 })
41 #else
42 #define bpf_printk(fmt, ...)
43 #endif
44 
45 #define INITIAL_CREDIT_PACKETS	100
46 #define MAX_BYTES_PER_PACKET	1500
47 #define MARK_THRESH		(40 * MAX_BYTES_PER_PACKET)
48 #define DROP_THRESH		(80 * 5 * MAX_BYTES_PER_PACKET)
49 #define LARGE_PKT_DROP_THRESH	(DROP_THRESH - (15 * MAX_BYTES_PER_PACKET))
50 #define MARK_REGION_SIZE	(LARGE_PKT_DROP_THRESH - MARK_THRESH)
51 #define LARGE_PKT_THRESH	120
52 #define MAX_CREDIT		(100 * MAX_BYTES_PER_PACKET)
53 #define INIT_CREDIT		(INITIAL_CREDIT_PACKETS * MAX_BYTES_PER_PACKET)
54 
55 // rate in bytes per ns << 20
56 #define CREDIT_PER_NS(delta, rate) ((((u64)(delta)) * (rate)) >> 20)
57 
58 struct bpf_map_def SEC("maps") queue_state = {
59 	.type = BPF_MAP_TYPE_CGROUP_STORAGE,
60 	.key_size = sizeof(struct bpf_cgroup_storage_key),
61 	.value_size = sizeof(struct hbm_vqueue),
62 };
63 BPF_ANNOTATE_KV_PAIR(queue_state, struct bpf_cgroup_storage_key,
64 		     struct hbm_vqueue);
65 
66 struct bpf_map_def SEC("maps") queue_stats = {
67 	.type = BPF_MAP_TYPE_ARRAY,
68 	.key_size = sizeof(u32),
69 	.value_size = sizeof(struct hbm_queue_stats),
70 	.max_entries = 1,
71 };
72 BPF_ANNOTATE_KV_PAIR(queue_stats, int, struct hbm_queue_stats);
73 
74 struct hbm_pkt_info {
75 	bool	is_ip;
76 	bool	is_tcp;
77 	short	ecn;
78 };
79 
80 static __always_inline void hbm_get_pkt_info(struct __sk_buff *skb,
81 					     struct hbm_pkt_info *pkti)
82 {
83 	struct iphdr iph;
84 	struct ipv6hdr *ip6h;
85 
86 	bpf_skb_load_bytes(skb, 0, &iph, 12);
87 	if (iph.version == 6) {
88 		ip6h = (struct ipv6hdr *)&iph;
89 		pkti->is_ip = true;
90 		pkti->is_tcp = (ip6h->nexthdr == 6);
91 		pkti->ecn = (ip6h->flow_lbl[0] >> 4) & INET_ECN_MASK;
92 	} else if (iph.version == 4) {
93 		pkti->is_ip = true;
94 		pkti->is_tcp = (iph.protocol == 6);
95 		pkti->ecn = iph.tos & INET_ECN_MASK;
96 	} else {
97 		pkti->is_ip = false;
98 		pkti->is_tcp = false;
99 		pkti->ecn = 0;
100 	}
101 }
102 
103 static __always_inline void hbm_init_vqueue(struct hbm_vqueue *qdp, int rate)
104 {
105 		bpf_printk("Initializing queue_state, rate:%d\n", rate * 128);
106 		qdp->lasttime = bpf_ktime_get_ns();
107 		qdp->credit = INIT_CREDIT;
108 		qdp->rate = rate * 128;
109 }
110 
111 static __always_inline void hbm_update_stats(struct hbm_queue_stats *qsp,
112 					     int len,
113 					     unsigned long long curtime,
114 					     bool congestion_flag,
115 					     bool drop_flag)
116 {
117 	if (qsp != NULL) {
118 		// Following is needed for work conserving
119 		__sync_add_and_fetch(&(qsp->bytes_total), len);
120 		if (qsp->stats) {
121 			// Optionally update statistics
122 			if (qsp->firstPacketTime == 0)
123 				qsp->firstPacketTime = curtime;
124 			qsp->lastPacketTime = curtime;
125 			__sync_add_and_fetch(&(qsp->pkts_total), 1);
126 			if (congestion_flag || drop_flag) {
127 				__sync_add_and_fetch(&(qsp->pkts_marked), 1);
128 				__sync_add_and_fetch(&(qsp->bytes_marked), len);
129 			}
130 			if (drop_flag) {
131 				__sync_add_and_fetch(&(qsp->pkts_dropped), 1);
132 				__sync_add_and_fetch(&(qsp->bytes_dropped),
133 						     len);
134 			}
135 		}
136 	}
137 }
138