Skip to contentSkip to conversation

CHOKe flow scheduler (0.7)

PATCH In review Stephen Hemminger 4 participants 26 messages Jan 13, 2011 – Feb 3, 2011

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 CHOKe flow scheduler (0.7)

This implements the CHOKe packet scheduler based on the existing Linux RED scheduler based on the algorithm described in the paper.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

---
Patch versions
0.3 (Eric) uses table for queue.
0.4 allows classification with TC filters
fixes crash when peek_random() finds a hole
0.5 (Eric) that fixes qlen with holes and peek 0.7 change to use separate params / stats than RED
account for drops in backlog

Almost ready, still need to make sure API (netlink) is right

 net/sched/Kconfig     |   11 +
 net/sched/Makefile    |    1 
 net/sched/sch_choke.c |  536 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 548 insertions(+)
net/sched/Kconfig +11 -0
@@ -205,6 +205,17 @@ config NET_SCH_DRR
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ)	+= sch_mult
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +556 -0

556 lines not on this page · raw message

include/linux/pkt_sched.h +29 -0
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* HARD maximal queue length (packets)	*/
+	__u32		qth_min;	/* Min average length threshold (packets) */
+	__u32		qth_max;	/* Max average length threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32           early;          /* Early drops */
+	__u32           pdrop;          /* Drops due to queue limits */
+	__u32           other;          /* Drops due to drop() calls */
+	__u32           marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
#1 lore raw

PATCH 1/1 CHOKe flow scheduler (iproute)

Preliminary interface for CHOKe scheduler in iproute

---
 include/linux/pkt_sched.h |   29 ++++++
 tc/Makefile               |    1 +
 tc/q_choke.c              |  221 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 251 insertions(+), 0 deletions(-)
 create mode 100644 tc/q_choke.c
include/linux/pkt_sched.h +29 -0
index 2cfa4bc..83bac92 100644
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* HARD maximal queue length (packets)	*/
+	__u32		qth_min;	/* Min average length threshold (packets) */
+	__u32		qth_max;	/* Max average length threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32           early;          /* Early drops */
+	__u32           pdrop;          /* Drops due to queue limits */
+	__u32           other;          /* Drops due to drop() calls */
+	__u32           marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
tc/Makefile +1 -0
index 101cc83..2cbd5d5 100644
@@ -15,6 +15,7 @@ TCMODULES += q_cbq.o
 TCMODULES += q_rr.o
 TCMODULES += q_multiq.o
 TCMODULES += q_netem.o
+TCMODULES += q_choke.o
 TCMODULES += f_rsvp.o
 TCMODULES += f_u32.o
 TCMODULES += f_route.o
tc/q_choke.c new file +221 -0

221 lines not on this page · raw message

Signature · 1 line
1.7.1
#2 lore raw

Re: [PATCH] CHOKe flow scheduler (iproute)

tc/q_choke.c line 187

Quoted 8 lines · Stephen Hemminger

Preliminary interface for CHOKe scheduler in iproute

tc/q_choke.c new file +6 -0
+		return -1;
+	fprintf(f, "limit %s min %s max %s ",
+		sprint_size(qopt->limit, b1),
+		sprint_size(qopt->qth_min, b2),
+		sprint_size(qopt->qth_max, b3));
+

sprint_size() adds 'b', not 'p' (packets)

fprintf(f, "limit %up min %up max %up ", qopt->limit, ...

#3 lore raw
In reply to Stephen Hemminger
Quoted 289 lines · Stephen Hemminger

This implements the CHOKe packet scheduler based on the existing Linux RED scheduler based on the algorithm described in the paper.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

---
Patch versions
0.3 (Eric) uses table for queue.
0.4 allows classification with TC filters
fixes crash when peek_random() finds a hole
0.5 (Eric) that fixes qlen with holes and peek 0.7 change to use separate params / stats than RED
account for drops in backlog

Almost ready, still need to make sure API (netlink) is right

 net/sched/Kconfig     |   11 +
 net/sched/Makefile    |    1 
 net/sched/sch_choke.c |  536 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 548 insertions(+)
net/sched/Kconfig +11 -0
@@ -205,6 +205,17 @@ config NET_SCH_DRR
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ)	+= sch_mult
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +215 -0
@@ -0,0 +1,556 @@
+/*
+ * net/sched/sch_choke.c	CHOKE scheduler
+ *
+ * Copyright (c) 2011 Stephen Hemminger <[email protected]>
+ * Copyright (c) 2011 Eric Dumazet <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/reciprocal_div.h>
+#include <net/pkt_sched.h>
+#include <net/inet_ecn.h>
+#include <net/red.h>
+
+/*	CHOKe stateless AQM for fair bandwidth allocation
+        =================================================
+
+   CHOKe (CHOose and Keep for responsive flows, CHOose and Kill for
+   unresponsive flows) is a variant of RED that penalizes misbehaving flows but
+   maintains no flow state. The difference from RED is an additional step
+   during the enqueuing process. If average queue size is over the
+   low threshold (qmin), a packet is chosen at random from the queue.
+   If both the new and chosen packet are from the same flow, both
+   are dropped. Unlike RED, CHOKe is not really a "classful" qdisc because it
+   needs to access packets in queue randomly. It has a minimal class
+   interface to allow overriding the builtin flow classifier with
+   filters.
+
+   Source:
+   R. Pan, B. Prabhakar, and K. Psounis, "CHOKe, A Stateless
+   Active Queue Management Scheme for Approximating Fair Bandwidth Allocation",
+   IEEE INFOCOM, 2000.
+
+   A. Tang, J. Wang, S. Low, "Understanding CHOKe: Throughput and Spatial
+   Characteristics", IEEE/ACM Transactions on Networking, 2004
+
+ */
+
+/* Upper bound on size of sk_buff table */
+#define CHOKE_MAX_QUEUE	(128*1024 - 1)
+
+struct choke_sched_data {
+/* Parameters */
+	u32		 limit;
+	unsigned char	 flags;
+
+	struct red_parms parms;
+
+/* Variables */
+	struct tcf_proto *filter_list;
+	struct {
+		u32	prob_drop;	/* Early probability drops */
+		u32	prob_mark;	/* Early probability marks */
+		u32	forced_drop;	/* Forced drops, qavg > max_thresh */
+		u32	forced_mark;	/* Forced marks, qavg > max_thresh */
+		u32	pdrop;          /* Drops due to queue limits */
+		u32	other;          /* Drops due to drop() calls */
+		u32	matched;	/* Drops to flow match */
+	} stats;
+
+	unsigned int	 head;
+	unsigned int	 tail;
+	unsigned int	 holes;
+	unsigned int	 tab_mask; /* size - 1 */
+
+	struct sk_buff **tab;
+};
+
+static inline unsigned int choke_len(const struct choke_sched_data *q)
+{
+	return (q->tail - q->head) & q->tab_mask;
+}
+
+/* deliver a random number between 0 and N - 1 */
+static inline u32 random_N(unsigned int N)
+{
+	return reciprocal_divide(random32(), N);
+}
+
+/* Select a packet at random from the queue in O(1) and handle holes */
+static struct sk_buff *choke_peek_random(struct choke_sched_data *q,
+					 unsigned int *pidx)
+{
+	struct sk_buff *skb;
+	int retrys = 3;
+
+	do {
+		*pidx = (q->head + random_N(choke_len(q))) & q->tab_mask;
+		skb = q->tab[*pidx];
+		if (skb)
+			return skb;
+	} while (--retrys > 0);
+
+	/* queue is has lots of holes use the head which is known to exist */
+	return q->tab[*pidx = q->head];
+}
+
+/* Is ECN parameter configured */
+static inline int use_ecn(const struct choke_sched_data *q)
+{
+	return q->flags & TC_RED_ECN;
+}
+
+/* Should packets over max just be dropped (versus marked) */
+static inline int use_harddrop(const struct choke_sched_data *q)
+{
+	return q->flags & TC_RED_HARDDROP;
+}
+
+/* Move head pointer forward to skip over holes */
+static void choke_zap_head_holes(struct choke_sched_data *q)
+{
+	while (q->holes && q->tab[q->head] == NULL) {
+		q->head = (q->head + 1) & q->tab_mask;
+		q->holes--;
+	}
+}
+
+/* Move tail pointer backwards to reuse holes */
+static void choke_zap_tail_holes(struct choke_sched_data *q)
+{
+	while (q->holes && q->tab[q->tail - 1] == NULL) {
+		q->tail = (q->tail - 1) & q->tab_mask;
+		q->holes--;
+	}
+}
+
+/* Drop packet from queue array by creating a "hole" */
+static void choke_drop_by_idx(struct choke_sched_data *q, unsigned int idx)
+{
+	q->tab[idx] = NULL;
+	q->holes++;
+
+	if (idx == q->head)
+		choke_zap_head_holes(q);
+	if (idx == q->tail)
+		choke_zap_tail_holes(q);
+}
+
+/* Classify flow using either:
+   1. pre-existing classification result in skb
+   2. fast internal classification
+   3. use TC filter based classification
+*/
+static inline unsigned int choke_classify(struct sk_buff *skb,
+					  struct Qdisc *sch, int *qerr)
+
+{
+	struct choke_sched_data *q = qdisc_priv(sch);
+	struct tcf_result res;
+	int result;
+
+	*qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
+
+	if (TC_H_MAJ(skb->priority) == sch->handle &&
+	    TC_H_MIN(skb->priority) > 0)
+		return TC_H_MIN(skb->priority);
+
+	if (!q->filter_list)
+		return skb_get_rxhash(skb);
+
+	result = tc_classify(skb, q->filter_list, &res);
+	if (result >= 0) {
+#ifdef CONFIG_NET_CLS_ACT
+		switch (result) {
+		case TC_ACT_STOLEN:
+		case TC_ACT_QUEUED:
+			*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+		case TC_ACT_SHOT:
+			return 0;
+		}
+#endif
+		return TC_H_MIN(res.classid);
+	}
+
+	return 0;
+}
+
+static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+	struct choke_sched_data *q = qdisc_priv(sch);
+	struct red_parms *p = &q->parms;
+	unsigned int hash;
+	int uninitialized_var(ret);
+
+	hash = choke_classify(skb, sch, &ret);
+	if (!hash) {
+		/* Packet was eaten by filter */
+		if (ret & __NET_XMIT_BYPASS)
+			sch->qstats.drops++;
+		kfree_skb(skb);
+		return ret;
+	}
+
+	/* Maybe add hash as field in struct qdisc_skb_cb? */
+	*(unsigned int *)(qdisc_skb_cb(skb)->data) = hash;
+
+	/* Compute average queue usage (see RED) */
+	p->qavg = red_calc_qavg(p, choke_len(q) - q->holes);
+	if (red_is_idling(p))
+		red_end_of_idle_period(p);
+
+	/* Is queue small? */
+	if (p->qavg <= p->qth_min)
+		p->qcount = -1;
+	else {
+		struct sk_buff *oskb;
+		unsigned int idx;

net/sched/sch_choke.c line 225

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+
+		/* Draw a packet at random from queue */
+		oskb = choke_peek_random(q, &idx);
+
+		/* Both packets from same flow ? */
+		if (*(unsigned int *)(qdisc_skb_cb(oskb)->data) == hash) {
+			/* Drop both packets */
+			q->stats.matched++;
+			choke_drop_by_idx(q, idx);
+			qdisc_drop(oskb, sch);

I feel we should add : sch->q.qlen--;

Quoted 31 lines
net/sched/sch_choke.c new file +31 -0
+			goto congestion_drop;
+		}
+
+		/* Queue is large, always mark/drop */
+		if (p->qavg > p->qth_max) {
+			p->qcount = -1;
+
+			sch->qstats.overlimits++;
+			if (use_harddrop(q) || !use_ecn(q) ||
+			    !INET_ECN_set_ce(skb)) {
+				q->stats.forced_drop++;
+				goto congestion_drop;
+			}
+
+			q->stats.forced_mark++;
+		} else if (++p->qcount) {
+			if (red_mark_probability(p, p->qavg)) {
+				p->qcount = 0;
+				p->qR = red_random(p);
+
+				sch->qstats.overlimits++;
+				if (!use_ecn(q) || !INET_ECN_set_ce(skb)) {
+					q->stats.prob_drop++;
+					goto congestion_drop;
+				}
+
+				q->stats.prob_mark++;
+			}
+		} else
+			p->qR = red_random(p);
+	}

net/sched/sch_choke.c line 266

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+
+	/* Admit new packet */
+	if (likely(choke_len(q) < q->limit)) {
+
+		q->tab[q->tail] = skb;
+		q->tail = (q->tail + 1) & q->tab_mask;
+
+		sch->qstats.backlog += qdisc_pkt_len(skb);
+		qdisc_update_bstats(sch, skb);
+		sch->q.qlen = choke_len(q) - q->holes;
	or : sch->q.qlen++;

(If sch->q.qlen is up2date in respect of above comment)

Quoted 19 lines
net/sched/sch_choke.c new file +19 -0
+		return NET_XMIT_SUCCESS;
+	}
+
+	q->stats.pdrop++;
+	sch->qstats.drops++;
+	kfree_skb(skb);
+	return NET_XMIT_DROP;
+
+ congestion_drop:
+	qdisc_drop(skb, sch);
+	return NET_XMIT_CN;
+}
+
+static struct sk_buff *choke_dequeue(struct Qdisc *sch)
+{
+	struct choke_sched_data *q = qdisc_priv(sch);
+	struct sk_buff *skb;
+
+	if (q->head == q->tail) {

net/sched/sch_choke.c line 295

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+		if (!red_is_idling(&q->parms))
+			red_start_of_idle_period(&q->parms);
+		return NULL;
+	}
+	skb = q->tab[q->head];
+	q->tab[q->head] = NULL; /* not really needed */
+	q->head = (q->head + 1) & q->tab_mask;
+	choke_zap_head_holes(q);
+	sch->qstats.backlog -= qdisc_pkt_len(skb);
+	sch->q.qlen = choke_len(q) - q->holes;
	sch->q.qlen--;
Quoted 20 lines
net/sched/sch_choke.c new file +20 -0
+
+	return skb;
+}
+
+static unsigned int choke_drop(struct Qdisc *sch)
+{
+	struct choke_sched_data *q = qdisc_priv(sch);
+	unsigned int len;
+
+	len = qdisc_queue_drop(sch);
+	if (len > 0)
+		q->stats.other++;
+	else {
+		if (!red_is_idling(&q->parms))
+			red_start_of_idle_period(&q->parms);
+	}
+
+	return len;
+}
+

net/sched/sch_choke.c line 325

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+static void choke_reset(struct Qdisc* sch)
+{
+	struct choke_sched_data *q = qdisc_priv(sch);
+
+	red_restart(&q->parms);
+}
+
+static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = {
+	[TCA_CHOKE_PARMS]	= { .len = sizeof(struct tc_red_qopt) },
+	[TCA_CHOKE_STAB]	= { .len = 256 },

RED_STAB_SIZE ?

Thanks !

#4 lore raw
Quoted 5 lines · Eric Dumazet
Quoted 4 lines · Stephen Hemminger

This implements the CHOKe packet scheduler based on the existing Linux RED scheduler based on the algorithm described in the paper.

Sorry for the long reply, I hit 'Send' button in the wrong window, before removing hunks.

#5 lore raw
In reply to Stephen Hemminger

net/sched/sch_choke.c line 269

Quoted 12 lines · Stephen Hemminger
net/sched/sch_choke.c new file +12 -0
+	/* Admit new packet */
+	if (likely(choke_len(q) < q->limit)) {
+
+		q->tab[q->tail] = skb;
+		q->tail = (q->tail + 1) & q->tab_mask;
+
+		sch->qstats.backlog += qdisc_pkt_len(skb);
+		qdisc_update_bstats(sch, skb);
+		sch->q.qlen = choke_len(q) - q->holes;
+		return NET_XMIT_SUCCESS;
+	}
+

You now must use qdisc_bstats_update() ;)

#6 lore raw

PATCH 1/1 CHOKe flow scheduler (0.8)

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability P (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

---
0.8 change queue length and holes account.

    keep sch->q.qlen updated, and holes counter not needed.
---
 net/sched/Kconfig     |   11 +
 net/sched/Makefile    |    1 
 net/sched/sch_choke.c |  536 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 548 insertions(+)
net/sched/Kconfig +11 -0
@@ -205,6 +205,17 @@ config NET_SCH_DRR
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ)	+= sch_mult
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +552 -0

552 lines not on this page · raw message

include/linux/pkt_sched.h +29 -0
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* HARD maximal queue length (packets)	*/
+	__u32		qth_min;	/* Min average length threshold (packets) */
+	__u32		qth_max;	/* Max average length threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32           early;          /* Early drops */
+	__u32           pdrop;          /* Drops due to queue limits */
+	__u32           other;          /* Drops due to drop() calls */
+	__u32           marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
#7 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

Quoted 26 lines · Stephen Hemminger

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability P (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

---
0.8 change queue length and holes account.

Quoted 8 lines
    keep sch->q.qlen updated, and holes counter not needed.
---
 net/sched/Kconfig     |   11 +
 net/sched/Makefile    |    1 
 net/sched/sch_choke.c |  536 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 548 insertions(+)

Hi Stephen

Your diffstat was an old one, here the right one.

 include/linux/pkt_sched.h |   29 +
 net/sched/Kconfig         |   11 
 net/sched/Makefile        |    1 
 net/sched/sch_choke.c     |  552 ++++++++++++++++++++++++++++++++++++

I tested v8 and found several serious problems, please find a diff of my latest changes :

- wrong oskb/skb used in choke_enqueue()
- choke_zap_head_holes() is called from choke_dequeue() and crash if we dequeued last packet. (!!!)
- out of bound access in choke_zap_tail_holes()
- choke_dequeue() can be shorter
- choke_change() must dequeue/drop in excess packets or risk new array overfill (if we reduce queue limit by tc qdisc change ...)
- inline is not needed, space errors in include file

Thanks !

 include/linux/pkt_sched.h |    8 +++---
 net/sched/sch_choke.c     |   43 ++++++++++++++++++++++--------------
 2 files changed, 31 insertions(+), 20 deletions(-)
include/linux/pkt_sched.h +4 -4
index 83bac92..498c798 100644
@@ -269,10 +269,10 @@ struct tc_choke_qopt {
 };
 
 struct tc_choke_xstats {
-	__u32           early;          /* Early drops */
-	__u32           pdrop;          /* Drops due to queue limits */
-	__u32           other;          /* Drops due to drop() calls */
-	__u32           marked;         /* Marked packets */
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
 	__u32		matched;	/* Drops due to flow match */
 };
 
net/sched/sch_choke.c +27 -16

101 lines not on this page · raw message

#8 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

Quoted 58 lines · Eric Dumazet
Quoted 26 lines · Stephen Hemminger

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with probability P (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

---
0.8 change queue length and holes account.

Quoted 8 lines
    keep sch->q.qlen updated, and holes counter not needed.
---
 net/sched/Kconfig     |   11 +
 net/sched/Makefile    |    1 
 net/sched/sch_choke.c |  536 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 548 insertions(+)

Hi Stephen

Your diffstat was an old one, here the right one.

 include/linux/pkt_sched.h |   29 +
 net/sched/Kconfig         |   11 
 net/sched/Makefile        |    1 
 net/sched/sch_choke.c     |  552 ++++++++++++++++++++++++++++++++++++

I tested v8 and found several serious problems, please find a diff of my latest changes :

- wrong oskb/skb used in choke_enqueue()
- choke_zap_head_holes() is called from choke_dequeue() and crash if we dequeued last packet. (!!!)
- out of bound access in choke_zap_tail_holes()
- choke_dequeue() can be shorter
- choke_change() must dequeue/drop in excess packets or risk new array overfill (if we reduce queue limit by tc qdisc change ...)
- inline is not needed, space errors in include file

Quoted 1 line

Thanks !

Hmm, please wait a bit, I had another crash when I stopped my bench/stress

#9 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

Quoted 2 lines · Eric Dumazet

Hmm, please wait a bit, I had another crash when I stopped my bench/stress

I am not sure p->qavg is correctly computed.

Crash happened because choke_peek_random() was called while no packet was in queue.

With my params (min=10833 max=32500 burst=18055 limit=130000) this implies qavg was very big while qlen==0 !

qdisc choke 11: dev ifb0 parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 200857857 bytes 365183 pkt (dropped 1010937, overlimits 557577 requeues 0)
rate 32253Kbit 7330pps backlog 17875996b 32505p requeues 0
marked 0 early 557577 pdrop 0 other 0 matched 226680

Here is latest diff :

 include/linux/pkt_sched.h |    8 +++----
 net/sched/sch_choke.c     |   50 +++++++++++++++++++++++++++++-----------------
 2 files changed, 36 insertions(+), 22 deletions(-)
include/linux/pkt_sched.h +4 -4
index 83bac92..498c798 100644
@@ -269,10 +269,10 @@ struct tc_choke_qopt {
 };
 
 struct tc_choke_xstats {
-	__u32           early;          /* Early drops */
-	__u32           pdrop;          /* Drops due to queue limits */
-	__u32           other;          /* Drops due to drop() calls */
-	__u32           marked;         /* Marked packets */
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
 	__u32		matched;	/* Drops due to flow match */
 };
 
net/sched/sch_choke.c +32 -18

115 lines not on this page · raw message

#10 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

Quoted 10 lines · Eric Dumazet
Quoted 2 lines · Eric Dumazet

Hmm, please wait a bit, I had another crash when I stopped my bench/stress

I am not sure p->qavg is correctly computed.

Crash happened because choke_peek_random() was called while no packet was in queue.

Quoted 7 lines

With my params (min=10833 max=32500 burst=18055 limit=130000) this implies qavg was very big while qlen==0 !

qdisc choke 11: dev ifb0 parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 200857857 bytes 365183 pkt (dropped 1010937, overlimits 557577 requeues 0)
rate 32253Kbit 7330pps backlog 17875996b 32505p requeues 0
marked 0 early 557577 pdrop 0 other 0 matched 226680

Moving the qdisc_bstats_update(sch, skb); out of choke_enqueue() to choke_dequeue(), I get nicer rate values (because packets that are enqueued but CHOKed dont artificialy raise the packet/byte rates)

Now, rate properly matches my 10Mbit CBQ bandwidth :

qdisc choke 11: parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 86470970 bytes 157418 pkt (dropped 127451, overlimits 48275 requeues 0) rate 9947Kbit 2264pps backlog 17759368b 32288p requeues 0
marked 0 early 48275 pdrop 0 other 0 matched 39588

For other qdiscs, it is less easy because qdisc_bstats_update() call is integrated in __qdisc_enqueue_tail() / qdisc_enqueue_tail(), so all users shall be updated at once.

#11 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

In reply to Stephen Hemminger
Quoted 11 lines · Stephen Hemminger
net/sched/sch_choke.c new file +10 -0
+static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
...
+	/* Is queue small? */
+	if (p->qavg <= p->qth_min)
+		p->qcount = -1;
+	else {
+		struct sk_buff *oskb;
+		unsigned int idx;
+
+		/* Draw a packet at random from queue */

net/sched/sch_choke.c line 223

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+		oskb = choke_peek_random(sch, &idx);
+
+		/* Both packets from same flow ? */
+		if (*(unsigned int *)(qdisc_skb_cb(oskb)->data) == hash) {
+			/* Drop both packets */
+			q->stats.matched++;
+			choke_drop_by_idx(q, idx);
+			sch->qstats.backlog -= qdisc_pkt_len(skb);
+			--sch->q.qlen;
+			qdisc_drop(oskb, sch);

You need to adjust the qlen values of parent qdiscs by calling qdisc_tree_decrease_qlen(), they are not aware that a second packet has been dropped.

Quoted 3 lines
net/sched/sch_choke.c new file +3 -0
+			goto congestion_drop;
+		}
+
#12 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

Quoted 18 lines · Patrick McHardy
Quoted 17 lines · Stephen Hemminger
net/sched/sch_choke.c new file +16 -0
+static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
...
+	/* Is queue small? */
+	if (p->qavg <= p->qth_min)
+		p->qcount = -1;
+	else {
+		struct sk_buff *oskb;
+		unsigned int idx;
+
+		/* Draw a packet at random from queue */
+		oskb = choke_peek_random(sch, &idx);
+
+		/* Both packets from same flow ? */
+		if (*(unsigned int *)(qdisc_skb_cb(oskb)->data) == hash) {
+			/* Drop both packets */
+			q->stats.matched++;
Quoted 8 lines

net/sched/sch_choke.c line 223

Quoted 4 lines
net/sched/sch_choke.c new file +4 -0
+			choke_drop_by_idx(q, idx);
+			sch->qstats.backlog -= qdisc_pkt_len(skb);
+			--sch->q.qlen;
+			qdisc_drop(oskb, sch);

You need to adjust the qlen values of parent qdiscs by calling qdisc_tree_decrease_qlen(), they are not aware that a second packet has been dropped.

I just saw that Eric already fixed this :)

#13 lore raw

Re: [PATCH] CHOKe flow scheduler (0.8)

Quoted 28 lines · Patrick McHardy
Quoted 18 lines · Patrick McHardy
Quoted 17 lines · Stephen Hemminger
(quoted diff) +16 -0
+static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
...
+	/* Is queue small? */
+	if (p->qavg <= p->qth_min)
+		p->qcount = -1;
+	else {
+		struct sk_buff *oskb;
+		unsigned int idx;
+
+		/* Draw a packet at random from queue */
+		oskb = choke_peek_random(sch, &idx);
+
+		/* Both packets from same flow ? */
+		if (*(unsigned int *)(qdisc_skb_cb(oskb)->data) == hash) {
+			/* Drop both packets */
+			q->stats.matched++;
Quoted 8 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+			choke_drop_by_idx(q, idx);
+			sch->qstats.backlog -= qdisc_pkt_len(skb);
+			--sch->q.qlen;
+			qdisc_drop(oskb, sch);

You need to adjust the qlen values of parent qdiscs by calling qdisc_tree_decrease_qlen(), they are not aware that a second packet has been dropped.

Quoted 2 lines

I just saw that Eric already fixed this :)
--

Thanks Patrick, I did that in choke_change() only, not on this part of the code.

qdisc cbq 1: root refcnt 2 rate 1000Mbit cell 8b (bounded,isolated) prio no-transmit/8 weight 1000Mbit allot 1514b level 2 ewma 5 avpkt 1000b maxidle 0us
Sent 92016573 bytes 167811 pkt (dropped 615544, overlimits 982637 requeues 0)
rate 6248bit 9pps backlog 0b 191202p requeues 0
borrowed 0 overactions 0 avgidle 125 undertime 0
qdisc choke 11: parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 91969929 bytes 167257 pkt (dropped 806746, overlimits 424342 requeues 0)
rate 56bit 0pps backlog 0b 0p requeues 0
marked 0 early 424342 pdrop 0 other 0 matched 191202

So yes : we see 191202 were CHOKed, and upper cbq leaks 191202 packets in 'backlog'

After following patch :

net/sched/sch_choke.c +1 -0
index 29a91d8..5479f7e 100644
@@ -223,6 +223,7 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 			q->stats.matched++;
 			choke_drop_by_idx(q, idx);
 			sch->qstats.backlog -= qdisc_pkt_len(oskb);
+			qdisc_tree_decrease_qlen(sch, 1);
 			--sch->q.qlen;
 			qdisc_drop(oskb, sch);
 			goto congestion_drop;

Everything seems fine :

qdisc cbq 1: root refcnt 2 rate 1000Mbit cell 8b (bounded,isolated) prio no-transmit/8 weight 1000Mbit allot 1514b level 2 ewma 5 avpkt 1000b maxidle 0us
Sent 93962148 bytes 170940 pkt (dropped 633053, overlimits 1010490 requeues 0)
rate 568bit 1pps backlog 0b 0p requeues 0
borrowed 0 overactions 0 avgidle 125 undertime 0
qdisc choke 11: parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 93957621 bytes 170888 pkt (dropped 829160, overlimits 436946 requeues 0)
rate 104bit 0pps backlog 0b 0p requeues 0
marked 0 early 436946 pdrop 0 other 0 matched 196107

#14 lore raw

PATCH 1/1 CHOKe flow scheduler (0.9)

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

---
This version is based on net-next, and assumes Eric's patch for corrected bstats is already applied.

0.9 incorporate patches from Patrick/Eric
rework the peek_random and drop code to simplify and fix bug where random_N needs to called with full length (including holes).

 include/linux/pkt_sched.h |   29 ++
 net/sched/Kconfig         |   11 
 net/sched/Makefile        |    1 
 net/sched/sch_choke.c     |  579 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 620 insertions(+)
net/sched/Kconfig +11 -0
@@ -205,6 +205,17 @@ config NET_SCH_DRR
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ)	+= sch_mult
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +579 -0

579 lines not on this page · raw message

include/linux/pkt_sched.h +29 -0
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* HARD maximal queue length (packets)	*/
+	__u32		qth_min;	/* Min average length threshold (packets) */
+	__u32		qth_max;	/* Max average length threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
#15 lore raw

Re: [PATCH] CHOKe flow scheduler (0.9)

Quoted 28 lines · Stephen Hemminger

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

Quoted 7 lines

---
This version is based on net-next, and assumes Eric's patch for corrected bstats is already applied.

0.9 incorporate patches from Patrick/Eric
rework the peek_random and drop code to simplify and fix bug where random_N needs to called with full length (including holes).

Nice catch, I now have more "matched" counts after my test :

qdisc choke 11: parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 93944198 bytes 170889 pkt (dropped 829140, overlimits 436686 requeues 0) rate 48bit 0pps backlog 0b 0p requeues 0
marked 0 early 436686 pdrop 0 other 0 matched 196227

You missed the qdisc_bstats_update() move from enqueue() to dequeue()

And some minor CodingStyle / checkpatch.pl changes, here is my latest diff on top of 0.9

I believe you can release v1 :)

Thanks !

 net/sched/sch_choke.c |   35 ++++++++++++++++++-----------------
 1 files changed, 18 insertions(+), 17 deletions(-)
net/sched/sch_choke.c +18 -17
index 55eeb7d..a7605a0 100644
@@ -20,7 +20,7 @@
 #include <net/red.h>
 
 /*	CHOKe stateless AQM for fair bandwidth allocation
-        =================================================
+	=================================================
 
    CHOKe (CHOose and Keep for responsive flows, CHOose and Kill for
    unresponsive flows) is a variant of RED that penalizes misbehaving flows but
@@ -137,10 +137,10 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)
 }
 
 /* Classify flow using either:
-   1. pre-existing classification result in skb
-   2. fast internal classification
-   3. use TC filter based classification
-*/
+ * 1. pre-existing classification result in skb
+ * 2. fast internal classification (rxhash)
+ * 3. use TC filter based classification
+ */
 static unsigned int choke_classify(struct sk_buff *skb,
 				   struct Qdisc *sch, int *qerr)
 
@@ -176,10 +176,9 @@ static unsigned int choke_classify(struct sk_buff *skb,
 }
 
 /* Select a packet at random from the queue and return flow classification
-   returns 0 if queue empty
+ * returns 0 if queue empty
  */
-static unsigned int choke_peek_random(struct Qdisc *sch,
-				      unsigned int *pidx)
+static unsigned int choke_peek_random(struct Qdisc *sch, unsigned int *pidx)
 {
 	struct choke_sched_data *q = qdisc_priv(sch);
 	const struct sk_buff *skb;
@@ -229,9 +228,9 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 		red_end_of_idle_period(p);
 
 	/* Is queue small? */
-	if (p->qavg <= p->qth_min)
+	if (p->qavg <= p->qth_min) {
 		p->qcount = -1;
-	else {
+	} else {
 		unsigned int idx;
 
 		/* Draw a packet at random from queue and compare flow */
@@ -266,8 +265,9 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 
 				q->stats.prob_mark++;
 			}
-		} else
+		} else {
 			p->qR = red_random(p);
+		}
 	}
 
 	/* Admit new packet */
@@ -276,7 +276,6 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 		q->tail = (q->tail + 1) & q->tab_mask;
 		++sch->q.qlen;
 		sch->qstats.backlog += qdisc_pkt_len(skb);
-		qdisc_bstats_update(sch, skb);
 		return NET_XMIT_SUCCESS;
 	}
 
@@ -306,6 +305,7 @@ static struct sk_buff *choke_dequeue(struct Qdisc *sch)
 	choke_zap_head_holes(q);
 	--sch->q.qlen;
 	sch->qstats.backlog -= qdisc_pkt_len(skb);
+	qdisc_bstats_update(sch, skb);
 
 	return skb;
 }
@@ -316,9 +316,9 @@ static unsigned int choke_drop(struct Qdisc *sch)
 	unsigned int len;
 
 	len = qdisc_queue_drop(sch);
-	if (len > 0)
+	if (len > 0) {
 		q->stats.other++;
-	else {
+	} else {
 		if (!red_is_idling(&q->parms))
 			red_start_of_idle_period(&q->parms);
 	}
@@ -326,7 +326,7 @@ static unsigned int choke_drop(struct Qdisc *sch)
 	return len;
 }
 
-static void choke_reset(struct Qdisc* sch)
+static void choke_reset(struct Qdisc *sch)
 {
 	struct choke_sched_data *q = qdisc_priv(sch);
 
@@ -410,8 +410,9 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
 
 		q->tab_mask = mask;
 		q->tab = ntab;
-	} else
+	} else {
 		sch_tree_lock(sch);
+	}
 
 	q->flags = ctl->flags;
 	q->limit = ctl->limit;
@@ -428,7 +429,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
 	return 0;
 }
 
-static int choke_init(struct Qdisc* sch, struct nlattr *opt)
+static int choke_init(struct Qdisc *sch, struct nlattr *opt)
 {
 	return choke_change(sch, opt);
 }
#16 lore raw

Re: [PATCH] CHOKe flow scheduler (0.9)

Quoted 44 lines · On Sat, 15 Jan 2011 08:45:42 +0100 Eric Dumazet <[email protected]> wrote:
Quoted 28 lines · Stephen Hemminger

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

Quoted 7 lines

---
This version is based on net-next, and assumes Eric's patch for corrected bstats is already applied.

0.9 incorporate patches from Patrick/Eric
rework the peek_random and drop code to simplify and fix bug where random_N needs to called with full length (including holes).

Nice catch, I now have more "matched" counts after my test :

qdisc choke 11: parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 93944198 bytes 170889 pkt (dropped 829140, overlimits 436686 requeues 0) rate 48bit 0pps backlog 0b 0p requeues 0
marked 0 early 436686 pdrop 0 other 0 matched 196227

Quoted 8 lines

You missed the qdisc_bstats_update() move from enqueue() to dequeue()

And some minor CodingStyle / checkpatch.pl changes, here is my latest diff on top of 0.9

I believe you can release v1 :)

Thanks !

I rolled in your changes. But there is one more change I want to make. The existing flow match based on hash is vulnerable to side-channel DoS attack. It is possible for a hostile flow to send packets that match the same hash value which would effectively kill a targeted flow.

The solution is to match based on full source and destination, not hash value. Still coding that up.

#17 lore raw

Re: [PATCH] CHOKe flow scheduler (0.9)

Quoted 7 lines · Stephen Hemminger

I rolled in your changes. But there is one more change I want to make. The existing flow match based on hash is vulnerable to side-channel DoS attack. It is possible for a hostile flow to send packets that match the same hash value which would effectively kill a targeted flow.

The solution is to match based on full source and destination, not hash value. Still coding that up.

I see, but you only want to make this full test if (!q->filter_list) ?

(or precisely only if skb_get_rxhash() was used to get the cookie )

#18 lore raw

Re: [PATCH] CHOKe flow scheduler (0.9)

Quoted 7 lines · On Mon, 17 Jan 2011 18:54:11 +0100 Eric Dumazet <[email protected]> wrote:
Quoted 5 lines · Stephen Hemminger

I rolled in your changes. But there is one more change I want to make. The existing flow match based on hash is vulnerable to side-channel DoS attack. It is possible for a hostile flow to send packets that match the same hash value which would effectively kill a targeted flow.

Quoted 6 lines
Quoted 2 lines

The solution is to match based on full source and destination, not hash value. Still coding that up.

I see, but you only want to make this full test if (!q->filter_list) ?

(or precisely only if skb_get_rxhash() was used to get the cookie )

This is what I am starting to retest. The code can probably be simplified to avoid the may_pull() on the packet already in queue.

Subject: sched: CHOKe flow scheduler

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

---
This version is based on net-next, and assumes Eric's patch for corrected bstats is already applied.

0.9 incorporate patches from Patrick/Eric
rework the peek_random and drop code to simplify and fix bug where random_N needs to called with full length (including holes).

 include/linux/pkt_sched.h |   29 ++
 net/sched/Kconfig         |   11 
 net/sched/Makefile        |    1 
 net/sched/sch_choke.c     |  579 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 620 insertions(+)
net/sched/Kconfig +11 -0
@@ -205,6 +205,17 @@ config NET_SCH_DRR
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ)	+= sch_mult
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +686 -0

686 lines not on this page · raw message

include/linux/pkt_sched.h +29 -0
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* Hard queue length (packets)	*/
+	__u32		qth_min;	/* Min average threshold (packets) */
+	__u32		qth_max;	/* Max average threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
#19 lore raw

Re: [PATCH] CHOKe flow scheduler (0.9)

Quoted 10 lines · Stephen Hemminger
net/sched/sch_choke.c new file +10 -0
+static bool choke_match_flow(struct sk_buff *skb1, struct sk_buff *skb2)
+{
+	int off1, off2, poff;
+	u8 ip_proto;
+	u32 ihl;
+
+	if (skb1->protocol != skb2->protocol)
+		return false;
+
+	off1 = skb_network_offset(skb1);

net/sched/sch_choke.c line 167

Quoted 10 lines
net/sched/sch_choke.c new file +9 -0
+	off2 = skb_network_offset(skb2);
 
+
+	switch (skb1->protocol) {
+	case __constant_htons(ETH_P_IP): {
+		struct iphdr *ip1, *ip2;
+
+		if (!pskb_may_pull(skb1, sizeof(struct iphdr) + off1))
+			return false;
+

pskb_network_may_pull() might be cleaner

net/sched/sch_choke.c line 168

Quoted 1 line
net/sched/sch_choke.c new file +1 -0
+		ip1 = (struct iphdr *) (skb1->data + off1);
	ip1 = ip_hdr(skb);

net/sched/sch_choke.c line 171

Quoted 3 lines
net/sched/sch_choke.c new file +3 -0
+		if (ip1->frag_off & htons(IP_MF | IP_OFFSET))
+			return false;	/* don't compare fragments */
+

Hmm, we should compare fragments if possible.

saddr/daddr are available, not the ports.

net/sched/sch_choke.c line 182

Quoted 11 lines
net/sched/sch_choke.c new file +11 -0
+		if (!pskb_may_pull(skb2, sizeof(struct iphdr) + off2))
+			return false;
+
+		ip2 = (struct iphdr *) (skb2->data + off2);
+		if (ip2->frag_off & htons(IP_MF | IP_OFFSET))
+			return false;
+
+		if (ip1->protocol != ip2->protocol ||
+		    ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr)
+			return false;
+
	What happens if ip1->ihl != ip2->ihl  here ?

Here I would add the fragment test :

	if ((ip1->frag_off | ip2->frag_off)) & htons(IP_MF | IP_OFFSET))
		return true;

net/sched/sch_choke.c line 192

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+		ip_proto = ip1->protocol;
+		ihl = ip1->ihl;
+		break;
+	}
+
+	case __constant_htons(ETH_P_IPV6): {
+		struct ipv6hdr *ip1, *ip2;
+
+		if (!pskb_may_pull(skb1, sizeof(struct ipv6hdr *) + off1))
+			return false;

ouch... sizeof(sizeof(struct ipv6hdr *) is not what you want but sizeof(struct ipv6hdr) is.

So just use :

	pskb_network_may_pull(skb1, sizeof(*ip1))

net/sched/sch_choke.c line 197

Quoted 5 lines
net/sched/sch_choke.c new file +5 -0
+
+		if (!pskb_may_pull(skb2, sizeof(struct ipv6hdr *) + off2))
+			return false;
+
+		ip1 = (struct ipv6hdr *) (skb1->data + off1);
	ip1 = ipv6_hdr(skb1);
Quoted 16 lines
net/sched/sch_choke.c new file +16 -0
+		ip2 = (struct ipv6hdr *) (skb2->data + off2);
+
+		if (ip1->nexthdr != ip2->nexthdr ||
+		    ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) != 0 ||
+		    ipv6_addr_cmp(&ip1->daddr, &ip2->daddr))
+			return false;
+
+		ihl = (40 >> 2);
+		ip_proto = ip1->nexthdr;
+		break;
+	}
+
+	default:
+		return false;
+	}
+
#20 lore raw

PATCH 1/1 CHOKe flow scheduler (0.10)

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

---
0.10 The internal flow classifier now does full compare of IPv4/IPv6 headers
to avoid possible DoS attack from hash collision. Internal flow match code needs review/testing.

 include/linux/pkt_sched.h |   29 +
 net/sched/Kconfig         |   11 
 net/sched/Makefile        |    1 
 net/sched/sch_choke.c     |  713 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 754 insertions(+)
net/sched/Kconfig +11 -0
@@ -205,6 +205,17 @@ config NET_SCH_DRR
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +1 -0
@@ -32,6 +32,7 @@ obj-$(CONFIG_NET_SCH_MULTIQ)	+= sch_mult
 obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +713 -0

713 lines not on this page · raw message

include/linux/pkt_sched.h +29 -0
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* Hard queue length (packets)	*/
+	__u32		qth_min;	/* Min average threshold (packets) */
+	__u32		qth_max;	/* Max average threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
#21 lore raw

Re: [PATCH] CHOKe flow scheduler (0.10)

Le jeudi 20 janvier 2011 à 09:38 -0800, Stephen Hemminger a écrit :

...

Hi Stephen

I am contemplating choke_linearize_header() (yet another hash function... oh well...) and say :

Instead of this boolean return, just use skb_get_rxhash(). It performs what you want, and return 32bits of information, instead of one bit.

(if result (rxhash) is zero, it means you can drop packet because it is not linearized)

So choke_match_flow() can be faster if rxhash differs (no cache miss on skb->data on an old skb)
(Do the full check only if rxhash is equal on skb1 / skb2)

More over, the skb_get_rxhash() call is _really_ needed only if CHOKe triggers :
if (p->qavg <= p->qth_min)
we dont have to compute rxhash at all.

Quoted 93 lines
net/sched/sch_choke.c new file +91 -0
+/* Make sure network and transport headers can be dereferenced */
+static bool choke_linearize_header(struct sk_buff *skb)
+{
+	int nhoff, poff;
+	struct ipv6hdr *ip6;
+	struct iphdr *ip;
+	u8 ip_proto;
+	u32 ihl;
+
+	nhoff = skb_network_offset(skb);
+
+	switch (skb->protocol) {
+	case __constant_htons(ETH_P_IP):
+		if (!pskb_may_pull(skb, sizeof(*ip) + nhoff))
+			return false;
+
+		ip = (struct iphdr *) (skb->data + nhoff);
+		if (ip->frag_off & htons(IP_MF | IP_OFFSET))
+			return false;
+
+		ip_proto = ip->protocol;
+		ihl = ip->ihl;
+		break;
+	case __constant_htons(ETH_P_IPV6):
+		if (!pskb_may_pull(skb, sizeof(*ip6) + nhoff))
+			return false;
+
+		ip6 = (struct ipv6hdr *) (skb->data + nhoff);
+		ip_proto = ip6->nexthdr;
+		ihl = (40 >> 2);
+		break;
+	default:
+		return true;
+	}
+
+	poff = proto_ports_offset(ip_proto);
+	if (poff >= 0) {
+		nhoff += ihl * 4 + poff;
+		if (!pskb_may_pull(skb, nhoff + 4))
+			return false;
+	}
+
+	return true;
+}
+
 
 
+/*
+ * Compare flow of two packets
+ *  Returns true only if source and destination address and port match.
+ *          false for special cases
+ */
+static bool choke_match_flow(const struct sk_buff *skb1,
+			     const struct sk_buff *skb2)
+{
+	int off1, off2, poff;
+
+	if (skb1->protocol != skb2->protocol)
+		return false;
+
+	off1 = skb_network_offset(skb1);
+	off2 = skb_network_offset(skb2);
+
+	switch (skb1->protocol) {
+	case __constant_htons(ETH_P_IP): {
+		const struct iphdr *ip1, *ip2;
+
+		ip1 = (struct iphdr *) (skb1->data + off1);
+		ip2 = (struct iphdr *) (skb2->data + off2);
+
+		if (ip1->protocol != ip2->protocol ||
+		    ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr)
+			return false;
+
+		if (ip1->frag_off & htons(IP_MF | IP_OFFSET) ||
+		    ip2->frag_off & htons(IP_MF | IP_OFFSET))
+			return ip1->id == ip2->id;
+
+		poff = proto_ports_offset(ip1->protocol);
+		if (poff < 0)
+			return true;
+		else {
+			const u32 *ports1, *ports2;
+
+			ports1 = (__force u32 *) (skb1->data + off1
+						  + ip1->ihl * 4 + poff);
+			ports2 = (__force u32 *) (skb2->data + off2
+						  + ip2->ihl * 4 + poff);
+
+			return *ports1 == *ports2;
+		}
+		break;
+	}

net/sched/sch_choke.c line 244

Quoted 10 lines
net/sched/sch_choke.c new file +10 -0
+
+	case __constant_htons(ETH_P_IPV6): {
+		const struct ipv6hdr *ip1, *ip2;
+
+		ip1 = (struct ipv6hdr *) (skb1->data + off1);
+		ip2 = (struct ipv6hdr *) (skb2->data + off2);
+
+		return (ip1->nexthdr == ip2->nexthdr &&
+			ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) == 0 &&
+			ipv6_addr_cmp(&ip1->daddr, &ip2->daddr) == 0);

Hmm, we also need to check ports, you might just move the check from case __constant_htons(ETH_P_IP) after the switch(skb1->protocol)

net/sched/sch_choke.c line 252

Quoted 8 lines
net/sched/sch_choke.c new file +8 -0
+	}
+
+	default: /* Maybe compare MAC header here? */
+		return false;
+	}
+	/* not reached */
+}
+

If you want, I can send a diff on your v0.10, just tell me !

Thanks !

#22 lore raw

Re: [PATCH] CHOKe flow scheduler (0.10)

Quoted 19 lines · Eric Dumazet

Le jeudi 20 janvier 2011 à 09:38 -0800, Stephen Hemminger a écrit :

...

Hi Stephen

I am contemplating choke_linearize_header() (yet another hash function... oh well...) and say :

Instead of this boolean return, just use skb_get_rxhash(). It performs what you want, and return 32bits of information, instead of one bit.

(if result (rxhash) is zero, it means you can drop packet because it is not linearized)

So choke_match_flow() can be faster if rxhash differs (no cache miss on skb->data on an old skb)
(Do the full check only if rxhash is equal on skb1 / skb2)

Quoted 7 lines

More over, the skb_get_rxhash() call is _really_ needed only if CHOKe triggers :
if (p->qavg <= p->qth_min)
we dont have to compute rxhash at all.

If you want, I can send a diff on your v0.10, just tell me !

Here is my diff against v 0.10

Seems to work well here

qdisc choke 11: parent 1:11 limit 130000b min 10833b max 32500b ewma 13 Plog 21 Scell_log 30
Sent 459876256 bytes 836183 pkt (dropped 318428, overlimits 28 requeues 0) rate 51823Kbit 11779pps backlog 5926323b 10781p requeues 0
marked 0 early 28 pdrop 0 other 0 matched 159200

Thanks !

 net/sched/sch_choke.c |  114 ++++++++++++----------------------------
 1 file changed, 37 insertions(+), 77 deletions(-)
net/sched/sch_choke.c +37 -77
index e1ac560..0e898ed 100644
@@ -141,63 +141,24 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx)
 	--sch->q.qlen;
 }
 
-/* Make sure network and transport headers can be dereferenced */
-static bool choke_linearize_header(struct sk_buff *skb)
-{
-	int nhoff, poff;
-	struct ipv6hdr *ip6;
-	struct iphdr *ip;
-	u8 ip_proto;
-	u32 ihl;
-
-	nhoff = skb_network_offset(skb);
-
-	switch (skb->protocol) {
-	case __constant_htons(ETH_P_IP):
-		if (!pskb_may_pull(skb, sizeof(*ip) + nhoff))
-			return false;
-
-		ip = (struct iphdr *) (skb->data + nhoff);
-		if (ip->frag_off & htons(IP_MF | IP_OFFSET))
-			return false;
-
-		ip_proto = ip->protocol;
-		ihl = ip->ihl;
-		break;
-	case __constant_htons(ETH_P_IPV6):
-		if (!pskb_may_pull(skb, sizeof(*ip6) + nhoff))
-			return false;
-
-		ip6 = (struct ipv6hdr *) (skb->data + nhoff);
-		ip_proto = ip6->nexthdr;
-		ihl = (40 >> 2);
-		break;
-	default:
-		return true;
-	}
-
-	poff = proto_ports_offset(ip_proto);
-	if (poff >= 0) {
-		nhoff += ihl * 4 + poff;
-		if (!pskb_may_pull(skb, nhoff + 4))
-			return false;
-	}
-
-	return true;
-}
-
 /*
  * Compare flow of two packets
  *  Returns true only if source and destination address and port match.
  *          false for special cases
  */
-static bool choke_match_flow(const struct sk_buff *skb1,
-			     const struct sk_buff *skb2)
+static bool choke_match_flow(struct sk_buff *skb1,
+			     struct sk_buff *skb2)
 {
 	int off1, off2, poff;
+	const u32 *ports1, *ports2;
+	u8 ip_proto;
 
 	if (skb1->protocol != skb2->protocol)
 		return false;
+	if (skb_get_rxhash(skb1) != skb_get_rxhash(skb2))
+		return false;
+	if (!skb_get_rxhash(skb1))
+		return true;
 
 	off1 = skb_network_offset(skb1);
 	off2 = skb_network_offset(skb2);
@@ -209,27 +170,14 @@ static bool choke_match_flow(const struct sk_buff *skb1,
 		ip1 = (struct iphdr *) (skb1->data + off1);
 		ip2 = (struct iphdr *) (skb2->data + off2);
 
-		if (ip1->protocol != ip2->protocol ||
+		ip_proto = ip1->protocol;
+		if (ip_proto != ip2->protocol ||
 		    ip1->saddr != ip2->saddr || ip1->daddr != ip2->daddr)
 			return false;
-
-		if (ip1->frag_off & htons(IP_MF | IP_OFFSET) ||
-		    ip2->frag_off & htons(IP_MF | IP_OFFSET))
-			return ip1->id == ip2->id;
-
-		poff = proto_ports_offset(ip1->protocol);
-		if (poff < 0)
-			return true;
-		else {
-			const u32 *ports1, *ports2;
-
-			ports1 = (__force u32 *) (skb1->data + off1
-						  + ip1->ihl * 4 + poff);
-			ports2 = (__force u32 *) (skb2->data + off2
-						  + ip2->ihl * 4 + poff);
-
-			return *ports1 == *ports2;
-		}
+		if ((ip1->frag_off | ip2->frag_off) & htons(IP_MF | IP_OFFSET))
+			ip_proto = 0;
+		off1 += ip1->ihl * 4;
+		off2 += ip2->ihl * 4;
 		break;
 	}
 
@@ -239,15 +187,32 @@ static bool choke_match_flow(const struct sk_buff *skb1,
 		ip1 = (struct ipv6hdr *) (skb1->data + off1);
 		ip2 = (struct ipv6hdr *) (skb2->data + off2);
 
-		return (ip1->nexthdr == ip2->nexthdr &&
-			ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) == 0 &&
-			ipv6_addr_cmp(&ip1->daddr, &ip2->daddr) == 0);
+		ip_proto = ip1->nexthdr;
+		if (ip_proto != ip2->nexthdr ||
+		    ipv6_addr_cmp(&ip1->saddr, &ip2->saddr) ||
+		    ipv6_addr_cmp(&ip1->daddr, &ip2->daddr))
+			return false;
+		off1 += 40;
+		off2 += 40;
 	}
 
 	default: /* Maybe compare MAC header here? */
 		return false;
 	}
-	/* not reached */
+	poff = proto_ports_offset(ip_proto);
+	if (poff < 0)
+		return true;
+	off1 += poff;
+	off2 += poff;
+	if (!pskb_may_pull(skb1, off1 + 4))
+		return false;
+
+	if (!pskb_may_pull(skb2, off2 + 4))
+		return false;
+
+	ports1 = (__force u32 *)(skb1->data + off1);
+	ports2 = (__force u32 *)(skb2->data + off2);
+	return *ports1 == *ports2;
 }
 
 static inline void choke_set_classid(struct sk_buff *skb, u16 classid)
@@ -319,10 +284,10 @@ static struct sk_buff *choke_peek_random(const struct choke_sched_data *q,
  * returns true if matched and sets *pidx
  */
 static bool choke_match_random(const struct choke_sched_data *q,
-			       const struct sk_buff *nskb,
+			       struct sk_buff *nskb,
 			       unsigned int *pidx)
 {
-	const struct sk_buff *oskb;
+	struct sk_buff *oskb;
 
 	if (q->head == q->tail)
 		return false;
@@ -331,7 +296,6 @@ static bool choke_match_random(const struct choke_sched_data *q,
 	if (q->filter_list)
 		return choke_get_classid(nskb) == choke_get_classid(oskb);
 
-
 	return choke_match_flow(oskb, nskb);
 }
 
@@ -345,10 +309,6 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 		/* If using external classifiers, get result and record it. */
 		if (!choke_classify(skb, sch, &ret))
 			goto other_drop;	/* Packet was eaten by filter */
-	} else {
-		/* for internal classifier, make header accessible */
-		if (!choke_linearize_header(skb))
-			goto other_drop;
 	}
 
 	/* Compute average queue usage (see RED) */
#23 lore raw

PATCH 1/1 CHOKe flow scheduler (0.11)

Subject: sched: CHOKe flow scheduler

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

---
0.11 - incorporates Eric's change to use rxhash

 include/linux/pkt_sched.h |   29 +
 net/sched/Kconfig         |   11 
 net/sched/Makefile        |    2 
 net/sched/sch_choke.c     |  676 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 718 insertions(+)
net/sched/Kconfig +11 -0
@@ -217,6 +217,17 @@ config NET_SCH_MQPRIO
 
 	  If unsure, say N.
 
+config NET_SCH_CHOKE
+	tristate "CHOose and Keep responsive flow scheduler (CHOKE)"
+	help
+	  Say Y here if you want to use the CHOKe packet scheduler (CHOose
+	  and Keep for responsive flows, CHOose and Kill for unresponsive
+	  flows). This is a variation of RED which trys to penalize flows
+	  that monopolize the queue.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called sch_choke.
+
 config NET_SCH_INGRESS
 	tristate "Ingress Qdisc"
 	depends on NET_CLS_ACT
net/sched/Makefile +2 -0
@@ -33,6 +33,8 @@ obj-$(CONFIG_NET_SCH_ATM)	+= sch_atm.o
 obj-$(CONFIG_NET_SCH_NETEM)	+= sch_netem.o
 obj-$(CONFIG_NET_SCH_DRR)	+= sch_drr.o
 obj-$(CONFIG_NET_SCH_MQPRIO)	+= sch_mqprio.o
+obj-$(CONFIG_NET_SCH_CHOKE)	+= sch_choke.o
+
 obj-$(CONFIG_NET_CLS_U32)	+= cls_u32.o
 obj-$(CONFIG_NET_CLS_ROUTE4)	+= cls_route.o
 obj-$(CONFIG_NET_CLS_FW)	+= cls_fw.o
net/sched/sch_choke.c new file +676 -0

676 lines not on this page · raw message

include/linux/pkt_sched.h +29 -0
@@ -247,6 +247,35 @@ struct tc_gred_sopt {
 	__u16		pad1;
 };
 
+/* CHOKe section */
+
+enum {
+	TCA_CHOKE_UNSPEC,
+	TCA_CHOKE_PARMS,
+	TCA_CHOKE_STAB,
+	__TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+	__u32		limit;		/* Hard queue length (packets)	*/
+	__u32		qth_min;	/* Min average threshold (packets) */
+	__u32		qth_max;	/* Max average threshold (packets) */
+	unsigned char   Wlog;		/* log(W)		*/
+	unsigned char   Plog;		/* log(P_max/(qth_max-qth_min))	*/
+	unsigned char   Scell_log;	/* cell size for idle damping */
+	unsigned char	flags;		/* see RED flags */
+};
+
+struct tc_choke_xstats {
+	__u32		early;          /* Early drops */
+	__u32		pdrop;          /* Drops due to queue limits */
+	__u32		other;          /* Drops due to drop() calls */
+	__u32		marked;         /* Marked packets */
+	__u32		matched;	/* Drops due to flow match */
+};
+
 /* HTB section */
 #define TC_HTB_NUMPRIO		8
 #define TC_HTB_MAXDEPTH		8
#24 lore raw

Re: [PATCH net-next] CHOKe flow scheduler (0.11)

Quoted 24 lines · Stephen Hemminger

Subject: sched: CHOKe flow scheduler

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Quoted 10 lines

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

---
0.11 - incorporates Eric's change to use rxhash

Thanks Stephen !

maintainer
#25 lore raw

Re: [PATCH net-next] CHOKe flow scheduler (0.11)

From: Eric Dumazet <[email protected]>
Date: Thu, 03 Feb 2011 02:59:12 +0100

Quoted 29 lines · Stephen Hemminger
Quoted 28 lines

Subject: sched: CHOKe flow scheduler

CHOKe ("CHOose and Kill" or "CHOose and Keep") is an alternative packet scheduler based on the Random Exponential Drop (RED) algorithm.

The core idea is:
For every packet arrival:
Calculate Qave
if (Qave < minth)
Queue the new packet
else
Select randomly a packet from the queue if (both packets from same flow)
then Drop both the packets
else if (Qave > maxth)
Drop packet
else
Admit packet with proability p (same as RED)

See also:
Rong Pan, Balaji Prabhakar, Konstantinos Psounis, "CHOKe: a stateless active
queue management scheme for approximating fair bandwidth allocation",
Proceeding of INFOCOM'2000, March 2000.

Help from:
Eric Dumazet <[email protected]>
Patrick McHardy <[email protected]>

Quoted 8 lines
Quoted 6 lines

---
0.11 - incorporates Eric's change to use rxhash

Also applied, thanks guys!