From f1b306ec5ff4303b185b2f4df0503d2c1e9e25a0 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 24 May 2012 17:33:39 -0700 Subject: [PATCH] compat: backport CoDel support down to 2.6.25 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 --- compat/Makefile | 1 + compat/sch_codel.c | 32 +++++++++++++++++++++++++++++- include/linux/compat-2.6.27.h | 15 ++++++++++++++ include/linux/compat-2.6.32.h | 8 ++++++++ include/linux/compat-2.6.38.h | 32 ++++++++++++++++++++++++++++++ include/linux/compat-3.3.h | 13 ++++++++++++ include/linux/compat-3.5.h | 37 +++++++++++++++++++++++++++++++++++ include/net/codel.h | 20 +++++++++++++++++++ scripts/gen-compat-config.sh | 12 ++++++++++++ 9 files changed, 169 insertions(+), 1 deletion(-) diff --git a/compat/Makefile b/compat/Makefile index 667e727..8397714 100644 --- a/compat/Makefile +++ b/compat/Makefile @@ -2,6 +2,7 @@ obj-m += compat.o #compat-objs := obj-$(CONFIG_COMPAT_FIRMWARE_CLASS) += compat_firmware_class.o +obj-$(CONFIG_COMPAT_NET_SCH_CODEL) += sch_codel.o compat-y += main.o diff --git a/compat/sch_codel.c b/compat/sch_codel.c index 2f9ab17..5ad66fb 100644 --- a/compat/sch_codel.c +++ b/compat/sch_codel.c @@ -58,6 +58,9 @@ struct codel_sched_data { 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() @@ -95,11 +98,16 @@ static int codel_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) { 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); } @@ -140,13 +148,21 @@ static int codel_change(struct Qdisc *sch, struct nlattr *opt) } 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); @@ -162,7 +178,11 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt) { 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); @@ -175,7 +195,11 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt) 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; @@ -195,7 +219,11 @@ static int codel_dump(struct Qdisc *sch, struct sk_buff *skb) 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, @@ -248,7 +276,9 @@ static struct Qdisc_ops codel_qdisc_ops __read_mostly = { .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, diff --git a/include/linux/compat-2.6.27.h b/include/linux/compat-2.6.27.h index 1e2af80..106e7ee 100644 --- a/include/linux/compat-2.6.27.h +++ b/include/linux/compat-2.6.27.h @@ -22,6 +22,21 @@ #include #include +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... */ diff --git a/include/linux/compat-2.6.32.h b/include/linux/compat-2.6.32.h index b0c699a..9d41fc8 100644 --- a/include/linux/compat-2.6.32.h +++ b/include/linux/compat-2.6.32.h @@ -10,6 +10,14 @@ #include #include #include +#include + +#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 diff --git a/include/linux/compat-2.6.38.h b/include/linux/compat-2.6.38.h index 63f9dd6..0a86468 100644 --- a/include/linux/compat-2.6.38.h +++ b/include/linux/compat-2.6.38.h @@ -8,6 +8,38 @@ #include #include #include +#include + +#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 diff --git a/include/linux/compat-3.3.h b/include/linux/compat-3.3.h index 69bdcd0..1eb873d 100644 --- a/include/linux/compat-3.3.h +++ b/include/linux/compat-3.3.h @@ -8,6 +8,19 @@ /* include to override NL80211_FEATURE_SK_TX_STATUS */ #include #include +#include + +#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); diff --git a/include/linux/compat-3.5.h b/include/linux/compat-3.5.h index 0bb35bb..c6d478e 100644 --- a/include/linux/compat-3.5.h +++ b/include/linux/compat-3.5.h @@ -8,6 +8,43 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)) +#include + +/* + * This backports: + * + * From 76e3cc126bb223013a6b9a0e2a51238d1ef2e409 Mon Sep 17 00:00:00 2001 + * From: Eric Dumazet + * 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() diff --git a/include/net/codel.h b/include/net/codel.h index 550debf..128082e 100644 --- a/include/net/codel.h +++ b/include/net/codel.h @@ -1,3 +1,9 @@ +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)) +#include_next +#else + #ifndef __NET_SCHED_CODEL_H #define __NET_SCHED_CODEL_H @@ -85,7 +91,11 @@ struct codel_skb_cb { 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) @@ -219,10 +229,19 @@ static bool codel_should_drop(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) { @@ -340,3 +359,4 @@ end: return skb; } #endif +#endif diff --git a/scripts/gen-compat-config.sh b/scripts/gen-compat-config.sh index f56cbfc..be90fdb 100755 --- a/scripts/gen-compat-config.sh +++ b/scripts/gen-compat-config.sh @@ -62,3 +62,15 @@ fi 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 -- 2.41.0