This is an experimental patch to bridge CCID2 and existing tcp congestion contrl
algorithms. Added support for "tcp_congestion_ops" in CCID2. This allows for
CCID2 to use different variants of congestion control. The default is the RFC
CCID2, named with much originality, "ccid2".
I added support for a "tcp adaptor" which allows existing tcp algorithms to be
used withing CCID2. They may be selected by asking for "tcp_name", where name
is "reno", "vegas", etc.
The following TCP algorithms are believed to work [at least partially] with
appropriate byte counting turned off:
bic, compound, cubic, highspeed, htcp, reno, scalable, vegas, veno.
These don't seem to work properly [I haven't added full support yet]:
hybla, lp, westwood.
This patch is mainly an RFC. If people seem to like the idea, I can work on
making the patch look good.
Signed-off-by: Andrea Bittau <[EMAIL PROTECTED]>
---
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 676333b..efc3139 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -196,6 +196,7 @@ #define DCCP_SOCKOPT_CHANGE_L 3
#define DCCP_SOCKOPT_CHANGE_R 4
#define DCCP_SOCKOPT_CCID_RX_INFO 128
#define DCCP_SOCKOPT_CCID_TX_INFO 192
+#define DCCP_SOCKOPT_CCID_TX_ALGO 193
#define DCCP_SERVICE_LIST_MAX_LEN 32
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index 6d3a57b..ea9c7d4 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -46,6 +46,289 @@ #else
#define ccid2_pr_debug(format, a...)
#endif
+static u32 ccid2ca_ssthresh(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ return max(2, hctx->ccid2hctx_cwnd >> 1);
+}
+
+inline static void* ccid2ca_priv(struct sock *sk)
+{
+ return ccid2_hc_tx_sk(sk)->ccid2hctx_ca_priv;
+}
+
+static void ccid2ca_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
+ u32 in_flight, int good_ack)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv *cap = ccid2ca_priv(sk);
+
+ /* check if we are dealing with a new ack vector */
+ /* XXX this method is unreliable. Can get two seperate ACKs with same
+ * highest ackno. */
+ if (ack != cap->ccid2cap_last_ack) {
+ /* If in slow-start, cwnd can increase at most Ack Ratio / 2
packets for
+ * this single ack. I round up.
+ * -sorbo.
+ */
+ cap->ccid2cap_maxincr = dccp_sk(sk)->dccps_l_ack_ratio >> 1;
+ cap->ccid2cap_maxincr++;
+
+ cap->ccid2cap_last_ack = ack;
+ }
+
+ /* slow start */
+ if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) {
+ cap->ccid2cap_acks = 0;
+
+ /* We can increase cwnd at most maxincr [ack_ratio/2] */
+ if (cap->ccid2cap_maxincr) {
+ /* increase every 2 acks */
+ cap->ccid2cap_ssacks++;
+ if (cap->ccid2cap_ssacks == 2) {
+ ccid2_change_cwnd(hctx, hctx->ccid2hctx_cwnd+1);
+ cap->ccid2cap_ssacks = 0;
+ cap->ccid2cap_maxincr--;
+ }
+ } else {
+ /* increased cwnd enough for this single ack */
+ cap->ccid2cap_ssacks = 0;
+ }
+ } else { /* additive increase */
+ cap->ccid2cap_ssacks = 0;
+ cap->ccid2cap_acks++;
+
+ if (cap->ccid2cap_acks >= hctx->ccid2hctx_cwnd) {
+ ccid2_change_cwnd(hctx, hctx->ccid2hctx_cwnd + 1);
+ cap->ccid2cap_acks = 0;
+ }
+ }
+}
+
+static void ccid2ca_init(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv *cap;
+
+ hctx->ccid2hctx_ca_priv = kmalloc(sizeof(*cap), GFP_ATOMIC); /* XXX */
+ BUG_ON(!hctx->ccid2hctx_ca_priv); /* XXX */
+
+ cap = ccid2ca_priv(sk);
+ memset(cap, 0, sizeof(*cap));
+ cap->ccid2cap_last_ack = -1;
+}
+
+static void ccid2ca_release(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ if (hctx->ccid2hctx_ca_priv) {
+ kfree(hctx->ccid2hctx_ca_priv);
+ hctx->ccid2hctx_ca_priv = NULL;
+ }
+}
+
+#define CCID2_CA_NAME "ccid2"
+struct tcp_congestion_ops ccid2_rfc = {
+ .name = CCID2_CA_NAME,
+ .owner = THIS_MODULE,
+ .ssthresh = ccid2ca_ssthresh,
+ .cong_avoid = ccid2ca_cong_avoid,
+ .init = ccid2ca_init,
+ .release = ccid2ca_release
+};
+
+static void ccid2ca_tcp_sync_d2t(struct dccp_sock *dp, struct tcp_sock *tp)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk((struct sock*)dp);
+ u64 seq;
+
+ tp->snd_cwnd = hctx->ccid2hctx_cwnd;
+ tp->snd_ssthresh = hctx->ccid2hctx_ssthresh;
+
+ /* hybla gets minimum rtt from init. We put a "high" value here to
+ * avoid it.
+ */
+ tp->srtt = hctx->ccid2hctx_srtt == -1 ? HZ*3 :
+ hctx->ccid2hctx_srtt;
+ /* htcp code failes due to division by 0 [an assert would be nice
+ * there]. But it might be that it's just lame to have an srtt of 0, so
+ * it's my code's fault. -sorbo.
+ */
+ if (tp->srtt == 0)
+ tp->srtt++;
+ tp->srtt <<= 3;
+
+ seq = dp->dccps_gss;
+ dccp_inc_seqno(&seq);
+ tp->snd_nxt = seq;
+}
+
+static void ccid2ca_tcp_sync_t2d(struct ccid2_hc_tx_sock *hctx,
+ struct tcp_sock *tp)
+{
+ ccid2_change_cwnd(hctx, tp->snd_cwnd);
+ hctx->ccid2hctx_ssthresh = tp->snd_ssthresh;
+}
+
+static void ccid2ca_tcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
+ u32 in_flight, int good_ack)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+ struct sock *ts = (struct sock*) &cap->ccid2capt_tp;
+ struct inet_connection_sock *icsk = inet_csk(ts);
+
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), &cap->ccid2capt_tp);
+ BUG_ON(!icsk->icsk_ca_ops->cong_avoid);
+ icsk->icsk_ca_ops->cong_avoid(ts, ack, rtt, in_flight, good_ack);
+ ccid2ca_tcp_sync_t2d(hctx, &cap->ccid2capt_tp);
+
+ tcp_sk(ts)->rx_opt.saw_tstamp = 0;
+}
+
+static u32 ccid2ca_tcp_ssthresh(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+ struct sock *ts = (struct sock*) &cap->ccid2capt_tp;
+ struct inet_connection_sock *icsk = inet_csk(ts);
+ u32 rc;
+
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), &cap->ccid2capt_tp);
+ BUG_ON(!icsk->icsk_ca_ops->ssthresh);
+ rc = icsk->icsk_ca_ops->ssthresh(ts);
+ ccid2ca_tcp_sync_t2d(hctx, &cap->ccid2capt_tp);
+
+ return rc;
+}
+
+static void ccid2ca_tcp_set_state(struct sock *sk, u8 new_state)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+ struct sock *ts = (struct sock*) &cap->ccid2capt_tp;
+ struct inet_connection_sock *icsk = inet_csk(ts);
+
+ if (!icsk->icsk_ca_ops->set_state)
+ return;
+
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), &cap->ccid2capt_tp);
+ icsk->icsk_ca_ops->set_state(ts, new_state);
+ ccid2ca_tcp_sync_t2d(hctx, &cap->ccid2capt_tp);
+}
+
+static void ccid2ca_tcp_pkts_acked(struct sock *sk, u32 pkts_acked)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+ struct sock *ts = (struct sock*) &cap->ccid2capt_tp;
+ struct inet_connection_sock *icsk = inet_csk(ts);
+
+ if (!icsk->icsk_ca_ops->pkts_acked)
+ return;
+
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), &cap->ccid2capt_tp);
+ icsk->icsk_ca_ops->pkts_acked(ts, pkts_acked);
+ ccid2ca_tcp_sync_t2d(hctx, &cap->ccid2capt_tp);
+}
+
+static void ccid2ca_tcp_rtt_sample(struct sock *sk, u32 usrtt)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+ struct sock *ts = (struct sock*) &cap->ccid2capt_tp;
+ struct inet_connection_sock *icsk = inet_csk(ts);
+ u32 delay = usecs_to_jiffies(usrtt);
+
+ /* an approximation */
+ tcp_sk(ts)->rx_opt.saw_tstamp = 1;
+ tcp_sk(ts)->rx_opt.rcv_tsecr = jiffies - delay;
+ tcp_sk(ts)->rx_opt.rcv_tsval = jiffies - (delay>>1); /* XXX */
+
+ if (!icsk->icsk_ca_ops->rtt_sample)
+ return;
+
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), &cap->ccid2capt_tp);
+ icsk->icsk_ca_ops->rtt_sample(ts, usrtt);
+ ccid2ca_tcp_sync_t2d(hctx, &cap->ccid2capt_tp);
+}
+
+static void ccid2ca_tcp_cwnd_event(struct sock *sk, enum tcp_ca_event event)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+ struct sock *ts = (struct sock*) &cap->ccid2capt_tp;
+ struct inet_connection_sock *icsk = inet_csk(ts);
+
+ if (!icsk->icsk_ca_ops->cwnd_event)
+ return;
+
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), &cap->ccid2capt_tp);
+ icsk->icsk_ca_ops->cwnd_event(ts, event);
+ ccid2ca_tcp_sync_t2d(hctx, &cap->ccid2capt_tp);
+}
+
+static void ccid2ca_tcp_init(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap;
+ int rc;
+ struct tcp_sock *tp;
+
+ hctx->ccid2hctx_ca_priv = kmalloc(sizeof(*cap), GFP_ATOMIC); /* XXX */
+ BUG_ON(!hctx->ccid2hctx_ca_priv); /* XXX */
+
+ cap = ccid2ca_priv(sk);
+ memset(cap, 0, sizeof(*cap));
+ tp = &cap->ccid2capt_tp;
+
+ /* set some default TCP values */
+ tp->snd_cwnd_clamp = 10000; /* XXX */
+ inet_csk((struct sock*)tp)->icsk_ca_state = TCP_CA_Open;
+ tp->mss_cache = 1;
+ ccid2ca_tcp_sync_d2t(dccp_sk(sk), tp);
+
+ /* Initialize the TCP congestion control algorithm */
+ /* We do this to make sure that reference counts are correct */
+ inet_csk((struct sock*) tp)->icsk_ca_ops = &tcp_init_congestion_ops;
+ tcp_init_congestion_control((struct sock*) tp);
+
+ rc = tcp_set_congestion_control((struct sock*) tp,
+ hctx->ccid2hctx_tcp_name);
+ BUG_ON(rc);
+
+ ccid2ca_tcp_sync_t2d(hctx, tp);
+}
+
+static void ccid2ca_tcp_release(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct ccid2ca_priv_tcp *cap = ccid2ca_priv(sk);
+
+ if (hctx->ccid2hctx_ca_priv) {
+ tcp_cleanup_congestion_control((struct sock*)
+ &cap->ccid2capt_tp);
+ kfree(hctx->ccid2hctx_ca_priv);
+ hctx->ccid2hctx_ca_priv = NULL;
+ }
+}
+
+#define CCID2_CA_TCP_NAME "tcp_"
+struct tcp_congestion_ops ccid2_tcp = {
+ .name = CCID2_CA_NAME,
+ .owner = THIS_MODULE,
+ .ssthresh = ccid2ca_tcp_ssthresh,
+ .cong_avoid = ccid2ca_tcp_cong_avoid,
+ .init = ccid2ca_tcp_init,
+ .release = ccid2ca_tcp_release,
+ .set_state = ccid2ca_tcp_set_state,
+ .pkts_acked = ccid2ca_tcp_pkts_acked,
+ .rtt_sample = ccid2ca_tcp_rtt_sample,
+ .cwnd_event = ccid2ca_tcp_cwnd_event
+};
+
#ifdef CONFIG_IP_DCCP_CCID2_DEBUG
static void ccid2_hc_tx_check_sanity(const struct ccid2_hc_tx_sock *hctx)
{
@@ -322,6 +605,9 @@ static inline void ccid2_profile_var(str
static void ccid2_change_cwnd(struct ccid2_hc_tx_sock *hctx, int val)
{
+ if (val == hctx->ccid2hctx_cwnd)
+ return;
+
if (val == 0)
val = 1;
@@ -373,6 +659,9 @@ static void ccid2_hc_tx_rto_expire(unsig
ccid2_pr_debug("RTO_EXPIRE\n");
ccid2_hc_tx_check_sanity(hctx);
+
+ if (hctx->ccid2hctx_ca_ops->set_state)
+ hctx->ccid2hctx_ca_ops->set_state(sk, TCP_CA_Loss);
/* back-off timer */
hctx->ccid2hctx_rto <<= 1;
@@ -392,8 +681,6 @@ static void ccid2_hc_tx_rto_expire(unsig
/* clear state about stuff we sent */
hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh;
- hctx->ccid2hctx_ssacks = 0;
- hctx->ccid2hctx_acks = 0;
hctx->ccid2hctx_sent = 0;
/* clear ack ratio state. */
@@ -402,6 +689,10 @@ static void ccid2_hc_tx_rto_expire(unsig
hctx->ccid2hctx_rpseq = 0;
hctx->ccid2hctx_rpdupack = -1;
ccid2_change_l_ack_ratio(sk, 1);
+
+ if (hctx->ccid2hctx_ca_ops->set_state)
+ hctx->ccid2hctx_ca_ops->set_state(sk, TCP_CA_Open);
+
ccid2_hc_tx_check_sanity(hctx);
out:
bh_unlock_sock(sk);
@@ -446,6 +737,9 @@ static void ccid2_hc_tx_packet_sent(stru
BUG_ON(!hctx->ccid2hctx_sendwait);
hctx->ccid2hctx_sendwait = 0;
+ if (hctx->ccid2hctx_pipe == 0 &&
+ hctx->ccid2hctx_ca_ops->cwnd_event)
+ hctx->ccid2hctx_ca_ops->cwnd_event(sk, CA_EVENT_TX_START);
ccid2_change_pipe(hctx, hctx->ccid2hctx_pipe+1);
BUG_ON(hctx->ccid2hctx_pipe < 0);
@@ -601,42 +895,18 @@ static void ccid2_hc_tx_kill_rto_timer(s
static inline void ccid2_new_ack(struct sock *sk,
struct ccid2_seq *seqp,
- unsigned int *maxincr)
+ u64 ackno)
{
struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ unsigned long r = (long)jiffies - (long)seqp->ccid2s_sent;
- /* slow start */
- if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) {
- hctx->ccid2hctx_acks = 0;
-
- /* We can increase cwnd at most maxincr [ack_ratio/2] */
- if (*maxincr) {
- /* increase every 2 acks */
- hctx->ccid2hctx_ssacks++;
- if (hctx->ccid2hctx_ssacks == 2) {
- ccid2_change_cwnd(hctx, hctx->ccid2hctx_cwnd+1);
- hctx->ccid2hctx_ssacks = 0;
- *maxincr = *maxincr - 1;
- }
- } else {
- /* increased cwnd enough for this single ack */
- hctx->ccid2hctx_ssacks = 0;
- }
- } else {
- hctx->ccid2hctx_ssacks = 0;
- hctx->ccid2hctx_acks++;
-
- if (hctx->ccid2hctx_acks >= hctx->ccid2hctx_cwnd) {
- ccid2_change_cwnd(hctx, hctx->ccid2hctx_cwnd + 1);
- hctx->ccid2hctx_acks = 0;
- }
- }
+ if (hctx->ccid2hctx_ca_ops->rtt_sample)
+ hctx->ccid2hctx_ca_ops->rtt_sample(sk, jiffies_to_usecs(r));
/* update RTO */
if (hctx->ccid2hctx_srtt == -1 ||
((long)jiffies - (long)hctx->ccid2hctx_lastrtt) >=
hctx->ccid2hctx_srtt) {
- unsigned long r = (long)jiffies - (long)seqp->ccid2s_sent;
int s;
/* first measurement */
@@ -694,6 +964,15 @@ #endif
/* we got a new ack, so re-start RTO timer */
ccid2_hc_tx_kill_rto_timer(sk);
ccid2_start_rto_timer(sk);
+
+ /* congestion control */
+ BUG_ON(!hctx->ccid2hctx_ca_ops->cong_avoid);
+ /* XXX we pass bougus paramets.
+ * 1) good
+ * 2) "in_flight". We pretend we are always cwnd limited.
+ */
+ hctx->ccid2hctx_ca_ops->cong_avoid(sk, ackno, hctx->ccid2hctx_srtt,
+ hctx->ccid2hctx_cwnd, 1);
}
static void ccid2_hc_tx_dec_pipe(struct sock *sk)
@@ -707,33 +986,33 @@ static void ccid2_hc_tx_dec_pipe(struct
ccid2_hc_tx_kill_rto_timer(sk);
}
-static void ccid2_congestion_event(struct ccid2_hc_tx_sock *hctx,
- struct ccid2_seq *seqp)
+static void ccid2_congestion_event(struct sock *sk, struct ccid2_seq *seqp)
{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
if (time_before(seqp->ccid2s_sent, hctx->ccid2hctx_last_cong)) {
dccp_pr_debug("Multiple losses in one RTT---treating as one\n");
return;
}
hctx->ccid2hctx_last_cong = jiffies;
-
- ccid2_change_cwnd(hctx, hctx->ccid2hctx_cwnd >> 1);
- hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd;
- if (hctx->ccid2hctx_ssthresh < 2)
- hctx->ccid2hctx_ssthresh = 2;
+
+ BUG_ON(!hctx->ccid2hctx_ca_ops->ssthresh);
+ hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_ca_ops->ssthresh(sk);
+ ccid2_change_cwnd(hctx, hctx->ccid2hctx_ssthresh);
}
static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
- u64 ackno, seqno;
+ u64 ackno, seqno, vec_ackno;
struct ccid2_seq *seqp;
unsigned char *vector;
unsigned char veclen;
int offset = 0;
int done = 0;
- unsigned int maxincr = 0;
+ int acked = 0;
ccid2_hc_tx_check_sanity(hctx);
/* check reverse path congestion */
@@ -779,7 +1058,7 @@ static void ccid2_hc_tx_packet_recv(stru
return;
}
- ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
+ vec_ackno = ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
if (ackno > hctx->ccid2hctx_high_ack)
hctx->ccid2hctx_high_ack = ackno;
@@ -792,13 +1071,6 @@ static void ccid2_hc_tx_packet_recv(stru
}
}
- /* If in slow-start, cwnd can increase at most Ack Ratio / 2 packets for
- * this single ack. I round up.
- * -sorbo.
- */
- maxincr = dp->dccps_l_ack_ratio >> 1;
- maxincr++;
-
/* go through all ack vectors */
while ((offset = ccid2_ackvector(sk, skb, offset,
&vector, &veclen)) != -1) {
@@ -836,11 +1108,13 @@ static void ccid2_hc_tx_packet_recv(stru
!seqp->ccid2s_acked) {
if (state ==
DCCP_ACKVEC_STATE_ECN_MARKED) {
- ccid2_congestion_event(hctx,
+ ccid2_congestion_event(sk,
seqp);
- } else
+ } else {
ccid2_new_ack(sk, seqp,
- &maxincr);
+ vec_ackno);
+ acked++;
+ }
seqp->ccid2s_acked = 1;
ccid2_pr_debug("Got ack for %llu\n",
@@ -864,6 +1138,9 @@ static void ccid2_hc_tx_packet_recv(stru
break;
}
+ if (acked && hctx->ccid2hctx_ca_ops->pkts_acked)
+ hctx->ccid2hctx_ca_ops->pkts_acked(sk, acked);
+
/* The state about what is acked should be correct now
* Check for NUMDUPACK
*/
@@ -902,7 +1179,7 @@ static void ccid2_hc_tx_packet_recv(stru
* order to detect multiple congestion events in
* one ack vector.
*/
- ccid2_congestion_event(hctx, seqp);
+ ccid2_congestion_event(sk, seqp);
ccid2_hc_tx_dec_pipe(sk);
}
if (seqp == hctx->ccid2hctx_seqt)
@@ -924,12 +1201,46 @@ static void ccid2_hc_tx_packet_recv(stru
ccid2_hc_tx_check_sanity(hctx);
}
+static int ccid2_hc_tx_ca(struct sock *sk, char *name)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ struct tcp_congestion_ops *ca = NULL;
+
+ /* XXX should be like TCP---a list of available CAs which can be
+ * registered at runtime. However, there needs to be support for "ccid
+ * globals" which currently do not exist. I can prolly have a static
+ * global in this file though...
+ */
+ if (strcmp(name, CCID2_CA_NAME) == 0)
+ ca = &ccid2_rfc;
+ else if (strncmp(name, CCID2_CA_TCP_NAME, strlen(CCID2_CA_TCP_NAME))
+ == 0) {
+ ca = &ccid2_tcp;
+ strncpy(hctx->ccid2hctx_tcp_name,
+ name + strlen(CCID2_CA_TCP_NAME),
+ sizeof(hctx->ccid2hctx_tcp_name) - 1);
+ hctx->ccid2hctx_tcp_name[sizeof(hctx->ccid2hctx_tcp_name)-1]= 0;
+ }
+
+ if (!ca)
+ return -1;
+
+ if (hctx->ccid2hctx_ca_ops && hctx->ccid2hctx_ca_ops->release)
+ hctx->ccid2hctx_ca_ops->release(sk);
+ hctx->ccid2hctx_ca_ops = ca;
+ if (hctx->ccid2hctx_ca_ops->init)
+ hctx->ccid2hctx_ca_ops->init(sk);
+ return 0;
+}
+
static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
{
struct ccid2_hc_tx_sock *hctx = ccid_priv(ccid);
+ int rc;
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct ccid *tmp;
if (ccid2_profile) {
- int rc;
int i;
for (i = 0; i < CCID2_PROF_LAST; i++) {
@@ -965,11 +1276,21 @@ static int ccid2_hc_tx_init(struct ccid
hctx->ccid2hctx_rpdupack = -1;
hctx->ccid2hctx_last_cong = jiffies;
hctx->ccid2hctx_high_ack = 0;
+ hctx->ccid2hctx_ca_ops = NULL;
hctx->ccid2hctx_rtotimer.function = &ccid2_hc_tx_rto_expire;
hctx->ccid2hctx_rtotimer.data = (unsigned long)sk;
init_timer(&hctx->ccid2hctx_rtotimer);
+ /* XXX hack: store ccid reference in sk because the ca init routine
+ * needs it
+ */
+ tmp = dp->dccps_hc_tx_ccid;
+ dp->dccps_hc_tx_ccid = ccid;
+ rc = ccid2_hc_tx_ca(sk, CCID2_CA_NAME);
+ BUG_ON(rc != 0);
+ dp->dccps_hc_tx_ccid = tmp;
+
ccid2_hc_tx_check_sanity(hctx);
return 0;
}
@@ -991,6 +1312,11 @@ static void ccid2_hc_tx_exit(struct sock
for (i = 0; i < CCID2_PROF_LAST; i++)
ccid2_pv_del(&hctx->ccid2hctx_profile.ccid2txp_vars[i]);
}
+
+ BUG_ON(!hctx->ccid2hctx_ca_ops);
+ if (hctx->ccid2hctx_ca_ops->release)
+ hctx->ccid2hctx_ca_ops->release(sk);
+ hctx->ccid2hctx_ca_ops = NULL;
}
static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
@@ -1050,6 +1376,24 @@ static int ccid2_hc_tx_profile(struct so
return 0;
}
+static int ccid2_hc_tx_algo(struct sock *sk, u32 __user *optval,
+ int __user *optlen)
+{
+ char name[DCCP_CA_NAME_MAX];
+ int len;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len > (sizeof(name) - 1))
+ return -EINVAL;
+
+ if (copy_from_user(name, optval, len))
+ return -EFAULT;
+ name[len] = 0;
+
+ return ccid2_hc_tx_ca(sk, name);
+}
+
static int ccid2_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
u32 __user *optval, int __user *optlen)
{
@@ -1057,6 +1401,10 @@ static int ccid2_hc_tx_getsockopt(struct
case DCCP_SOCKOPT_CCID_TX_INFO:
if (ccid2_profile)
return ccid2_hc_tx_profile(sk, optval, optlen);
+
+ /* XXX should be SETsockopt */
+ case DCCP_SOCKOPT_CCID_TX_ALGO:
+ return ccid2_hc_tx_algo(sk, optval, optlen);
}
return -ENOPROTOOPT;
diff --git a/net/dccp/ccids/ccid2.h b/net/dccp/ccids/ccid2.h
index 5f4720a..05da321 100644
--- a/net/dccp/ccids/ccid2.h
+++ b/net/dccp/ccids/ccid2.h
@@ -86,8 +86,6 @@ #define CCID2_SEQBUF_MAX 128
*/
struct ccid2_hc_tx_sock {
int ccid2hctx_cwnd;
- int ccid2hctx_ssacks;
- int ccid2hctx_acks;
int ccid2hctx_ssthresh;
int ccid2hctx_pipe;
int ccid2hctx_numdupack;
@@ -109,6 +107,21 @@ struct ccid2_hc_tx_sock {
unsigned long ccid2hctx_last_cong;
struct ccid2_txprofile ccid2hctx_profile;
u64 ccid2hctx_high_ack;
+ struct tcp_congestion_ops *ccid2hctx_ca_ops;
+ void *ccid2hctx_ca_priv;
+ char ccid2hctx_tcp_name[TCP_CA_NAME_MAX];
+};
+#define DCCP_CA_NAME_MAX (TCP_CA_NAME_MAX+4+1)
+
+struct ccid2ca_priv {
+ int ccid2cap_maxincr;
+ int ccid2cap_acks;
+ int ccid2cap_ssacks;
+ u64 ccid2cap_last_ack;
+};
+
+struct ccid2ca_priv_tcp {
+ struct tcp_sock ccid2capt_tp;
};
struct ccid2_hc_rx_sock {
@@ -124,4 +137,7 @@ static inline struct ccid2_hc_rx_sock *c
{
return ccid_priv(dccp_sk(sk)->dccps_hc_rx_ccid);
}
+
+static void ccid2_change_cwnd(struct ccid2_hc_tx_sock *hctx, int val);
+
#endif /* _DCCP_CCID2_H_ */
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 857eefc..efcccf0 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -94,6 +94,7 @@ void tcp_init_congestion_control(struct
if (icsk->icsk_ca_ops->init)
icsk->icsk_ca_ops->init(sk);
}
+EXPORT_SYMBOL_GPL(tcp_init_congestion_control);
/* Manage refcounts on socket close. */
void tcp_cleanup_congestion_control(struct sock *sk)
@@ -104,6 +105,7 @@ void tcp_cleanup_congestion_control(stru
icsk->icsk_ca_ops->release(sk);
module_put(icsk->icsk_ca_ops->owner);
}
+EXPORT_SYMBOL_GPL(tcp_cleanup_congestion_control);
/* Used by sysctl to change default congestion control */
int tcp_set_default_congestion_control(const char *name)
@@ -173,7 +175,7 @@ int tcp_set_congestion_control(struct so
rcu_read_unlock();
return err;
}
-
+EXPORT_SYMBOL_GPL(tcp_set_congestion_control);
/*
* Linear increase during slow start
-
To unsubscribe from this list: send the line "unsubscribe dccp" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html