Browse Source

kernel: backport patches improving fq_codel drop behavior

Signed-off-by: Felix Fietkau <[email protected]>
Felix Fietkau 9 years ago
parent
commit
fad8bdfa40

+ 237 - 0
target/linux/generic/patches-4.4/030-net_sched-introduce-qdisc_replace-helper.patch

@@ -0,0 +1,237 @@
+From: WANG Cong <[email protected]>
+Date: Thu, 25 Feb 2016 14:55:00 -0800
+Subject: [PATCH] net_sched: introduce qdisc_replace() helper
+
+Remove nearly duplicated code and prepare for the following patch.
+
+Cc: Jamal Hadi Salim <[email protected]>
+Acked-by: Jamal Hadi Salim <[email protected]>
+Signed-off-by: Cong Wang <[email protected]>
+Signed-off-by: David S. Miller <[email protected]>
+---
+
+--- a/include/net/sch_generic.h
++++ b/include/net/sch_generic.h
+@@ -698,6 +698,23 @@ static inline void qdisc_reset_queue(str
+ 	sch->qstats.backlog = 0;
+ }
+ 
++static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new,
++					  struct Qdisc **pold)
++{
++	struct Qdisc *old;
++
++	sch_tree_lock(sch);
++	old = *pold;
++	*pold = new;
++	if (old != NULL) {
++		qdisc_tree_decrease_qlen(old, old->q.qlen);
++		qdisc_reset(old);
++	}
++	sch_tree_unlock(sch);
++
++	return old;
++}
++
+ static inline unsigned int __qdisc_queue_drop(struct Qdisc *sch,
+ 					      struct sk_buff_head *list)
+ {
+--- a/net/sched/sch_cbq.c
++++ b/net/sched/sch_cbq.c
+@@ -1624,13 +1624,8 @@ static int cbq_graft(struct Qdisc *sch,
+ 			new->reshape_fail = cbq_reshape_fail;
+ #endif
+ 	}
+-	sch_tree_lock(sch);
+-	*old = cl->q;
+-	cl->q = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
+ 
++	*old = qdisc_replace(sch, new, &cl->q);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_drr.c
++++ b/net/sched/sch_drr.c
+@@ -226,11 +226,7 @@ static int drr_graft_class(struct Qdisc
+ 			new = &noop_qdisc;
+ 	}
+ 
+-	sch_tree_lock(sch);
+-	drr_purge_queue(cl);
+-	*old = cl->qdisc;
+-	cl->qdisc = new;
+-	sch_tree_unlock(sch);
++	*old = qdisc_replace(sch, new, &cl->qdisc);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_dsmark.c
++++ b/net/sched/sch_dsmark.c
+@@ -73,13 +73,7 @@ static int dsmark_graft(struct Qdisc *sc
+ 			new = &noop_qdisc;
+ 	}
+ 
+-	sch_tree_lock(sch);
+-	*old = p->q;
+-	p->q = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
+-
++	*old = qdisc_replace(sch, new, &p->q);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_hfsc.c
++++ b/net/sched/sch_hfsc.c
+@@ -1215,11 +1215,7 @@ hfsc_graft_class(struct Qdisc *sch, unsi
+ 			new = &noop_qdisc;
+ 	}
+ 
+-	sch_tree_lock(sch);
+-	hfsc_purge_queue(sch, cl);
+-	*old = cl->qdisc;
+-	cl->qdisc = new;
+-	sch_tree_unlock(sch);
++	*old = qdisc_replace(sch, new, &cl->qdisc);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_htb.c
++++ b/net/sched/sch_htb.c
+@@ -1163,14 +1163,7 @@ static int htb_graft(struct Qdisc *sch,
+ 				     cl->common.classid)) == NULL)
+ 		return -ENOBUFS;
+ 
+-	sch_tree_lock(sch);
+-	*old = cl->un.leaf.q;
+-	cl->un.leaf.q = new;
+-	if (*old != NULL) {
+-		qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-		qdisc_reset(*old);
+-	}
+-	sch_tree_unlock(sch);
++	*old = qdisc_replace(sch, new, &cl->un.leaf.q);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_multiq.c
++++ b/net/sched/sch_multiq.c
+@@ -303,13 +303,7 @@ static int multiq_graft(struct Qdisc *sc
+ 	if (new == NULL)
+ 		new = &noop_qdisc;
+ 
+-	sch_tree_lock(sch);
+-	*old = q->queues[band];
+-	q->queues[band] = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
+-
++	*old = qdisc_replace(sch, new, &q->queues[band]);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_netem.c
++++ b/net/sched/sch_netem.c
+@@ -1037,15 +1037,7 @@ static int netem_graft(struct Qdisc *sch
+ {
+ 	struct netem_sched_data *q = qdisc_priv(sch);
+ 
+-	sch_tree_lock(sch);
+-	*old = q->qdisc;
+-	q->qdisc = new;
+-	if (*old) {
+-		qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-		qdisc_reset(*old);
+-	}
+-	sch_tree_unlock(sch);
+-
++	*old = qdisc_replace(sch, new, &q->qdisc);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_prio.c
++++ b/net/sched/sch_prio.c
+@@ -268,13 +268,7 @@ static int prio_graft(struct Qdisc *sch,
+ 	if (new == NULL)
+ 		new = &noop_qdisc;
+ 
+-	sch_tree_lock(sch);
+-	*old = q->queues[band];
+-	q->queues[band] = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
+-
++	*old = qdisc_replace(sch, new, &q->queues[band]);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_qfq.c
++++ b/net/sched/sch_qfq.c
+@@ -617,11 +617,7 @@ static int qfq_graft_class(struct Qdisc
+ 			new = &noop_qdisc;
+ 	}
+ 
+-	sch_tree_lock(sch);
+-	qfq_purge_queue(cl);
+-	*old = cl->qdisc;
+-	cl->qdisc = new;
+-	sch_tree_unlock(sch);
++	*old = qdisc_replace(sch, new, &cl->qdisc);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_red.c
++++ b/net/sched/sch_red.c
+@@ -313,12 +313,7 @@ static int red_graft(struct Qdisc *sch,
+ 	if (new == NULL)
+ 		new = &noop_qdisc;
+ 
+-	sch_tree_lock(sch);
+-	*old = q->qdisc;
+-	q->qdisc = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
++	*old = qdisc_replace(sch, new, &q->qdisc);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_sfb.c
++++ b/net/sched/sch_sfb.c
+@@ -606,12 +606,7 @@ static int sfb_graft(struct Qdisc *sch,
+ 	if (new == NULL)
+ 		new = &noop_qdisc;
+ 
+-	sch_tree_lock(sch);
+-	*old = q->qdisc;
+-	q->qdisc = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
++	*old = qdisc_replace(sch, new, &q->qdisc);
+ 	return 0;
+ }
+ 
+--- a/net/sched/sch_tbf.c
++++ b/net/sched/sch_tbf.c
+@@ -502,13 +502,7 @@ static int tbf_graft(struct Qdisc *sch,
+ 	if (new == NULL)
+ 		new = &noop_qdisc;
+ 
+-	sch_tree_lock(sch);
+-	*old = q->qdisc;
+-	q->qdisc = new;
+-	qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
+-	qdisc_reset(*old);
+-	sch_tree_unlock(sch);
+-
++	*old = qdisc_replace(sch, new, &q->qdisc);
+ 	return 0;
+ }
+ 

+ 647 - 0
target/linux/generic/patches-4.4/031-net_sched-update-hierarchical-backlog-too.patch

@@ -0,0 +1,647 @@
+From: WANG Cong <[email protected]>
+Date: Thu, 25 Feb 2016 14:55:01 -0800
+Subject: [PATCH] net_sched: update hierarchical backlog too
+
+When the bottom qdisc decides to, for example, drop some packet,
+it calls qdisc_tree_decrease_qlen() to update the queue length
+for all its ancestors, we need to update the backlog too to
+keep the stats on root qdisc accurate.
+
+Cc: Jamal Hadi Salim <[email protected]>
+Acked-by: Jamal Hadi Salim <[email protected]>
+Signed-off-by: Cong Wang <[email protected]>
+Signed-off-by: David S. Miller <[email protected]>
+---
+
+--- a/include/net/codel.h
++++ b/include/net/codel.h
+@@ -162,12 +162,14 @@ struct codel_vars {
+  * struct codel_stats - contains codel shared variables and stats
+  * @maxpacket:	largest packet we've seen so far
+  * @drop_count:	temp count of dropped packets in dequeue()
++ * @drop_len:	bytes of dropped packets in dequeue()
+  * ecn_mark:	number of packets we ECN marked instead of dropping
+  * ce_mark:	number of packets CE marked because sojourn time was above ce_threshold
+  */
+ struct codel_stats {
+ 	u32		maxpacket;
+ 	u32		drop_count;
++	u32		drop_len;
+ 	u32		ecn_mark;
+ 	u32		ce_mark;
+ };
+@@ -308,6 +310,7 @@ static struct sk_buff *codel_dequeue(str
+ 								  vars->rec_inv_sqrt);
+ 					goto end;
+ 				}
++				stats->drop_len += qdisc_pkt_len(skb);
+ 				qdisc_drop(skb, sch);
+ 				stats->drop_count++;
+ 				skb = dequeue_func(vars, sch);
+@@ -330,6 +333,7 @@ static struct sk_buff *codel_dequeue(str
+ 		if (params->ecn && INET_ECN_set_ce(skb)) {
+ 			stats->ecn_mark++;
+ 		} else {
++			stats->drop_len += qdisc_pkt_len(skb);
+ 			qdisc_drop(skb, sch);
+ 			stats->drop_count++;
+ 
+--- a/include/net/sch_generic.h
++++ b/include/net/sch_generic.h
+@@ -396,7 +396,8 @@ struct Qdisc *dev_graft_qdisc(struct net
+ 			      struct Qdisc *qdisc);
+ void qdisc_reset(struct Qdisc *qdisc);
+ void qdisc_destroy(struct Qdisc *qdisc);
+-void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n);
++void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, unsigned int n,
++			       unsigned int len);
+ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
+ 			  const struct Qdisc_ops *ops);
+ struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
+@@ -707,7 +708,7 @@ static inline struct Qdisc *qdisc_replac
+ 	old = *pold;
+ 	*pold = new;
+ 	if (old != NULL) {
+-		qdisc_tree_decrease_qlen(old, old->q.qlen);
++		qdisc_tree_reduce_backlog(old, old->q.qlen, old->qstats.backlog);
+ 		qdisc_reset(old);
+ 	}
+ 	sch_tree_unlock(sch);
+--- a/net/sched/sch_api.c
++++ b/net/sched/sch_api.c
+@@ -744,14 +744,15 @@ static u32 qdisc_alloc_handle(struct net
+ 	return 0;
+ }
+ 
+-void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
++void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n,
++			       unsigned int len)
+ {
+ 	const struct Qdisc_class_ops *cops;
+ 	unsigned long cl;
+ 	u32 parentid;
+ 	int drops;
+ 
+-	if (n == 0)
++	if (n == 0 && len == 0)
+ 		return;
+ 	drops = max_t(int, n, 0);
+ 	rcu_read_lock();
+@@ -774,11 +775,12 @@ void qdisc_tree_decrease_qlen(struct Qdi
+ 			cops->put(sch, cl);
+ 		}
+ 		sch->q.qlen -= n;
++		sch->qstats.backlog -= len;
+ 		__qdisc_qstats_drop(sch, drops);
+ 	}
+ 	rcu_read_unlock();
+ }
+-EXPORT_SYMBOL(qdisc_tree_decrease_qlen);
++EXPORT_SYMBOL(qdisc_tree_reduce_backlog);
+ 
+ static void notify_and_destroy(struct net *net, struct sk_buff *skb,
+ 			       struct nlmsghdr *n, u32 clid,
+--- a/net/sched/sch_cbq.c
++++ b/net/sched/sch_cbq.c
+@@ -1909,7 +1909,7 @@ static int cbq_delete(struct Qdisc *sch,
+ {
+ 	struct cbq_sched_data *q = qdisc_priv(sch);
+ 	struct cbq_class *cl = (struct cbq_class *)arg;
+-	unsigned int qlen;
++	unsigned int qlen, backlog;
+ 
+ 	if (cl->filters || cl->children || cl == &q->link)
+ 		return -EBUSY;
+@@ -1917,8 +1917,9 @@ static int cbq_delete(struct Qdisc *sch,
+ 	sch_tree_lock(sch);
+ 
+ 	qlen = cl->q->q.qlen;
++	backlog = cl->q->qstats.backlog;
+ 	qdisc_reset(cl->q);
+-	qdisc_tree_decrease_qlen(cl->q, qlen);
++	qdisc_tree_reduce_backlog(cl->q, qlen, backlog);
+ 
+ 	if (cl->next_alive)
+ 		cbq_deactivate_class(cl);
+--- a/net/sched/sch_choke.c
++++ b/net/sched/sch_choke.c
+@@ -128,8 +128,8 @@ static void choke_drop_by_idx(struct Qdi
+ 		choke_zap_tail_holes(q);
+ 
+ 	qdisc_qstats_backlog_dec(sch, skb);
++	qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb));
+ 	qdisc_drop(skb, sch);
+-	qdisc_tree_decrease_qlen(sch, 1);
+ 	--sch->q.qlen;
+ }
+ 
+@@ -456,6 +456,7 @@ static int choke_change(struct Qdisc *sc
+ 		old = q->tab;
+ 		if (old) {
+ 			unsigned int oqlen = sch->q.qlen, tail = 0;
++			unsigned dropped = 0;
+ 
+ 			while (q->head != q->tail) {
+ 				struct sk_buff *skb = q->tab[q->head];
+@@ -467,11 +468,12 @@ static int choke_change(struct Qdisc *sc
+ 					ntab[tail++] = skb;
+ 					continue;
+ 				}
++				dropped += qdisc_pkt_len(skb);
+ 				qdisc_qstats_backlog_dec(sch, skb);
+ 				--sch->q.qlen;
+ 				qdisc_drop(skb, sch);
+ 			}
+-			qdisc_tree_decrease_qlen(sch, oqlen - sch->q.qlen);
++			qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped);
+ 			q->head = 0;
+ 			q->tail = tail;
+ 		}
+--- a/net/sched/sch_codel.c
++++ b/net/sched/sch_codel.c
+@@ -79,12 +79,13 @@ static struct sk_buff *codel_qdisc_deque
+ 
+ 	skb = codel_dequeue(sch, &q->params, &q->vars, &q->stats, dequeue);
+ 
+-	/* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
++	/* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+ 	 * or HTB crashes. Defer it for next round.
+ 	 */
+ 	if (q->stats.drop_count && sch->q.qlen) {
+-		qdisc_tree_decrease_qlen(sch, q->stats.drop_count);
++		qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
+ 		q->stats.drop_count = 0;
++		q->stats.drop_len = 0;
+ 	}
+ 	if (skb)
+ 		qdisc_bstats_update(sch, skb);
+@@ -116,7 +117,7 @@ static int codel_change(struct Qdisc *sc
+ {
+ 	struct codel_sched_data *q = qdisc_priv(sch);
+ 	struct nlattr *tb[TCA_CODEL_MAX + 1];
+-	unsigned int qlen;
++	unsigned int qlen, dropped = 0;
+ 	int err;
+ 
+ 	if (!opt)
+@@ -156,10 +157,11 @@ static int codel_change(struct Qdisc *sc
+ 	while (sch->q.qlen > sch->limit) {
+ 		struct sk_buff *skb = __skb_dequeue(&sch->q);
+ 
++		dropped += qdisc_pkt_len(skb);
+ 		qdisc_qstats_backlog_dec(sch, skb);
+ 		qdisc_drop(skb, sch);
+ 	}
+-	qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
++	qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
+ 
+ 	sch_tree_unlock(sch);
+ 	return 0;
+--- a/net/sched/sch_drr.c
++++ b/net/sched/sch_drr.c
+@@ -53,9 +53,10 @@ static struct drr_class *drr_find_class(
+ static void drr_purge_queue(struct drr_class *cl)
+ {
+ 	unsigned int len = cl->qdisc->q.qlen;
++	unsigned int backlog = cl->qdisc->qstats.backlog;
+ 
+ 	qdisc_reset(cl->qdisc);
+-	qdisc_tree_decrease_qlen(cl->qdisc, len);
++	qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
+ }
+ 
+ static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
+--- a/net/sched/sch_fq.c
++++ b/net/sched/sch_fq.c
+@@ -662,6 +662,7 @@ static int fq_change(struct Qdisc *sch,
+ 	struct fq_sched_data *q = qdisc_priv(sch);
+ 	struct nlattr *tb[TCA_FQ_MAX + 1];
+ 	int err, drop_count = 0;
++	unsigned drop_len = 0;
+ 	u32 fq_log;
+ 
+ 	if (!opt)
+@@ -736,10 +737,11 @@ static int fq_change(struct Qdisc *sch,
+ 
+ 		if (!skb)
+ 			break;
++		drop_len += qdisc_pkt_len(skb);
+ 		kfree_skb(skb);
+ 		drop_count++;
+ 	}
+-	qdisc_tree_decrease_qlen(sch, drop_count);
++	qdisc_tree_reduce_backlog(sch, drop_count, drop_len);
+ 
+ 	sch_tree_unlock(sch);
+ 	return err;
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -175,7 +175,7 @@ static unsigned int fq_codel_qdisc_drop(
+ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+ {
+ 	struct fq_codel_sched_data *q = qdisc_priv(sch);
+-	unsigned int idx;
++	unsigned int idx, prev_backlog;
+ 	struct fq_codel_flow *flow;
+ 	int uninitialized_var(ret);
+ 
+@@ -203,6 +203,7 @@ static int fq_codel_enqueue(struct sk_bu
+ 	if (++sch->q.qlen <= sch->limit)
+ 		return NET_XMIT_SUCCESS;
+ 
++	prev_backlog = sch->qstats.backlog;
+ 	q->drop_overlimit++;
+ 	/* Return Congestion Notification only if we dropped a packet
+ 	 * from this flow.
+@@ -211,7 +212,7 @@ static int fq_codel_enqueue(struct sk_bu
+ 		return NET_XMIT_CN;
+ 
+ 	/* As we dropped a packet, better let upper stack know this */
+-	qdisc_tree_decrease_qlen(sch, 1);
++	qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog);
+ 	return NET_XMIT_SUCCESS;
+ }
+ 
+@@ -241,6 +242,7 @@ static struct sk_buff *fq_codel_dequeue(
+ 	struct fq_codel_flow *flow;
+ 	struct list_head *head;
+ 	u32 prev_drop_count, prev_ecn_mark;
++	unsigned int prev_backlog;
+ 
+ begin:
+ 	head = &q->new_flows;
+@@ -259,6 +261,7 @@ begin:
+ 
+ 	prev_drop_count = q->cstats.drop_count;
+ 	prev_ecn_mark = q->cstats.ecn_mark;
++	prev_backlog = sch->qstats.backlog;
+ 
+ 	skb = codel_dequeue(sch, &q->cparams, &flow->cvars, &q->cstats,
+ 			    dequeue);
+@@ -276,12 +279,14 @@ begin:
+ 	}
+ 	qdisc_bstats_update(sch, skb);
+ 	flow->deficit -= qdisc_pkt_len(skb);
+-	/* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
++	/* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
+ 	 * or HTB crashes. Defer it for next round.
+ 	 */
+ 	if (q->cstats.drop_count && sch->q.qlen) {
+-		qdisc_tree_decrease_qlen(sch, q->cstats.drop_count);
++		qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
++					  q->cstats.drop_len);
+ 		q->cstats.drop_count = 0;
++		q->cstats.drop_len = 0;
+ 	}
+ 	return skb;
+ }
+@@ -372,11 +377,13 @@ static int fq_codel_change(struct Qdisc
+ 	while (sch->q.qlen > sch->limit) {
+ 		struct sk_buff *skb = fq_codel_dequeue(sch);
+ 
++		q->cstats.drop_len += qdisc_pkt_len(skb);
+ 		kfree_skb(skb);
+ 		q->cstats.drop_count++;
+ 	}
+-	qdisc_tree_decrease_qlen(sch, q->cstats.drop_count);
++	qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len);
+ 	q->cstats.drop_count = 0;
++	q->cstats.drop_len = 0;
+ 
+ 	sch_tree_unlock(sch);
+ 	return 0;
+--- a/net/sched/sch_hfsc.c
++++ b/net/sched/sch_hfsc.c
+@@ -895,9 +895,10 @@ static void
+ hfsc_purge_queue(struct Qdisc *sch, struct hfsc_class *cl)
+ {
+ 	unsigned int len = cl->qdisc->q.qlen;
++	unsigned int backlog = cl->qdisc->qstats.backlog;
+ 
+ 	qdisc_reset(cl->qdisc);
+-	qdisc_tree_decrease_qlen(cl->qdisc, len);
++	qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
+ }
+ 
+ static void
+--- a/net/sched/sch_hhf.c
++++ b/net/sched/sch_hhf.c
+@@ -382,6 +382,7 @@ static int hhf_enqueue(struct sk_buff *s
+ 	struct hhf_sched_data *q = qdisc_priv(sch);
+ 	enum wdrr_bucket_idx idx;
+ 	struct wdrr_bucket *bucket;
++	unsigned int prev_backlog;
+ 
+ 	idx = hhf_classify(skb, sch);
+ 
+@@ -409,6 +410,7 @@ static int hhf_enqueue(struct sk_buff *s
+ 	if (++sch->q.qlen <= sch->limit)
+ 		return NET_XMIT_SUCCESS;
+ 
++	prev_backlog = sch->qstats.backlog;
+ 	q->drop_overlimit++;
+ 	/* Return Congestion Notification only if we dropped a packet from this
+ 	 * bucket.
+@@ -417,7 +419,7 @@ static int hhf_enqueue(struct sk_buff *s
+ 		return NET_XMIT_CN;
+ 
+ 	/* As we dropped a packet, better let upper stack know this. */
+-	qdisc_tree_decrease_qlen(sch, 1);
++	qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog);
+ 	return NET_XMIT_SUCCESS;
+ }
+ 
+@@ -527,7 +529,7 @@ static int hhf_change(struct Qdisc *sch,
+ {
+ 	struct hhf_sched_data *q = qdisc_priv(sch);
+ 	struct nlattr *tb[TCA_HHF_MAX + 1];
+-	unsigned int qlen;
++	unsigned int qlen, prev_backlog;
+ 	int err;
+ 	u64 non_hh_quantum;
+ 	u32 new_quantum = q->quantum;
+@@ -577,12 +579,14 @@ static int hhf_change(struct Qdisc *sch,
+ 	}
+ 
+ 	qlen = sch->q.qlen;
++	prev_backlog = sch->qstats.backlog;
+ 	while (sch->q.qlen > sch->limit) {
+ 		struct sk_buff *skb = hhf_dequeue(sch);
+ 
+ 		kfree_skb(skb);
+ 	}
+-	qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
++	qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen,
++				  prev_backlog - sch->qstats.backlog);
+ 
+ 	sch_tree_unlock(sch);
+ 	return 0;
+--- a/net/sched/sch_htb.c
++++ b/net/sched/sch_htb.c
+@@ -1265,7 +1265,6 @@ static int htb_delete(struct Qdisc *sch,
+ {
+ 	struct htb_sched *q = qdisc_priv(sch);
+ 	struct htb_class *cl = (struct htb_class *)arg;
+-	unsigned int qlen;
+ 	struct Qdisc *new_q = NULL;
+ 	int last_child = 0;
+ 
+@@ -1285,9 +1284,11 @@ static int htb_delete(struct Qdisc *sch,
+ 	sch_tree_lock(sch);
+ 
+ 	if (!cl->level) {
+-		qlen = cl->un.leaf.q->q.qlen;
++		unsigned int qlen = cl->un.leaf.q->q.qlen;
++		unsigned int backlog = cl->un.leaf.q->qstats.backlog;
++
+ 		qdisc_reset(cl->un.leaf.q);
+-		qdisc_tree_decrease_qlen(cl->un.leaf.q, qlen);
++		qdisc_tree_reduce_backlog(cl->un.leaf.q, qlen, backlog);
+ 	}
+ 
+ 	/* delete from hash and active; remainder in destroy_class */
+@@ -1421,10 +1422,11 @@ static int htb_change_class(struct Qdisc
+ 		sch_tree_lock(sch);
+ 		if (parent && !parent->level) {
+ 			unsigned int qlen = parent->un.leaf.q->q.qlen;
++			unsigned int backlog = parent->un.leaf.q->qstats.backlog;
+ 
+ 			/* turn parent into inner node */
+ 			qdisc_reset(parent->un.leaf.q);
+-			qdisc_tree_decrease_qlen(parent->un.leaf.q, qlen);
++			qdisc_tree_reduce_backlog(parent->un.leaf.q, qlen, backlog);
+ 			qdisc_destroy(parent->un.leaf.q);
+ 			if (parent->prio_activity)
+ 				htb_deactivate(q, parent);
+--- a/net/sched/sch_multiq.c
++++ b/net/sched/sch_multiq.c
+@@ -218,7 +218,8 @@ static int multiq_tune(struct Qdisc *sch
+ 		if (q->queues[i] != &noop_qdisc) {
+ 			struct Qdisc *child = q->queues[i];
+ 			q->queues[i] = &noop_qdisc;
+-			qdisc_tree_decrease_qlen(child, child->q.qlen);
++			qdisc_tree_reduce_backlog(child, child->q.qlen,
++						  child->qstats.backlog);
+ 			qdisc_destroy(child);
+ 		}
+ 	}
+@@ -238,8 +239,9 @@ static int multiq_tune(struct Qdisc *sch
+ 				q->queues[i] = child;
+ 
+ 				if (old != &noop_qdisc) {
+-					qdisc_tree_decrease_qlen(old,
+-								 old->q.qlen);
++					qdisc_tree_reduce_backlog(old,
++								  old->q.qlen,
++								  old->qstats.backlog);
+ 					qdisc_destroy(old);
+ 				}
+ 				sch_tree_unlock(sch);
+--- a/net/sched/sch_netem.c
++++ b/net/sched/sch_netem.c
+@@ -598,7 +598,8 @@ deliver:
+ 				if (unlikely(err != NET_XMIT_SUCCESS)) {
+ 					if (net_xmit_drop_count(err)) {
+ 						qdisc_qstats_drop(sch);
+-						qdisc_tree_decrease_qlen(sch, 1);
++						qdisc_tree_reduce_backlog(sch, 1,
++									  qdisc_pkt_len(skb));
+ 					}
+ 				}
+ 				goto tfifo_dequeue;
+--- a/net/sched/sch_pie.c
++++ b/net/sched/sch_pie.c
+@@ -183,7 +183,7 @@ static int pie_change(struct Qdisc *sch,
+ {
+ 	struct pie_sched_data *q = qdisc_priv(sch);
+ 	struct nlattr *tb[TCA_PIE_MAX + 1];
+-	unsigned int qlen;
++	unsigned int qlen, dropped = 0;
+ 	int err;
+ 
+ 	if (!opt)
+@@ -232,10 +232,11 @@ static int pie_change(struct Qdisc *sch,
+ 	while (sch->q.qlen > sch->limit) {
+ 		struct sk_buff *skb = __skb_dequeue(&sch->q);
+ 
++		dropped += qdisc_pkt_len(skb);
+ 		qdisc_qstats_backlog_dec(sch, skb);
+ 		qdisc_drop(skb, sch);
+ 	}
+-	qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
++	qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
+ 
+ 	sch_tree_unlock(sch);
+ 	return 0;
+--- a/net/sched/sch_prio.c
++++ b/net/sched/sch_prio.c
+@@ -191,7 +191,7 @@ static int prio_tune(struct Qdisc *sch,
+ 		struct Qdisc *child = q->queues[i];
+ 		q->queues[i] = &noop_qdisc;
+ 		if (child != &noop_qdisc) {
+-			qdisc_tree_decrease_qlen(child, child->q.qlen);
++			qdisc_tree_reduce_backlog(child, child->q.qlen, child->qstats.backlog);
+ 			qdisc_destroy(child);
+ 		}
+ 	}
+@@ -210,8 +210,9 @@ static int prio_tune(struct Qdisc *sch,
+ 				q->queues[i] = child;
+ 
+ 				if (old != &noop_qdisc) {
+-					qdisc_tree_decrease_qlen(old,
+-								 old->q.qlen);
++					qdisc_tree_reduce_backlog(old,
++								  old->q.qlen,
++								  old->qstats.backlog);
+ 					qdisc_destroy(old);
+ 				}
+ 				sch_tree_unlock(sch);
+--- a/net/sched/sch_qfq.c
++++ b/net/sched/sch_qfq.c
+@@ -220,9 +220,10 @@ static struct qfq_class *qfq_find_class(
+ static void qfq_purge_queue(struct qfq_class *cl)
+ {
+ 	unsigned int len = cl->qdisc->q.qlen;
++	unsigned int backlog = cl->qdisc->qstats.backlog;
+ 
+ 	qdisc_reset(cl->qdisc);
+-	qdisc_tree_decrease_qlen(cl->qdisc, len);
++	qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
+ }
+ 
+ static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
+--- a/net/sched/sch_red.c
++++ b/net/sched/sch_red.c
+@@ -210,7 +210,8 @@ static int red_change(struct Qdisc *sch,
+ 	q->flags = ctl->flags;
+ 	q->limit = ctl->limit;
+ 	if (child) {
+-		qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
++		qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
++					  q->qdisc->qstats.backlog);
+ 		qdisc_destroy(q->qdisc);
+ 		q->qdisc = child;
+ 	}
+--- a/net/sched/sch_sfb.c
++++ b/net/sched/sch_sfb.c
+@@ -510,7 +510,8 @@ static int sfb_change(struct Qdisc *sch,
+ 
+ 	sch_tree_lock(sch);
+ 
+-	qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
++	qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
++				  q->qdisc->qstats.backlog);
+ 	qdisc_destroy(q->qdisc);
+ 	q->qdisc = child;
+ 
+--- a/net/sched/sch_sfq.c
++++ b/net/sched/sch_sfq.c
+@@ -346,7 +346,7 @@ static int
+ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+ {
+ 	struct sfq_sched_data *q = qdisc_priv(sch);
+-	unsigned int hash;
++	unsigned int hash, dropped;
+ 	sfq_index x, qlen;
+ 	struct sfq_slot *slot;
+ 	int uninitialized_var(ret);
+@@ -461,7 +461,7 @@ enqueue:
+ 		return NET_XMIT_SUCCESS;
+ 
+ 	qlen = slot->qlen;
+-	sfq_drop(sch);
++	dropped = sfq_drop(sch);
+ 	/* Return Congestion Notification only if we dropped a packet
+ 	 * from this flow.
+ 	 */
+@@ -469,7 +469,7 @@ enqueue:
+ 		return NET_XMIT_CN;
+ 
+ 	/* As we dropped a packet, better let upper stack know this */
+-	qdisc_tree_decrease_qlen(sch, 1);
++	qdisc_tree_reduce_backlog(sch, 1, dropped);
+ 	return NET_XMIT_SUCCESS;
+ }
+ 
+@@ -537,6 +537,7 @@ static void sfq_rehash(struct Qdisc *sch
+ 	struct sfq_slot *slot;
+ 	struct sk_buff_head list;
+ 	int dropped = 0;
++	unsigned int drop_len = 0;
+ 
+ 	__skb_queue_head_init(&list);
+ 
+@@ -565,6 +566,7 @@ static void sfq_rehash(struct Qdisc *sch
+ 			if (x >= SFQ_MAX_FLOWS) {
+ drop:
+ 				qdisc_qstats_backlog_dec(sch, skb);
++				drop_len += qdisc_pkt_len(skb);
+ 				kfree_skb(skb);
+ 				dropped++;
+ 				continue;
+@@ -594,7 +596,7 @@ drop:
+ 		}
+ 	}
+ 	sch->q.qlen -= dropped;
+-	qdisc_tree_decrease_qlen(sch, dropped);
++	qdisc_tree_reduce_backlog(sch, dropped, drop_len);
+ }
+ 
+ static void sfq_perturbation(unsigned long arg)
+@@ -618,7 +620,7 @@ static int sfq_change(struct Qdisc *sch,
+ 	struct sfq_sched_data *q = qdisc_priv(sch);
+ 	struct tc_sfq_qopt *ctl = nla_data(opt);
+ 	struct tc_sfq_qopt_v1 *ctl_v1 = NULL;
+-	unsigned int qlen;
++	unsigned int qlen, dropped = 0;
+ 	struct red_parms *p = NULL;
+ 
+ 	if (opt->nla_len < nla_attr_size(sizeof(*ctl)))
+@@ -667,8 +669,8 @@ static int sfq_change(struct Qdisc *sch,
+ 
+ 	qlen = sch->q.qlen;
+ 	while (sch->q.qlen > q->limit)
+-		sfq_drop(sch);
+-	qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
++		dropped += sfq_drop(sch);
++	qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
+ 
+ 	del_timer(&q->perturb_timer);
+ 	if (q->perturb_period) {
+--- a/net/sched/sch_tbf.c
++++ b/net/sched/sch_tbf.c
+@@ -160,6 +160,7 @@ static int tbf_segment(struct sk_buff *s
+ 	struct tbf_sched_data *q = qdisc_priv(sch);
+ 	struct sk_buff *segs, *nskb;
+ 	netdev_features_t features = netif_skb_features(skb);
++	unsigned int len = 0, prev_len = qdisc_pkt_len(skb);
+ 	int ret, nb;
+ 
+ 	segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+@@ -172,6 +173,7 @@ static int tbf_segment(struct sk_buff *s
+ 		nskb = segs->next;
+ 		segs->next = NULL;
+ 		qdisc_skb_cb(segs)->pkt_len = segs->len;
++		len += segs->len;
+ 		ret = qdisc_enqueue(segs, q->qdisc);
+ 		if (ret != NET_XMIT_SUCCESS) {
+ 			if (net_xmit_drop_count(ret))
+@@ -183,7 +185,7 @@ static int tbf_segment(struct sk_buff *s
+ 	}
+ 	sch->q.qlen += nb;
+ 	if (nb > 1)
+-		qdisc_tree_decrease_qlen(sch, 1 - nb);
++		qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len);
+ 	consume_skb(skb);
+ 	return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
+ }
+@@ -399,7 +401,8 @@ static int tbf_change(struct Qdisc *sch,
+ 
+ 	sch_tree_lock(sch);
+ 	if (child) {
+-		qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
++		qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
++					  q->qdisc->qstats.backlog);
+ 		qdisc_destroy(q->qdisc);
+ 		q->qdisc = child;
+ 	}

+ 189 - 0
target/linux/generic/patches-4.4/032-fq_codel-add-batch-ability-to-fq_codel_drop.patch

@@ -0,0 +1,189 @@
+From: Eric Dumazet <[email protected]>
+Date: Sun, 1 May 2016 16:47:26 -0700
+Subject: [PATCH] fq_codel: add batch ability to fq_codel_drop()
+
+In presence of inelastic flows and stress, we can call
+fq_codel_drop() for every packet entering fq_codel qdisc.
+
+fq_codel_drop() is quite expensive, as it does a linear scan
+of 4 KB of memory to find a fat flow.
+Once found, it drops the oldest packet of this flow.
+
+Instead of dropping a single packet, try to drop 50% of the backlog
+of this fat flow, with a configurable limit of 64 packets per round.
+
+TCA_FQ_CODEL_DROP_BATCH_SIZE is the new attribute to make this
+limit configurable.
+
+With this strategy the 4 KB search is amortized to a single cache line
+per drop [1], so fq_codel_drop() no longer appears at the top of kernel
+profile in presence of few inelastic flows.
+
+[1] Assuming a 64byte cache line, and 1024 buckets
+
+Signed-off-by: Eric Dumazet <[email protected]>
+Reported-by: Dave Taht <[email protected]>
+Cc: Jonathan Morton <[email protected]>
+Acked-by: Jesper Dangaard Brouer <[email protected]>
+Acked-by: Dave Taht
+Signed-off-by: David S. Miller <[email protected]>
+---
+
+--- a/include/uapi/linux/pkt_sched.h
++++ b/include/uapi/linux/pkt_sched.h
+@@ -711,6 +711,7 @@ enum {
+ 	TCA_FQ_CODEL_FLOWS,
+ 	TCA_FQ_CODEL_QUANTUM,
+ 	TCA_FQ_CODEL_CE_THRESHOLD,
++	TCA_FQ_CODEL_DROP_BATCH_SIZE,
+ 	__TCA_FQ_CODEL_MAX
+ };
+ 
+--- a/net/sched/sch_fq_codel.c
++++ b/net/sched/sch_fq_codel.c
+@@ -57,6 +57,7 @@ struct fq_codel_sched_data {
+ 	u32		flows_cnt;	/* number of flows */
+ 	u32		perturbation;	/* hash perturbation */
+ 	u32		quantum;	/* psched_mtu(qdisc_dev(sch)); */
++	u32		drop_batch_size;
+ 	struct codel_params cparams;
+ 	struct codel_stats cstats;
+ 	u32		drop_overlimit;
+@@ -133,17 +134,20 @@ static inline void flow_queue_add(struct
+ 	skb->next = NULL;
+ }
+ 
+-static unsigned int fq_codel_drop(struct Qdisc *sch)
++static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets)
+ {
+ 	struct fq_codel_sched_data *q = qdisc_priv(sch);
+ 	struct sk_buff *skb;
+ 	unsigned int maxbacklog = 0, idx = 0, i, len;
+ 	struct fq_codel_flow *flow;
++	unsigned int threshold;
+ 
+-	/* Queue is full! Find the fat flow and drop packet from it.
++	/* Queue is full! Find the fat flow and drop packet(s) from it.
+ 	 * This might sound expensive, but with 1024 flows, we scan
+ 	 * 4KB of memory, and we dont need to handle a complex tree
+ 	 * in fast path (packet queue/enqueue) with many cache misses.
++	 * In stress mode, we'll try to drop 64 packets from the flow,
++	 * amortizing this linear lookup to one cache line per drop.
+ 	 */
+ 	for (i = 0; i < q->flows_cnt; i++) {
+ 		if (q->backlogs[i] > maxbacklog) {
+@@ -151,15 +155,24 @@ static unsigned int fq_codel_drop(struct
+ 			idx = i;
+ 		}
+ 	}
++
++	/* Our goal is to drop half of this fat flow backlog */
++	threshold = maxbacklog >> 1;
++
+ 	flow = &q->flows[idx];
+-	skb = dequeue_head(flow);
+-	len = qdisc_pkt_len(skb);
++	len = 0;
++	i = 0;
++	do {
++		skb = dequeue_head(flow);
++		len += qdisc_pkt_len(skb);
++		kfree_skb(skb);
++	} while (++i < max_packets && len < threshold);
++
++	flow->dropped += i;
+ 	q->backlogs[idx] -= len;
+-	sch->q.qlen--;
+-	qdisc_qstats_drop(sch);
+-	qdisc_qstats_backlog_dec(sch, skb);
+-	kfree_skb(skb);
+-	flow->dropped++;
++	sch->qstats.drops += i;
++	sch->qstats.backlog -= len;
++	sch->q.qlen -= i;
+ 	return idx;
+ }
+ 
+@@ -168,14 +181,14 @@ static unsigned int fq_codel_qdisc_drop(
+ 	unsigned int prev_backlog;
+ 
+ 	prev_backlog = sch->qstats.backlog;
+-	fq_codel_drop(sch);
++	fq_codel_drop(sch, 1U);
+ 	return prev_backlog - sch->qstats.backlog;
+ }
+ 
+ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+ {
+ 	struct fq_codel_sched_data *q = qdisc_priv(sch);
+-	unsigned int idx, prev_backlog;
++	unsigned int idx, prev_backlog, prev_qlen;
+ 	struct fq_codel_flow *flow;
+ 	int uninitialized_var(ret);
+ 
+@@ -204,16 +217,22 @@ static int fq_codel_enqueue(struct sk_bu
+ 		return NET_XMIT_SUCCESS;
+ 
+ 	prev_backlog = sch->qstats.backlog;
+-	q->drop_overlimit++;
+-	/* Return Congestion Notification only if we dropped a packet
+-	 * from this flow.
++	prev_qlen = sch->q.qlen;
++
++	/* fq_codel_drop() is quite expensive, as it performs a linear search
++	 * in q->backlogs[] to find a fat flow.
++	 * So instead of dropping a single packet, drop half of its backlog
++	 * with a 64 packets limit to not add a too big cpu spike here.
+ 	 */
+-	if (fq_codel_drop(sch) == idx)
+-		return NET_XMIT_CN;
++	ret = fq_codel_drop(sch, q->drop_batch_size);
+ 
+-	/* As we dropped a packet, better let upper stack know this */
+-	qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog);
+-	return NET_XMIT_SUCCESS;
++	q->drop_overlimit += prev_qlen - sch->q.qlen;
++
++	/* As we dropped packet(s), better let upper stack know this */
++	qdisc_tree_reduce_backlog(sch, prev_qlen - sch->q.qlen,
++				  prev_backlog - sch->qstats.backlog);
++
++	return ret == idx ? NET_XMIT_CN : NET_XMIT_SUCCESS;
+ }
+ 
+ /* This is the specific function called from codel_dequeue()
+@@ -323,6 +342,7 @@ static const struct nla_policy fq_codel_
+ 	[TCA_FQ_CODEL_FLOWS]	= { .type = NLA_U32 },
+ 	[TCA_FQ_CODEL_QUANTUM]	= { .type = NLA_U32 },
+ 	[TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NLA_U32 },
++	[TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NLA_U32 },
+ };
+ 
+ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt)
+@@ -374,6 +394,9 @@ static int fq_codel_change(struct Qdisc
+ 	if (tb[TCA_FQ_CODEL_QUANTUM])
+ 		q->quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]));
+ 
++	if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE])
++		q->drop_batch_size = min(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]));
++
+ 	while (sch->q.qlen > sch->limit) {
+ 		struct sk_buff *skb = fq_codel_dequeue(sch);
+ 
+@@ -419,6 +442,7 @@ static int fq_codel_init(struct Qdisc *s
+ 
+ 	sch->limit = 10*1024;
+ 	q->flows_cnt = 1024;
++	q->drop_batch_size = 64;
+ 	q->quantum = psched_mtu(qdisc_dev(sch));
+ 	q->perturbation = prandom_u32();
+ 	INIT_LIST_HEAD(&q->new_flows);
+@@ -476,6 +500,8 @@ static int fq_codel_dump(struct Qdisc *s
+ 			q->cparams.ecn) ||
+ 	    nla_put_u32(skb, TCA_FQ_CODEL_QUANTUM,
+ 			q->quantum) ||
++	    nla_put_u32(skb, TCA_FQ_CODEL_DROP_BATCH_SIZE,
++			q->drop_batch_size) ||
+ 	    nla_put_u32(skb, TCA_FQ_CODEL_FLOWS,
+ 			q->flows_cnt))
+ 		goto nla_put_failure;

+ 5 - 5
target/linux/generic/patches-4.4/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch

@@ -284,15 +284,15 @@ Signed-off-by: Yousong Zhou <[email protected]>
 +	EXPORT(kexec_argv_buf)
 +	EXPORT(kexec_argv_buf)
 +	.skip		KEXEC_COMMAND_LINE_SIZE
 +	.skip		KEXEC_COMMAND_LINE_SIZE
 +	.size		kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE
 +	.size		kexec_argv_buf, KEXEC_COMMAND_LINE_SIZE
-+
-+kexec_argv:
-+	EXPORT(kexec_argv)
-+	.skip		KEXEC_ARGV_SIZE
-+	.size		kexec_argv, KEXEC_ARGV_SIZE
  
  
 -relocate_new_kernel_size:
 -relocate_new_kernel_size:
 -	EXPORT(relocate_new_kernel_size)
 -	EXPORT(relocate_new_kernel_size)
 -	PTR		relocate_new_kernel_end - relocate_new_kernel
 -	PTR		relocate_new_kernel_end - relocate_new_kernel
 -	.size		relocate_new_kernel_size, PTRSIZE
 -	.size		relocate_new_kernel_size, PTRSIZE
++kexec_argv:
++	EXPORT(kexec_argv)
++	.skip		KEXEC_ARGV_SIZE
++	.size		kexec_argv, KEXEC_ARGV_SIZE
++
 +kexec_relocate_new_kernel_end:
 +kexec_relocate_new_kernel_end:
 +	EXPORT(kexec_relocate_new_kernel_end)
 +	EXPORT(kexec_relocate_new_kernel_end)

+ 15 - 18
target/linux/generic/patches-4.4/531-debloat_lzma.patch

@@ -219,26 +219,26 @@
  {
  {
    UInt32 dicSize;
    UInt32 dicSize;
    Byte d;
    Byte d;
-@@ -935,7 +883,7 @@ static SRes LzmaDec_AllocateProbs2(CLzma
+@@ -935,33 +883,11 @@ static SRes LzmaDec_AllocateProbs2(CLzma
    return SZ_OK;
    return SZ_OK;
  }
  }
  
  
 -SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
 -SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
-+static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
- {
-   CLzmaProps propNew;
-   RINOK(LzmaProps_Decode(&propNew, props, propsSize));
-@@ -943,28 +891,6 @@ SRes LzmaDec_AllocateProbs(CLzmaDec *p,
-   p->prop = propNew;
-   return SZ_OK;
- }
--
--SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
 -{
 -{
 -  CLzmaProps propNew;
 -  CLzmaProps propNew;
--  SizeT dicBufSize;
 -  RINOK(LzmaProps_Decode(&propNew, props, propsSize));
 -  RINOK(LzmaProps_Decode(&propNew, props, propsSize));
 -  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
 -  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+-  p->prop = propNew;
+-  return SZ_OK;
+-}
+-
+-SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
++static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+ {
+   CLzmaProps propNew;
+-  SizeT dicBufSize;
+   RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+   RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
 -  dicBufSize = propNew.dicSize;
 -  dicBufSize = propNew.dicSize;
 -  if (p->dic == 0 || dicBufSize != p->dicBufSize)
 -  if (p->dic == 0 || dicBufSize != p->dicBufSize)
 -  {
 -  {
@@ -251,12 +251,9 @@
 -    }
 -    }
 -  }
 -  }
 -  p->dicBufSize = dicBufSize;
 -  p->dicBufSize = dicBufSize;
--  p->prop = propNew;
--  return SZ_OK;
--}
- 
- SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
-     const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+   p->prop = propNew;
+   return SZ_OK;
+ }
 --- a/include/linux/lzma/LzmaEnc.h
 --- a/include/linux/lzma/LzmaEnc.h
 +++ b/include/linux/lzma/LzmaEnc.h
 +++ b/include/linux/lzma/LzmaEnc.h
 @@ -31,9 +31,6 @@ typedef struct _CLzmaEncProps
 @@ -31,9 +31,6 @@ typedef struct _CLzmaEncProps

+ 2 - 3
target/linux/generic/patches-4.4/645-bridge_multicast_to_unicast.patch

@@ -382,8 +382,6 @@ Implement optinal multicast->unicast conversion for igmp snooping
  
  
 -		port = (unsigned long)lport > (unsigned long)rport ?
 -		port = (unsigned long)lport > (unsigned long)rport ?
 -		       lport : rport;
 -		       lport : rport;
--
--		prev = maybe_deliver(prev, port, skb, __packet_hook);
 +		if ((unsigned long)lport > (unsigned long)rport) {
 +		if ((unsigned long)lport > (unsigned long)rport) {
 +			port = lport;
 +			port = lport;
 +			addr = p->unicast ? p->eth_addr : NULL;
 +			addr = p->unicast ? p->eth_addr : NULL;
@@ -391,7 +389,8 @@ Implement optinal multicast->unicast conversion for igmp snooping
 +			port = rport;
 +			port = rport;
 +			addr = NULL;
 +			addr = NULL;
 +		}
 +		}
-+
+ 
+-		prev = maybe_deliver(prev, port, skb, __packet_hook);
 +		if (addr)
 +		if (addr)
 +			prev = maybe_deliver_addr(prev, port, skb, addr,
 +			prev = maybe_deliver_addr(prev, port, skb, addr,
 +						  __packet_hook);
 +						  __packet_hook);

+ 2 - 2
target/linux/generic/patches-4.4/660-fq_codel_defaults.patch

@@ -1,6 +1,6 @@
 --- a/net/sched/sch_fq_codel.c
 --- a/net/sched/sch_fq_codel.c
 +++ b/net/sched/sch_fq_codel.c
 +++ b/net/sched/sch_fq_codel.c
-@@ -410,8 +410,8 @@ static int fq_codel_init(struct Qdisc *s
+@@ -440,8 +440,8 @@ static int fq_codel_init(struct Qdisc *s
  	struct fq_codel_sched_data *q = qdisc_priv(sch);
  	struct fq_codel_sched_data *q = qdisc_priv(sch);
  	int i;
  	int i;
  
  
@@ -8,6 +8,6 @@
 -	q->flows_cnt = 1024;
 -	q->flows_cnt = 1024;
 +	sch->limit = 1024;
 +	sch->limit = 1024;
 +	q->flows_cnt = 128;
 +	q->flows_cnt = 128;
+ 	q->drop_batch_size = 64;
  	q->quantum = psched_mtu(qdisc_dev(sch));
  	q->quantum = psched_mtu(qdisc_dev(sch));
  	q->perturbation = prandom_u32();
  	q->perturbation = prandom_u32();
- 	INIT_LIST_HEAD(&q->new_flows);

+ 1 - 1
target/linux/generic/patches-4.4/661-fq_codel_keep_dropped_stats.patch

@@ -1,6 +1,6 @@
 --- a/net/sched/sch_fq_codel.c
 --- a/net/sched/sch_fq_codel.c
 +++ b/net/sched/sch_fq_codel.c
 +++ b/net/sched/sch_fq_codel.c
-@@ -198,7 +198,6 @@ static int fq_codel_enqueue(struct sk_bu
+@@ -211,7 +211,6 @@ static int fq_codel_enqueue(struct sk_bu
  		list_add_tail(&flow->flowchain, &q->new_flows);
  		list_add_tail(&flow->flowchain, &q->new_flows);
  		q->new_flow_count++;
  		q->new_flow_count++;
  		flow->deficit = q->quantum;
  		flow->deficit = q->quantum;

+ 3 - 3
target/linux/generic/patches-4.4/662-use_fq_codel_by_default.patch

@@ -13,7 +13,7 @@
  	  device, it has to decide which ones to send first, which ones to
  	  device, it has to decide which ones to send first, which ones to
 --- a/net/sched/sch_fq_codel.c
 --- a/net/sched/sch_fq_codel.c
 +++ b/net/sched/sch_fq_codel.c
 +++ b/net/sched/sch_fq_codel.c
-@@ -621,7 +621,7 @@ static const struct Qdisc_class_ops fq_c
+@@ -654,7 +654,7 @@ static const struct Qdisc_class_ops fq_c
  	.walk		=	fq_codel_walk,
  	.walk		=	fq_codel_walk,
  };
  };
  
  
@@ -22,7 +22,7 @@
  	.cl_ops		=	&fq_codel_class_ops,
  	.cl_ops		=	&fq_codel_class_ops,
  	.id		=	"fq_codel",
  	.id		=	"fq_codel",
  	.priv_size	=	sizeof(struct fq_codel_sched_data),
  	.priv_size	=	sizeof(struct fq_codel_sched_data),
-@@ -637,6 +637,7 @@ static struct Qdisc_ops fq_codel_qdisc_o
+@@ -670,6 +670,7 @@ static struct Qdisc_ops fq_codel_qdisc_o
  	.dump_stats =	fq_codel_dump_stats,
  	.dump_stats =	fq_codel_dump_stats,
  	.owner		=	THIS_MODULE,
  	.owner		=	THIS_MODULE,
  };
  };
@@ -84,7 +84,7 @@
  		if (qdisc == NULL) {
  		if (qdisc == NULL) {
 --- a/net/sched/sch_api.c
 --- a/net/sched/sch_api.c
 +++ b/net/sched/sch_api.c
 +++ b/net/sched/sch_api.c
-@@ -1946,7 +1946,7 @@ static int __init pktsched_init(void)
+@@ -1948,7 +1948,7 @@ static int __init pktsched_init(void)
  		return err;
  		return err;
  	}
  	}