1218155Slstewart/*-
2218155Slstewart * Copyright (c) 2009-2010
3218155Slstewart *	Swinburne University of Technology, Melbourne, Australia
4218155Slstewart * Copyright (c) 2010-2011 The FreeBSD Foundation
5218155Slstewart * All rights reserved.
6218155Slstewart *
7218155Slstewart * This software was developed at the Centre for Advanced Internet
8220560Slstewart * Architectures, Swinburne University of Technology, by David Hayes and
9220560Slstewart * Lawrence Stewart, made possible in part by a grant from the Cisco University
10220560Slstewart * Research Program Fund at Community Foundation Silicon Valley.
11218155Slstewart *
12218155Slstewart * Portions of this software were developed at the Centre for Advanced Internet
13218155Slstewart * Architectures, Swinburne University of Technology, Melbourne, Australia by
14218155Slstewart * David Hayes under sponsorship from the FreeBSD Foundation.
15218155Slstewart *
16218155Slstewart * Redistribution and use in source and binary forms, with or without
17218155Slstewart * modification, are permitted provided that the following conditions
18218155Slstewart * are met:
19218155Slstewart * 1. Redistributions of source code must retain the above copyright
20218155Slstewart *    notice, this list of conditions and the following disclaimer.
21218155Slstewart * 2. Redistributions in binary form must reproduce the above copyright
22218155Slstewart *    notice, this list of conditions and the following disclaimer in the
23218155Slstewart *    documentation and/or other materials provided with the distribution.
24218155Slstewart *
25218155Slstewart * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26218155Slstewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27218155Slstewart * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28218155Slstewart * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29218155Slstewart * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30218155Slstewart * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31218155Slstewart * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32218155Slstewart * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33218155Slstewart * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34218155Slstewart * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35218155Slstewart * SUCH DAMAGE.
36218155Slstewart */
37218155Slstewart
38218155Slstewart/*
39218155Slstewart * An implementation of the CAIA-Hamilton delay based congestion control
40218155Slstewart * algorithm, based on "Improved coexistence and loss tolerance for delay based
41218155Slstewart * TCP congestion control" by D. A. Hayes and G. Armitage., in 35th Annual IEEE
42218155Slstewart * Conference on Local Computer Networks (LCN 2010), Denver, Colorado, USA,
43218155Slstewart * 11-14 October 2010.
44218155Slstewart *
45218155Slstewart * Originally released as part of the NewTCP research project at Swinburne
46220560Slstewart * University of Technology's Centre for Advanced Internet Architectures,
47220560Slstewart * Melbourne, Australia, which was made possible in part by a grant from the
48220560Slstewart * Cisco University Research Program Fund at Community Foundation Silicon
49220560Slstewart * Valley. More details are available at:
50218155Slstewart *   http://caia.swin.edu.au/urp/newtcp/
51218155Slstewart */
52218155Slstewart
53218155Slstewart#include <sys/cdefs.h>
54218155Slstewart__FBSDID("$FreeBSD: releng/10.3/sys/netinet/cc/cc_chd.c 273736 2014-10-27 14:38:00Z hselasky $");
55218155Slstewart
56218155Slstewart#include <sys/param.h>
57218155Slstewart#include <sys/kernel.h>
58218155Slstewart#include <sys/khelp.h>
59218155Slstewart#include <sys/limits.h>
60218155Slstewart#include <sys/malloc.h>
61218155Slstewart#include <sys/module.h>
62218155Slstewart#include <sys/queue.h>
63218155Slstewart#include <sys/socket.h>
64218155Slstewart#include <sys/socketvar.h>
65218155Slstewart#include <sys/sysctl.h>
66218155Slstewart#include <sys/systm.h>
67218155Slstewart
68218155Slstewart#include <net/if.h>
69218155Slstewart#include <net/vnet.h>
70218155Slstewart
71218155Slstewart#include <netinet/cc.h>
72218155Slstewart#include <netinet/tcp_seq.h>
73218155Slstewart#include <netinet/tcp_timer.h>
74218155Slstewart#include <netinet/tcp_var.h>
75218155Slstewart
76218155Slstewart#include <netinet/cc/cc_module.h>
77218155Slstewart
78218155Slstewart#include <netinet/khelp/h_ertt.h>
79218155Slstewart
80218155Slstewart#define	CAST_PTR_INT(X)	(*((int*)(X)))
81218155Slstewart
82218155Slstewart/*
83218155Slstewart * Private signal type for rate based congestion signal.
84218155Slstewart * See <netinet/cc.h> for appropriate bit-range to use for private signals.
85218155Slstewart */
86218155Slstewart#define	CC_CHD_DELAY	0x02000000
87218155Slstewart
88218155Slstewart/* Largest possible number returned by random(). */
89218155Slstewart#define	RANDOM_MAX	INT_MAX
90218155Slstewart
91218155Slstewartstatic void	chd_ack_received(struct cc_var *ccv, uint16_t ack_type);
92218155Slstewartstatic void	chd_cb_destroy(struct cc_var *ccv);
93218155Slstewartstatic int	chd_cb_init(struct cc_var *ccv);
94218155Slstewartstatic void	chd_cong_signal(struct cc_var *ccv, uint32_t signal_type);
95218155Slstewartstatic void	chd_conn_init(struct cc_var *ccv);
96218155Slstewartstatic int	chd_mod_init(void);
97218155Slstewart
98218155Slstewartstruct chd {
99218155Slstewart	/*
100218155Slstewart	 * Shadow window - keeps track of what the NewReno congestion window
101218155Slstewart	 * would have been if delay-based cwnd backoffs had not been made. This
102218155Slstewart	 * functionality aids coexistence with loss-based TCP flows which may be
103218155Slstewart	 * sharing links along the path.
104218155Slstewart	 */
105218155Slstewart	unsigned long shadow_w;
106218155Slstewart	/*
107218155Slstewart	 * Loss-based TCP compatibility flag - When set, it turns on the shadow
108218155Slstewart	 * window functionality.
109218155Slstewart	 */
110218155Slstewart	int loss_compete;
111218155Slstewart	 /* The maximum round trip time seen within a measured rtt period. */
112218155Slstewart	int maxrtt_in_rtt;
113218155Slstewart	/* The previous qdly that caused cwnd to backoff. */
114218155Slstewart	int prev_backoff_qdly;
115218155Slstewart};
116218155Slstewart
117218155Slstewartstatic int ertt_id;
118218155Slstewart
119218155Slstewartstatic VNET_DEFINE(uint32_t, chd_qmin) = 5;
120218155Slstewartstatic VNET_DEFINE(uint32_t, chd_pmax) = 50;
121218155Slstewartstatic VNET_DEFINE(uint32_t, chd_loss_fair) = 1;
122218155Slstewartstatic VNET_DEFINE(uint32_t, chd_use_max) = 1;
123218155Slstewartstatic VNET_DEFINE(uint32_t, chd_qthresh) = 20;
124218155Slstewart#define	V_chd_qthresh	VNET(chd_qthresh)
125218155Slstewart#define	V_chd_qmin	VNET(chd_qmin)
126218155Slstewart#define	V_chd_pmax	VNET(chd_pmax)
127218155Slstewart#define	V_chd_loss_fair	VNET(chd_loss_fair)
128218155Slstewart#define	V_chd_use_max	VNET(chd_use_max)
129218155Slstewart
130220592Spluknetstatic MALLOC_DEFINE(M_CHD, "chd data",
131218155Slstewart    "Per connection data required for the CHD congestion control algorithm");
132218155Slstewart
133218155Slstewartstruct cc_algo chd_cc_algo = {
134218155Slstewart	.name = "chd",
135218155Slstewart	.ack_received = chd_ack_received,
136218155Slstewart	.cb_destroy = chd_cb_destroy,
137218155Slstewart	.cb_init = chd_cb_init,
138218155Slstewart	.cong_signal = chd_cong_signal,
139218155Slstewart	.conn_init = chd_conn_init,
140218155Slstewart	.mod_init = chd_mod_init
141218155Slstewart};
142218155Slstewart
143218155Slstewartstatic __inline void
144218155Slstewartchd_window_decrease(struct cc_var *ccv)
145218155Slstewart{
146218155Slstewart	unsigned long win;
147218155Slstewart
148218155Slstewart	win = min(CCV(ccv, snd_wnd), CCV(ccv, snd_cwnd)) / CCV(ccv, t_maxseg);
149218155Slstewart	win -= max((win / 2), 1);
150218155Slstewart	CCV(ccv, snd_ssthresh) = max(win, 2) * CCV(ccv, t_maxseg);
151218155Slstewart}
152218155Slstewart
153218155Slstewart/*
154218155Slstewart * Probabilistic backoff function. Returns 1 if we should backoff or 0
155218155Slstewart * otherwise. The calculation of p is similar to the calculation of p in cc_hd.
156218155Slstewart */
157218155Slstewartstatic __inline int
158218155Slstewartshould_backoff(int qdly, int maxqdly, struct chd *chd_data)
159218155Slstewart{
160218155Slstewart	unsigned long p, rand;
161218155Slstewart
162218155Slstewart	rand = random();
163218155Slstewart
164218155Slstewart	if (qdly < V_chd_qthresh) {
165218155Slstewart		chd_data->loss_compete = 0;
166218155Slstewart		p = (((RANDOM_MAX / 100) * V_chd_pmax) /
167218155Slstewart		    (V_chd_qthresh - V_chd_qmin)) *
168218155Slstewart		    (qdly - V_chd_qmin);
169218155Slstewart	} else {
170218155Slstewart		if (qdly > V_chd_qthresh) {
171218155Slstewart			p = (((RANDOM_MAX / 100) * V_chd_pmax) /
172218155Slstewart			    (maxqdly - V_chd_qthresh)) *
173218155Slstewart			    (maxqdly - qdly);
174218155Slstewart			if (V_chd_loss_fair && rand < p)
175218155Slstewart				chd_data->loss_compete = 1;
176218155Slstewart		} else {
177218155Slstewart			p = (RANDOM_MAX / 100) * V_chd_pmax;
178218155Slstewart			chd_data->loss_compete = 0;
179218155Slstewart		}
180218155Slstewart	}
181218155Slstewart
182218155Slstewart	return (rand < p);
183218155Slstewart}
184218155Slstewart
185218155Slstewartstatic __inline void
186218155Slstewartchd_window_increase(struct cc_var *ccv, int new_measurement)
187218155Slstewart{
188218155Slstewart	struct chd *chd_data;
189218155Slstewart	int incr;
190218155Slstewart
191218155Slstewart	chd_data = ccv->cc_data;
192218155Slstewart	incr = 0;
193218155Slstewart
194218155Slstewart	if (CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh)) {
195218155Slstewart		/* Adapted from NewReno slow start. */
196218155Slstewart		if (V_tcp_do_rfc3465) {
197218155Slstewart			/* In slow-start with ABC enabled. */
198218155Slstewart			if (CCV(ccv, snd_nxt) == CCV(ccv, snd_max)) {
199218155Slstewart				/* Not due to RTO. */
200218155Slstewart				incr = min(ccv->bytes_this_ack,
201218155Slstewart				    V_tcp_abc_l_var * CCV(ccv, t_maxseg));
202218155Slstewart			} else {
203218155Slstewart				/* Due to RTO. */
204218155Slstewart				incr = min(ccv->bytes_this_ack,
205218155Slstewart				    CCV(ccv, t_maxseg));
206218155Slstewart			}
207218155Slstewart		} else
208218155Slstewart			incr = CCV(ccv, t_maxseg);
209218155Slstewart
210218155Slstewart	} else { /* Congestion avoidance. */
211218155Slstewart		if (V_tcp_do_rfc3465) {
212218155Slstewart			if (ccv->flags & CCF_ABC_SENTAWND) {
213218155Slstewart				ccv->flags &= ~CCF_ABC_SENTAWND;
214218155Slstewart				incr = CCV(ccv, t_maxseg);
215218155Slstewart			}
216218155Slstewart		} else if (new_measurement)
217218155Slstewart			incr = CCV(ccv, t_maxseg);
218218155Slstewart	}
219218155Slstewart
220218155Slstewart	if (chd_data->shadow_w > 0) {
221218155Slstewart		/* Track NewReno window. */
222218155Slstewart		chd_data->shadow_w = min(chd_data->shadow_w + incr,
223218155Slstewart		    TCP_MAXWIN << CCV(ccv, snd_scale));
224218155Slstewart	}
225218155Slstewart
226218155Slstewart	CCV(ccv,snd_cwnd) = min(CCV(ccv, snd_cwnd) + incr,
227218155Slstewart	    TCP_MAXWIN << CCV(ccv, snd_scale));
228218155Slstewart}
229218155Slstewart
230218155Slstewart/*
231218155Slstewart * All ACK signals are used for timing measurements to determine delay-based
232218155Slstewart * congestion. However, window increases are only performed when
233218155Slstewart * ack_type == CC_ACK.
234218155Slstewart */
235218155Slstewartstatic void
236218155Slstewartchd_ack_received(struct cc_var *ccv, uint16_t ack_type)
237218155Slstewart{
238218155Slstewart	struct chd *chd_data;
239218155Slstewart	struct ertt *e_t;
240218155Slstewart	int backoff, new_measurement, qdly, rtt;
241218155Slstewart
242218155Slstewart	e_t = khelp_get_osd(CCV(ccv, osd), ertt_id);
243218155Slstewart	chd_data = ccv->cc_data;
244218155Slstewart	new_measurement = e_t->flags & ERTT_NEW_MEASUREMENT;
245218155Slstewart	backoff = qdly = 0;
246218155Slstewart
247218155Slstewart	chd_data->maxrtt_in_rtt = imax(e_t->rtt, chd_data->maxrtt_in_rtt);
248218155Slstewart
249218155Slstewart	if (new_measurement) {
250218155Slstewart		/*
251218155Slstewart		 * There is a new per RTT measurement, so check to see if there
252218155Slstewart		 * is delay based congestion.
253218155Slstewart		 */
254218155Slstewart		rtt = V_chd_use_max ? chd_data->maxrtt_in_rtt : e_t->rtt;
255218155Slstewart		chd_data->maxrtt_in_rtt = 0;
256218155Slstewart
257218155Slstewart		if (rtt && e_t->minrtt && !IN_RECOVERY(CCV(ccv, t_flags))) {
258218155Slstewart			qdly = rtt - e_t->minrtt;
259218155Slstewart			if (qdly > V_chd_qmin) {
260218155Slstewart				/*
261218155Slstewart				 * Probabilistic delay based congestion
262218155Slstewart				 * indication.
263218155Slstewart				 */
264218155Slstewart				backoff = should_backoff(qdly,
265218155Slstewart				    e_t->maxrtt - e_t->minrtt, chd_data);
266218155Slstewart			} else
267218155Slstewart				chd_data->loss_compete = 0;
268218155Slstewart		}
269218155Slstewart		/* Reset per RTT measurement flag to start a new measurement. */
270218155Slstewart		e_t->flags &= ~ERTT_NEW_MEASUREMENT;
271218155Slstewart	}
272218155Slstewart
273218155Slstewart	if (backoff) {
274218155Slstewart		/*
275218155Slstewart		 * Update shadow_w before delay based backoff.
276218155Slstewart		 */
277218155Slstewart		if (chd_data->loss_compete ||
278218155Slstewart		    qdly > chd_data->prev_backoff_qdly) {
279218155Slstewart			/*
280218155Slstewart			 * Delay is higher than when we backed off previously,
281218155Slstewart			 * so it is possible that this flow is competing with
282218155Slstewart			 * loss based flows.
283218155Slstewart			 */
284218155Slstewart			chd_data->shadow_w = max(CCV(ccv, snd_cwnd),
285218155Slstewart			    chd_data->shadow_w);
286218155Slstewart		} else {
287218155Slstewart			/*
288218155Slstewart			 * Reset shadow_w, as it is probable that this flow is
289218155Slstewart			 * not competing with loss based flows at the moment.
290218155Slstewart			 */
291218155Slstewart			chd_data->shadow_w = 0;
292218155Slstewart		}
293218155Slstewart
294218155Slstewart		chd_data->prev_backoff_qdly = qdly;
295218155Slstewart		/*
296218155Slstewart		 * Send delay-based congestion signal to the congestion signal
297218155Slstewart		 * handler.
298218155Slstewart		 */
299218155Slstewart		chd_cong_signal(ccv, CC_CHD_DELAY);
300218155Slstewart
301218155Slstewart	} else if (ack_type == CC_ACK)
302218155Slstewart		chd_window_increase(ccv, new_measurement);
303218155Slstewart}
304218155Slstewart
305218155Slstewartstatic void
306218155Slstewartchd_cb_destroy(struct cc_var *ccv)
307218155Slstewart{
308218155Slstewart
309218155Slstewart	if (ccv->cc_data != NULL)
310218155Slstewart		free(ccv->cc_data, M_CHD);
311218155Slstewart}
312218155Slstewart
313218155Slstewartstatic int
314218155Slstewartchd_cb_init(struct cc_var *ccv)
315218155Slstewart{
316218155Slstewart	struct chd *chd_data;
317218155Slstewart
318218155Slstewart	chd_data = malloc(sizeof(struct chd), M_CHD, M_NOWAIT);
319218155Slstewart	if (chd_data == NULL)
320218155Slstewart		return (ENOMEM);
321218155Slstewart
322218155Slstewart	chd_data->shadow_w = 0;
323218155Slstewart	ccv->cc_data = chd_data;
324218155Slstewart
325218155Slstewart	return (0);
326218155Slstewart}
327218155Slstewart
328218155Slstewartstatic void
329218155Slstewartchd_cong_signal(struct cc_var *ccv, uint32_t signal_type)
330218155Slstewart{
331218155Slstewart	struct ertt *e_t;
332218155Slstewart	struct chd *chd_data;
333218155Slstewart	int qdly;
334218155Slstewart
335218155Slstewart	e_t = khelp_get_osd(CCV(ccv, osd), ertt_id);
336218155Slstewart	chd_data = ccv->cc_data;
337218155Slstewart	qdly = imax(e_t->rtt, chd_data->maxrtt_in_rtt) - e_t->minrtt;
338218155Slstewart
339218155Slstewart	switch(signal_type) {
340218155Slstewart	case CC_CHD_DELAY:
341218155Slstewart		chd_window_decrease(ccv); /* Set new ssthresh. */
342218155Slstewart		CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh);
343218155Slstewart		CCV(ccv, snd_recover) = CCV(ccv, snd_max);
344218155Slstewart		ENTER_CONGRECOVERY(CCV(ccv, t_flags));
345218155Slstewart		break;
346218155Slstewart
347218155Slstewart	case CC_NDUPACK: /* Packet loss. */
348218155Slstewart		/*
349218155Slstewart		 * Only react to loss as a congestion signal if qdly >
350218155Slstewart		 * V_chd_qthresh.  If qdly is less than qthresh, presume that
351218155Slstewart		 * this is a non congestion related loss. If qdly is greater
352218155Slstewart		 * than qthresh, assume that we are competing with loss based
353218155Slstewart		 * tcp flows and restore window from any unnecessary backoffs,
354218155Slstewart		 * before the decrease.
355218155Slstewart		 */
356218155Slstewart		if (!IN_RECOVERY(CCV(ccv, t_flags)) && qdly > V_chd_qthresh) {
357218155Slstewart			if (chd_data->loss_compete) {
358218155Slstewart				CCV(ccv, snd_cwnd) = max(CCV(ccv, snd_cwnd),
359218155Slstewart				    chd_data->shadow_w);
360218155Slstewart			}
361218155Slstewart			chd_window_decrease(ccv);
362218155Slstewart		} else {
363218155Slstewart			 /*
364218155Slstewart			  * This loss isn't congestion related, or already
365218155Slstewart			  * recovering from congestion.
366218155Slstewart			  */
367218155Slstewart			CCV(ccv, snd_ssthresh) = CCV(ccv, snd_cwnd);
368218155Slstewart			CCV(ccv, snd_recover) = CCV(ccv, snd_max);
369218155Slstewart		}
370218155Slstewart
371218155Slstewart		if (chd_data->shadow_w > 0) {
372218155Slstewart			chd_data->shadow_w = max(chd_data->shadow_w /
373218155Slstewart			    CCV(ccv, t_maxseg) / 2, 2) * CCV(ccv, t_maxseg);
374218155Slstewart		}
375218155Slstewart		ENTER_FASTRECOVERY(CCV(ccv, t_flags));
376218155Slstewart		break;
377218155Slstewart
378218155Slstewart	default:
379218155Slstewart		newreno_cc_algo.cong_signal(ccv, signal_type);
380218155Slstewart	}
381218155Slstewart}
382218155Slstewart
383218155Slstewartstatic void
384218155Slstewartchd_conn_init(struct cc_var *ccv)
385218155Slstewart{
386218155Slstewart	struct chd *chd_data;
387218155Slstewart
388218155Slstewart	chd_data = ccv->cc_data;
389218155Slstewart	chd_data->prev_backoff_qdly = 0;
390218155Slstewart	chd_data->maxrtt_in_rtt = 0;
391218155Slstewart	chd_data->loss_compete = 0;
392218155Slstewart	/*
393218155Slstewart	 * Initialise the shadow_cwnd to be equal to snd_cwnd in case we are
394218155Slstewart	 * competing with loss based flows from the start.
395218155Slstewart	 */
396218155Slstewart	chd_data->shadow_w = CCV(ccv, snd_cwnd);
397218155Slstewart}
398218155Slstewart
399218155Slstewartstatic int
400218155Slstewartchd_mod_init(void)
401218155Slstewart{
402218155Slstewart
403218155Slstewart	ertt_id = khelp_get_id("ertt");
404218155Slstewart	if (ertt_id <= 0) {
405218155Slstewart		printf("%s: h_ertt module not found\n", __func__);
406218155Slstewart		return (ENOENT);
407218155Slstewart	}
408218155Slstewart
409218155Slstewart	chd_cc_algo.after_idle = newreno_cc_algo.after_idle;
410218155Slstewart	chd_cc_algo.post_recovery = newreno_cc_algo.post_recovery;
411218155Slstewart
412218155Slstewart	return (0);
413218155Slstewart}
414218155Slstewart
415218155Slstewartstatic int
416218155Slstewartchd_loss_fair_handler(SYSCTL_HANDLER_ARGS)
417218155Slstewart{
418218155Slstewart	int error;
419218155Slstewart	uint32_t new;
420218155Slstewart
421218155Slstewart	new = V_chd_loss_fair;
422218155Slstewart	error = sysctl_handle_int(oidp, &new, 0, req);
423218155Slstewart	if (error == 0 && req->newptr != NULL) {
424218155Slstewart		if (CAST_PTR_INT(req->newptr) > 1)
425218155Slstewart			error = EINVAL;
426218155Slstewart		else
427218155Slstewart			V_chd_loss_fair = new;
428218155Slstewart	}
429218155Slstewart
430218155Slstewart	return (error);
431218155Slstewart}
432218155Slstewart
433218155Slstewartstatic int
434218155Slstewartchd_pmax_handler(SYSCTL_HANDLER_ARGS)
435218155Slstewart{
436218155Slstewart	int error;
437218155Slstewart	uint32_t new;
438218155Slstewart
439218155Slstewart	new = V_chd_pmax;
440218155Slstewart	error = sysctl_handle_int(oidp, &new, 0, req);
441218155Slstewart	if (error == 0 && req->newptr != NULL) {
442218155Slstewart		if (CAST_PTR_INT(req->newptr) == 0 ||
443218155Slstewart		    CAST_PTR_INT(req->newptr) > 100)
444218155Slstewart			error = EINVAL;
445218155Slstewart		else
446218155Slstewart			V_chd_pmax = new;
447218155Slstewart	}
448218155Slstewart
449218155Slstewart	return (error);
450218155Slstewart}
451218155Slstewart
452218155Slstewartstatic int
453218155Slstewartchd_qthresh_handler(SYSCTL_HANDLER_ARGS)
454218155Slstewart{
455218155Slstewart	int error;
456218155Slstewart	uint32_t new;
457218155Slstewart
458218155Slstewart	new = V_chd_qthresh;
459218155Slstewart	error = sysctl_handle_int(oidp, &new, 0, req);
460218155Slstewart	if (error == 0 && req->newptr != NULL) {
461218155Slstewart		if (CAST_PTR_INT(req->newptr) <= V_chd_qmin)
462218155Slstewart			error = EINVAL;
463218155Slstewart		else
464218155Slstewart			V_chd_qthresh = new;
465218155Slstewart	}
466218155Slstewart
467218155Slstewart	return (error);
468218155Slstewart}
469218155Slstewart
470218155SlstewartSYSCTL_DECL(_net_inet_tcp_cc_chd);
471218155SlstewartSYSCTL_NODE(_net_inet_tcp_cc, OID_AUTO, chd, CTLFLAG_RW, NULL,
472218155Slstewart    "CAIA Hamilton delay-based congestion control related settings");
473218155Slstewart
474218155SlstewartSYSCTL_VNET_PROC(_net_inet_tcp_cc_chd, OID_AUTO, loss_fair,
475218155Slstewart    CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(chd_loss_fair), 1, &chd_loss_fair_handler,
476218155Slstewart    "IU", "Flag to enable shadow window functionality.");
477218155Slstewart
478218155SlstewartSYSCTL_VNET_PROC(_net_inet_tcp_cc_chd, OID_AUTO, pmax,
479218155Slstewart    CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(chd_pmax), 5, &chd_pmax_handler,
480218155Slstewart    "IU", "Per RTT maximum backoff probability as a percentage");
481218155Slstewart
482218155SlstewartSYSCTL_VNET_PROC(_net_inet_tcp_cc_chd, OID_AUTO, queue_threshold,
483218155Slstewart    CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(chd_qthresh), 20, &chd_qthresh_handler,
484218155Slstewart    "IU", "Queueing congestion threshold in ticks");
485218155Slstewart
486218155SlstewartSYSCTL_VNET_UINT(_net_inet_tcp_cc_chd, OID_AUTO, queue_min,
487273736Shselasky    CTLFLAG_RW, &VNET_NAME(chd_qmin), 5,
488218155Slstewart    "Minimum queueing delay threshold in ticks");
489218155Slstewart
490218155SlstewartSYSCTL_VNET_UINT(_net_inet_tcp_cc_chd,  OID_AUTO, use_max,
491273736Shselasky    CTLFLAG_RW, &VNET_NAME(chd_use_max), 1,
492218155Slstewart    "Use the maximum RTT seen within the measurement period (RTT) "
493218155Slstewart    "as the basic delay measurement for the algorithm.");
494218155Slstewart
495218155SlstewartDECLARE_CC_MODULE(chd, &chd_cc_algo);
496218155SlstewartMODULE_DEPEND(chd, ertt, 1, 1, 1);
497