This is available as a module called sch_codel.
For details on CoDel see:
http://lwn.net/Articles/496509/
This goes only compile tested against all the below kernels,
run time test results would be appreciated.
mcgrof@tux ~/compat (git::master)$ ckmake
Trying kernel 3.4.0-030400rc1-generic [OK]
Trying kernel 3.3.7-030307-generic [OK]
Trying kernel 3.2.2-030202-generic [OK]
Trying kernel 3.1.10-030110-generic [OK]
Trying kernel 3.0.18-030018-generic [OK]
Trying kernel 2.6.39-
02063904-generic [OK]
Trying kernel 2.6.38-
02063808-generic [OK]
Trying kernel 2.6.37-
02063706-generic [OK]
Trying kernel 2.6.36-
02063604-generic [OK]
Trying kernel 2.6.35-
02063512-generic [OK]
Trying kernel 2.6.34-
02063410-generic [OK]
Trying kernel 2.6.33-
02063305-generic [OK]
Trying kernel 2.6.32-
02063255-generic [OK]
Trying kernel 2.6.31-
02063113-generic [OK]
Trying kernel 2.6.30-
02063010-generic [OK]
Trying kernel 2.6.29-
02062906-generic [OK]
Trying kernel 2.6.28-
02062810-generic [OK]
Trying kernel 2.6.27-020627-generic [OK]
Trying kernel 2.6.26-020626-generic [OK]
Trying kernel 2.6.25-020625-generic [OK]
Trying kernel 2.6.24-020624-generic [OK]
Signed-off-by: Luis R. Rodriguez <mcgrof@frijolero.org>
#compat-objs :=
obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o
+obj-$(CONFIG_COMPAT_NET_SCH_CODEL) += sch_codel.o
compat-y += main.o
struct codel_vars vars;
struct codel_stats stats;
u32 drop_overlimit;
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ u32 limit;
+#endif
};
/* This is the specific function called from codel_dequeue()
{
struct codel_sched_data *q;
+ q = qdisc_priv(sch);
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ if (likely(qdisc_qlen(sch) < q->limit)) {
+#else
if (likely(qdisc_qlen(sch) < sch->limit)) {
+#endif
codel_set_enqueue_time(skb);
return qdisc_enqueue_tail(skb, sch);
}
- q = qdisc_priv(sch);
q->drop_overlimit++;
return qdisc_drop(skb, sch);
}
}
if (tb[TCA_CODEL_LIMIT])
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ q->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]);
+#else
sch->limit = nla_get_u32(tb[TCA_CODEL_LIMIT]);
+#endif
if (tb[TCA_CODEL_ECN])
q->params.ecn = !!nla_get_u32(tb[TCA_CODEL_ECN]);
qlen = sch->q.qlen;
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ while (sch->q.qlen > q->limit) {
+#else
while (sch->q.qlen > sch->limit) {
+#endif
struct sk_buff *skb = __skb_dequeue(&sch->q);
sch->qstats.backlog -= qdisc_pkt_len(skb);
{
struct codel_sched_data *q = qdisc_priv(sch);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ q->limit = DEFAULT_CODEL_LIMIT;
+#else
sch->limit = DEFAULT_CODEL_LIMIT;
+#endif
codel_params_init(&q->params);
codel_vars_init(&q->vars);
return err;
}
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ if (q->limit >= 1)
+#else
if (sch->limit >= 1)
+#endif
sch->flags |= TCQ_F_CAN_BYPASS;
else
sch->flags &= ~TCQ_F_CAN_BYPASS;
if (nla_put_u32(skb, TCA_CODEL_TARGET,
codel_time_to_us(q->params.target)) ||
nla_put_u32(skb, TCA_CODEL_LIMIT,
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39))
+ q->limit) ||
+#else
sch->limit) ||
+#endif
nla_put_u32(skb, TCA_CODEL_INTERVAL,
codel_time_to_us(q->params.interval)) ||
nla_put_u32(skb, TCA_CODEL_ECN,
.enqueue = codel_qdisc_enqueue,
.dequeue = codel_qdisc_dequeue,
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28))
.peek = qdisc_peek_dequeued,
+#endif
.init = codel_init,
.reset = codel_reset,
.change = codel_change,
#include <net/sch_generic.h>
#include <linux/ethtool.h>
+struct qdisc_skb_cb {
+ unsigned int pkt_len;
+ char data[];
+};
+
+static inline struct qdisc_skb_cb *qdisc_skb_cb(struct sk_buff *skb)
+{
+ return (struct qdisc_skb_cb *)skb->cb;
+}
+
+static inline unsigned int qdisc_pkt_len(struct sk_buff *skb)
+{
+ return qdisc_skb_cb(skb)->pkt_len;
+}
+
#define PCI_PM_CAP_PME_SHIFT 11
/* I can't find a more suitable replacement... */
#include <net/iw_handler.h>
#include <linux/workqueue.h>
#include <net/genetlink.h>
+#include <net/sch_generic.h>
+
+#define TCQ_F_CAN_BYPASS 4
+
+static inline int qdisc_qlen(const struct Qdisc *q)
+{
+ return q->q.qlen;
+}
#define SDIO_VENDOR_ID_INTEL 0x0089
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
+#include <net/sch_generic.h>
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static inline void bstats_update(struct gnet_stats_basic_packed *bstats,
+ const struct sk_buff *skb)
+{
+ bstats->bytes += qdisc_pkt_len((struct sk_buff *) skb);
+ bstats->packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
+}
+static inline void qdisc_bstats_update(struct Qdisc *sch,
+ const struct sk_buff *skb)
+{
+ bstats_update(&sch->bstats, skb);
+}
+#else
+/*
+ * kernels <= 2.6.30 do not pass a const skb to qdisc_pkt_len, and
+ * gnet_stats_basic_packed did not exist (see c1a8f1f1c8)
+ */
+static inline void bstats_update(struct gnet_stats_basic *bstats,
+ struct sk_buff *skb)
+{
+ bstats->bytes += qdisc_pkt_len(skb);
+ bstats->packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
+}
+static inline void qdisc_bstats_update(struct Qdisc *sch,
+ struct sk_buff *skb)
+{
+ bstats_update(&sch->bstats, skb);
+}
+#endif
+
/* rename member in struct mmc_host in include/linux/mmc/host.h */
#define max_segs max_hw_segs
/* include to override NL80211_FEATURE_SK_TX_STATUS */
#include <linux/nl80211.h>
#include <linux/skbuff.h>
+#include <net/sch_generic.h>
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,37))
+static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
+{
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct qdisc_skb_cb) + sz);
+}
+#else
+static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
+{
+ /* XXX ? */
+}
+#endif
extern struct sk_buff *__pskb_copy(struct sk_buff *skb,
int headroom, gfp_t gfp_mask);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0))
+#include <linux/pkt_sched.h>
+
+/*
+ * This backports:
+ *
+ * From 76e3cc126bb223013a6b9a0e2a51238d1ef2e409 Mon Sep 17 00:00:00 2001
+ * From: Eric Dumazet <edumazet@google.com>
+ * Date: Thu, 10 May 2012 07:51:25 +0000
+ * Subject: [PATCH] codel: Controlled Delay AQM
+ */
+
+/* CODEL */
+
+enum {
+ TCA_CODEL_UNSPEC,
+ TCA_CODEL_TARGET,
+ TCA_CODEL_LIMIT,
+ TCA_CODEL_INTERVAL,
+ TCA_CODEL_ECN,
+ __TCA_CODEL_MAX
+};
+
+#define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1)
+
+struct tc_codel_xstats {
+ __u32 maxpacket; /* largest packet we've seen so far */
+ __u32 count; /* how many drops we've done since the last time we
+ * entered dropping state
+ */
+ __u32 lastcount; /* count at entry to dropping state */
+ __u32 ldelay; /* in-queue delay seen by most recently dequeued packet */
+ __s32 drop_next; /* time to drop next packet */
+ __u32 drop_overlimit; /* number of time max qdisc packet limit was hit */
+ __u32 ecn_mark; /* number of packets we ECN marked instead of dropped */
+ __u32 dropping; /* are we in dropping state ? */
+};
+
/* Backports tty_lock: Localise the lock */
#define tty_lock(__tty) tty_lock()
#define tty_unlock(__tty) tty_unlock()
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
+#include_next <net/codel.h>
+#else
+
#ifndef __NET_SCHED_CODEL_H
#define __NET_SCHED_CODEL_H
static struct codel_skb_cb *get_codel_cb(const struct sk_buff *skb)
{
qdisc_cb_private_validate(skb, sizeof(struct codel_skb_cb));
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37))
+ return (struct codel_skb_cb *)qdisc_skb_cb((struct sk_buff *) skb)->data;
+#else
return (struct codel_skb_cb *)qdisc_skb_cb(skb)->data;
+#endif
}
static codel_time_t codel_get_enqueue_time(const struct sk_buff *skb)
}
vars->ldelay = now - codel_get_enqueue_time(skb);
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37))
+ sch->qstats.backlog -= qdisc_pkt_len((struct sk_buff *)skb);
+#else
sch->qstats.backlog -= qdisc_pkt_len(skb);
+#endif
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,37))
+ if (unlikely(qdisc_pkt_len((struct sk_buff *)skb) > stats->maxpacket))
+ stats->maxpacket = qdisc_pkt_len((struct sk_buff *)skb);
+#else
if (unlikely(qdisc_pkt_len(skb) > stats->maxpacket))
stats->maxpacket = qdisc_pkt_len(skb);
+#endif
if (codel_time_before(vars->ldelay, params->target) ||
sch->qstats.backlog <= stats->maxpacket) {
return skb;
}
#endif
+#endif
if [[ ${CONFIG_COMPAT_KERNEL_2_6_36} = "y" ]]; then
echo "export CONFIG_COMPAT_KFIFO=y"
fi
+
+if [[ ${CONFIG_COMPAT_KERNEL_3_5} = "y" ]]; then
+ # We don't have 2.6.24 backport support yet for Codel
+ # For those who want to try this is what is required that I can tell
+ # so far:
+ # * struct Qdisc_ops
+ # - init and change callback ops use a different argument dataype
+ # - you need to parse data received from userspace differently
+ if [[ ${CONFIG_COMPAT_KERNEL_2_6_25} != "y" ]]; then
+ echo "export CONFIG_COMPAT_NET_SCH_CODEL=m"
+ fi
+fi