Repository navigation
Expand file tree
/
Copy pathvcc319.patch
More file actions
258 lines (249 loc) · 8.15 KB
/
Copy pathvcc319.patch
File metadata and controls
258 lines (249 loc) · 8.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 67309ec..a42df5e 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -309,6 +309,23 @@ struct tcp_sock {
struct tcp_md5sig_info __rcu *md5sig_info;
#endif
+ struct {
+ u32 acked_bytes_ecn;
+ u32 acked_bytes_total;
+ u32 prior_snd_una;
+ u32 prior_rcv_nxt;
+ u32 dctcp_alpha;
+ u32 next_seq;
+ u32 ce_state;
+ u32 delayed_ack_reserved;
+ u32 target_window;
+ u32 last_window;
+ u32 last_cwnd_red_ts;
+ u32 last_cwnd_inc_ts;
+ u32 bytes_in_flight;
+ u32 last_prr_acked;
+ } vtcp_state;
+
/* TCP fastopen related information */
struct tcp_fastopen_request *fastopen_req;
/* fastopen_rsk points to request_sock that resulted in this big
@@ -378,4 +395,35 @@ static inline int fastopen_init_queue(struct sock *sk, int backlog)
return 0;
}
+/**
+ * Initialize tcp parameters for this socket
+ */
+static inline int vtcp_init(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ tp->vtcp_state.prior_snd_una = 0;
+ tp->vtcp_state.prior_rcv_nxt = 0;
+
+ tp->vtcp_state.dctcp_alpha = 1024U;
+
+ tp->vtcp_state.delayed_ack_reserved = 0;
+ tp->vtcp_state.ce_state = 0;
+
+ tp->vtcp_state.next_seq = tp->snd_nxt;
+
+ tp->vtcp_state.acked_bytes_ecn = 0;
+ tp->vtcp_state.acked_bytes_total = 0;
+
+ tp->vtcp_state.target_window = 0;
+ tp->vtcp_state.last_window = 0;
+
+ tp->vtcp_state.last_cwnd_red_ts = 0;
+ tp->vtcp_state.last_cwnd_inc_ts = 0;
+ tp->vtcp_state.bytes_in_flight = 0;
+
+ tp->vtcp_state.last_prr_acked = 0;
+ return 0;
+}
+
#endif /* _LINUX_TCP_H */
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 0ffef1a..d8e1674 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -75,6 +75,7 @@ struct netns_ipv4 {
struct local_ports ip_local_ports;
int sysctl_tcp_ecn;
+ int sysctl_tcp_vtcp;
int sysctl_ip_no_pmtu_disc;
int sysctl_ip_fwd_use_pmtu;
int sysctl_ip_nonlocal_bind;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index e0ee384..a183b0c 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -828,6 +828,13 @@ static struct ctl_table ipv4_net_table[] = {
.proc_handler = proc_dointvec
},
{
+ .procname = "tcp_vtcp",
+ .data = &init_net.ipv4.sysctl_tcp_vtcp,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
.procname = "ip_local_port_range",
.maxlen = sizeof(init_net.ipv4.ip_local_ports.range),
.data = &init_net.ipv4.ip_local_ports.range,
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 3075723..3b11015 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -422,6 +422,7 @@ void tcp_init_sock(struct sock *sk)
sock_update_memcg(sk);
sk_sockets_allocated_inc(sk);
local_bh_enable();
+ vtcp_init(sk);
}
EXPORT_SYMBOL(tcp_init_sock);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 075ab4d..f3ed0dd5 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3292,7 +3292,7 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
if (likely(!tcp_hdr(skb)->syn))
nwin <<= tp->rx_opt.snd_wscale;
-
+
if (tcp_may_update_window(tp, ack, ack_seq, nwin)) {
flag |= FLAG_WIN_UPDATE;
tcp_update_wl(tp, ack_seq);
@@ -3398,11 +3398,11 @@ static inline void tcp_in_ack_event(struct sock *sk, u32 flags)
}
/* This routine deals with incoming acks, but not outgoing ones. */
-static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
+static int __tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
- u32 prior_snd_una = tp->snd_una;
+ u32 prior_snd_una = tp->snd_una; // first byte we want ack for
u32 ack_seq = TCP_SKB_CB(skb)->seq;
u32 ack = TCP_SKB_CB(skb)->ack_seq;
bool is_dupack = false;
@@ -3560,6 +3560,99 @@ old_ack:
return 0;
}
+static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
+{
+ const struct net *net = sock_net(sk);
+
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct tcphdr *th = tcp_hdr(skb);
+ u32 prior_snd_una = tp->snd_una; // first byte we want ack for
+ u32 ack = TCP_SKB_CB(skb)->ack_seq;
+ unsigned int cur_mss = tcp_current_mss(sk);
+
+ unsigned short shiftedwindow;
+
+ /*
+ * Guard this so that sysctl can act as a proxy for turning on
+ * and off the virtual Ecn behavior.
+ */
+ if (net->ipv4.sysctl_tcp_vtcp == 1) {
+ if (before(ack, prior_snd_una)) {
+ return __tcp_ack(sk, skb, flag);
+ }
+ /*
+ * This block initiates throttling.
+ * first: does current packet have ECE?
+ * second: is this not a syn? (ECN on SYN is negotiation, not congestion signal)
+ * third: is ECN on for this socket? (ECN is set to on during connection establishment if vtcp is on)
+ * fourth: is time running?
+ */
+ if (th->ece && !th->syn && (tp->ecn_flags & TCP_ECN_OK)
+ && (!tp->vtcp_state.last_cwnd_red_ts
+ || after(ack, tp->vtcp_state.last_cwnd_red_ts))) {
+
+ if (tp->vtcp_state.ce_state != 0) {
+ // in throttled growth state, halve window and start reducing
+ tp->vtcp_state.target_window = max(tp->vtcp_state.last_window/2U, 2*cur_mss);
+ tp->vtcp_state.last_prr_acked = ack - 2 * cur_mss ;
+ tp->vtcp_state.ce_state = 2; // decreasing mode
+
+ } else if (tp->vtcp_state.ce_state==0) {
+ // state transfer: from no throttling to reducing window
+ tp->vtcp_state.ce_state = 2;
+ tp->vtcp_state.target_window = max(tp->snd_cwnd*cur_mss/2U, 2*cur_mss);
+ tp->vtcp_state.last_window = tp->snd_cwnd*cur_mss;
+ }
+
+ tp->vtcp_state.last_cwnd_red_ts = tp->snd_nxt;
+ tp->vtcp_state.bytes_in_flight = 0;
+ tcp_ecn_queue_cwr(tp);
+ }
+
+
+ if (tp->vtcp_state.ce_state==2) { // in decreasing mode
+
+ if (ack - prior_snd_una > tcp_packets_in_flight(tp)*cur_mss) {
+ tp->vtcp_state.last_window = tp->vtcp_state.target_window;
+ } else {
+ unsigned segments_acked = (ack - tp->vtcp_state.last_prr_acked)/cur_mss;
+ unsigned snd_cnt = segments_acked / 2;
+ tp->vtcp_state.last_prr_acked += cur_mss * snd_cnt * 2;
+ tp->vtcp_state.last_window =
+ /* max(tp->vtcp_state.target_window, tcp_packets_in_flight(tp)*cur_mss-(ack-prior_snd_una), */
+ max(tp->vtcp_state.target_window, tp->snd_nxt-ack + cur_mss * snd_cnt);
+ }
+
+
+ shiftedwindow = (unsigned short)((tp->vtcp_state.last_window >> tp->rx_opt.snd_wscale) + 1);
+ th->window = htons(shiftedwindow);
+
+ if (tp->vtcp_state.last_window <= tp->vtcp_state.target_window ) {
+ tp->vtcp_state.ce_state = 1;
+ }
+
+ } else if (tp->vtcp_state.ce_state == 1) {
+
+ // throttled growth state
+ tp->vtcp_state.bytes_in_flight += (ack - prior_snd_una);
+ if (tp->vtcp_state.bytes_in_flight >= tp->vtcp_state.last_window) {
+ tp->vtcp_state.last_window += (tp->vtcp_state.bytes_in_flight*cur_mss) / tp->vtcp_state.last_window;
+ tp->vtcp_state.last_cwnd_inc_ts = tcp_time_stamp;
+ tp->vtcp_state.bytes_in_flight = 0;
+ }
+
+ shiftedwindow = (unsigned short)(tp->vtcp_state.last_window >> tp->rx_opt.snd_wscale) + 1;
+ th->window = htons(shiftedwindow);
+ }
+
+ // always shield the guest from ECN
+ th->ece = 0;
+ }
+
+ // We're a wrapper, remember =] ?
+ return __tcp_ack(sk, skb, flag);
+}
+
/* Look for tcp options. Normally only called on SYN and SYNACK packets.
* But, this can also be called on packets in the established flow when
* the fast version below fails.
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index d22f544..c7bb65f 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2457,6 +2457,7 @@ static int __net_init tcp_sk_init(struct net *net)
*per_cpu_ptr(net->ipv4.tcp_sk, cpu) = sk;
}
net->ipv4.sysctl_tcp_ecn = 2;
+ net->ipv4.sysctl_tcp_vtcp = 0;
return 0;
fail:
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 65caf8b..35a5a5b 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -333,7 +333,7 @@ static void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb)
static void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
- bool use_ecn = sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
+ bool use_ecn = sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 || sock_net(sk)->ipv4.sysctl_tcp_vtcp == 1 ||
tcp_ca_needs_ecn(sk);
if (!use_ecn) {