forked from mirrors/linux
		
	net: sched: introduce and use qdisc tree flush/purge helpers
The same code to flush qdisc tree and purge the qdisc queue
is duplicated in many places and in most cases it does not
respect NOLOCK qdisc: the global backlog len is used and the
per CPU values are ignored.
This change addresses the above, factoring-out the relevant
code and using the helpers introduced by the previous patch
to fetch the correct backlog len.
Fixes: c5ad119fb6 ("net: sched: pfifo_fast use skb_array")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									5dd431b6b9
								
							
						
					
					
						commit
						e5f0e8f8e4
					
				
					 11 changed files with 35 additions and 73 deletions
				
			
		| 
						 | 
					@ -941,6 +941,23 @@ static inline void qdisc_qstats_qlen_backlog(struct Qdisc *sch,  __u32 *qlen,
 | 
				
			||||||
	*backlog = qstats.backlog;
 | 
						*backlog = qstats.backlog;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void qdisc_tree_flush_backlog(struct Qdisc *sch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__u32 qlen, backlog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qdisc_qstats_qlen_backlog(sch, &qlen, &backlog);
 | 
				
			||||||
 | 
						qdisc_tree_reduce_backlog(sch, qlen, backlog);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void qdisc_purge_queue(struct Qdisc *sch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__u32 qlen, backlog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						qdisc_qstats_qlen_backlog(sch, &qlen, &backlog);
 | 
				
			||||||
 | 
						qdisc_reset(sch);
 | 
				
			||||||
 | 
						qdisc_tree_reduce_backlog(sch, qlen, backlog);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void qdisc_skb_head_init(struct qdisc_skb_head *qh)
 | 
					static inline void qdisc_skb_head_init(struct qdisc_skb_head *qh)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	qh->head = NULL;
 | 
						qh->head = NULL;
 | 
				
			||||||
| 
						 | 
					@ -1124,13 +1141,8 @@ static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new,
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
	old = *pold;
 | 
						old = *pold;
 | 
				
			||||||
	*pold = new;
 | 
						*pold = new;
 | 
				
			||||||
	if (old != NULL) {
 | 
						if (old != NULL)
 | 
				
			||||||
		unsigned int qlen = old->q.qlen;
 | 
							qdisc_tree_flush_backlog(old);
 | 
				
			||||||
		unsigned int backlog = old->qstats.backlog;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		qdisc_reset(old);
 | 
					 | 
				
			||||||
		qdisc_tree_reduce_backlog(old, qlen, backlog);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sch_tree_unlock(sch);
 | 
						sch_tree_unlock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return old;
 | 
						return old;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1667,17 +1667,13 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cbq_sched_data *q = qdisc_priv(sch);
 | 
						struct cbq_sched_data *q = qdisc_priv(sch);
 | 
				
			||||||
	struct cbq_class *cl = (struct cbq_class *)arg;
 | 
						struct cbq_class *cl = (struct cbq_class *)arg;
 | 
				
			||||||
	unsigned int qlen, backlog;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cl->filters || cl->children || cl == &q->link)
 | 
						if (cl->filters || cl->children || cl == &q->link)
 | 
				
			||||||
		return -EBUSY;
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qlen = cl->q->q.qlen;
 | 
						qdisc_purge_queue(cl->q);
 | 
				
			||||||
	backlog = cl->q->qstats.backlog;
 | 
					 | 
				
			||||||
	qdisc_reset(cl->q);
 | 
					 | 
				
			||||||
	qdisc_tree_reduce_backlog(cl->q, qlen, backlog);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cl->next_alive)
 | 
						if (cl->next_alive)
 | 
				
			||||||
		cbq_deactivate_class(cl);
 | 
							cbq_deactivate_class(cl);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,15 +50,6 @@ static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
 | 
				
			||||||
	return container_of(clc, struct drr_class, common);
 | 
						return container_of(clc, struct drr_class, common);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_reduce_backlog(cl->qdisc, len, backlog);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
 | 
					static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
 | 
				
			||||||
	[TCA_DRR_QUANTUM]	= { .type = NLA_U32 },
 | 
						[TCA_DRR_QUANTUM]	= { .type = NLA_U32 },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -167,7 +158,7 @@ static int drr_delete_class(struct Qdisc *sch, unsigned long arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drr_purge_queue(cl);
 | 
						qdisc_purge_queue(cl->qdisc);
 | 
				
			||||||
	qdisc_class_hash_remove(&q->clhash, &cl->common);
 | 
						qdisc_class_hash_remove(&q->clhash, &cl->common);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_unlock(sch);
 | 
						sch_tree_unlock(sch);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -844,16 +844,6 @@ qdisc_peek_len(struct Qdisc *sch)
 | 
				
			||||||
	return len;
 | 
						return len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_reduce_backlog(cl->qdisc, len, backlog);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
hfsc_adjust_levels(struct hfsc_class *cl)
 | 
					hfsc_adjust_levels(struct hfsc_class *cl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1076,7 +1066,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
 | 
				
			||||||
	qdisc_class_hash_insert(&q->clhash, &cl->cl_common);
 | 
						qdisc_class_hash_insert(&q->clhash, &cl->cl_common);
 | 
				
			||||||
	list_add_tail(&cl->siblings, &parent->children);
 | 
						list_add_tail(&cl->siblings, &parent->children);
 | 
				
			||||||
	if (parent->level == 0)
 | 
						if (parent->level == 0)
 | 
				
			||||||
		hfsc_purge_queue(sch, parent);
 | 
							qdisc_purge_queue(parent->qdisc);
 | 
				
			||||||
	hfsc_adjust_levels(parent);
 | 
						hfsc_adjust_levels(parent);
 | 
				
			||||||
	sch_tree_unlock(sch);
 | 
						sch_tree_unlock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1112,7 +1102,7 @@ hfsc_delete_class(struct Qdisc *sch, unsigned long arg)
 | 
				
			||||||
	list_del(&cl->siblings);
 | 
						list_del(&cl->siblings);
 | 
				
			||||||
	hfsc_adjust_levels(cl->cl_parent);
 | 
						hfsc_adjust_levels(cl->cl_parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hfsc_purge_queue(sch, cl);
 | 
						qdisc_purge_queue(cl->qdisc);
 | 
				
			||||||
	qdisc_class_hash_remove(&q->clhash, &cl->cl_common);
 | 
						qdisc_class_hash_remove(&q->clhash, &cl->cl_common);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_unlock(sch);
 | 
						sch_tree_unlock(sch);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1269,13 +1269,8 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cl->level) {
 | 
						if (!cl->level)
 | 
				
			||||||
		unsigned int qlen = cl->leaf.q->q.qlen;
 | 
							qdisc_purge_queue(cl->leaf.q);
 | 
				
			||||||
		unsigned int backlog = cl->leaf.q->qstats.backlog;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		qdisc_reset(cl->leaf.q);
 | 
					 | 
				
			||||||
		qdisc_tree_reduce_backlog(cl->leaf.q, qlen, backlog);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* delete from hash and active; remainder in destroy_class */
 | 
						/* delete from hash and active; remainder in destroy_class */
 | 
				
			||||||
	qdisc_class_hash_remove(&q->clhash, &cl->common);
 | 
						qdisc_class_hash_remove(&q->clhash, &cl->common);
 | 
				
			||||||
| 
						 | 
					@ -1403,12 +1398,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
 | 
				
			||||||
					  classid, NULL);
 | 
										  classid, NULL);
 | 
				
			||||||
		sch_tree_lock(sch);
 | 
							sch_tree_lock(sch);
 | 
				
			||||||
		if (parent && !parent->level) {
 | 
							if (parent && !parent->level) {
 | 
				
			||||||
			unsigned int qlen = parent->leaf.q->q.qlen;
 | 
					 | 
				
			||||||
			unsigned int backlog = parent->leaf.q->qstats.backlog;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* turn parent into inner node */
 | 
								/* turn parent into inner node */
 | 
				
			||||||
			qdisc_reset(parent->leaf.q);
 | 
								qdisc_purge_queue(parent->leaf.q);
 | 
				
			||||||
			qdisc_tree_reduce_backlog(parent->leaf.q, qlen, backlog);
 | 
					 | 
				
			||||||
			qdisc_put(parent->leaf.q);
 | 
								qdisc_put(parent->leaf.q);
 | 
				
			||||||
			if (parent->prio_activity)
 | 
								if (parent->prio_activity)
 | 
				
			||||||
				htb_deactivate(q, parent);
 | 
									htb_deactivate(q, parent);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,9 +201,9 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
 | 
				
			||||||
	for (i = q->bands; i < q->max_bands; i++) {
 | 
						for (i = q->bands; i < q->max_bands; i++) {
 | 
				
			||||||
		if (q->queues[i] != &noop_qdisc) {
 | 
							if (q->queues[i] != &noop_qdisc) {
 | 
				
			||||||
			struct Qdisc *child = q->queues[i];
 | 
								struct Qdisc *child = q->queues[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			q->queues[i] = &noop_qdisc;
 | 
								q->queues[i] = &noop_qdisc;
 | 
				
			||||||
			qdisc_tree_reduce_backlog(child, child->q.qlen,
 | 
								qdisc_tree_flush_backlog(child);
 | 
				
			||||||
						  child->qstats.backlog);
 | 
					 | 
				
			||||||
			qdisc_put(child);
 | 
								qdisc_put(child);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -225,9 +225,7 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
 | 
				
			||||||
					qdisc_hash_add(child, true);
 | 
										qdisc_hash_add(child, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (old != &noop_qdisc) {
 | 
									if (old != &noop_qdisc) {
 | 
				
			||||||
					qdisc_tree_reduce_backlog(old,
 | 
										qdisc_tree_flush_backlog(old);
 | 
				
			||||||
								  old->q.qlen,
 | 
					 | 
				
			||||||
								  old->qstats.backlog);
 | 
					 | 
				
			||||||
					qdisc_put(old);
 | 
										qdisc_put(old);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				sch_tree_unlock(sch);
 | 
									sch_tree_unlock(sch);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,12 +216,8 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt,
 | 
				
			||||||
	q->bands = qopt->bands;
 | 
						q->bands = qopt->bands;
 | 
				
			||||||
	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
 | 
						memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = q->bands; i < oldbands; i++) {
 | 
						for (i = q->bands; i < oldbands; i++)
 | 
				
			||||||
		struct Qdisc *child = q->queues[i];
 | 
							qdisc_tree_flush_backlog(q->queues[i]);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		qdisc_tree_reduce_backlog(child, child->q.qlen,
 | 
					 | 
				
			||||||
					  child->qstats.backlog);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = oldbands; i < q->bands; i++) {
 | 
						for (i = oldbands; i < q->bands; i++) {
 | 
				
			||||||
		q->queues[i] = queues[i];
 | 
							q->queues[i] = queues[i];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -217,15 +217,6 @@ static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid)
 | 
				
			||||||
	return container_of(clc, struct qfq_class, common);
 | 
						return container_of(clc, struct qfq_class, common);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_reduce_backlog(cl->qdisc, len, backlog);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
 | 
					static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
 | 
				
			||||||
	[TCA_QFQ_WEIGHT] = { .type = NLA_U32 },
 | 
						[TCA_QFQ_WEIGHT] = { .type = NLA_U32 },
 | 
				
			||||||
	[TCA_QFQ_LMAX] = { .type = NLA_U32 },
 | 
						[TCA_QFQ_LMAX] = { .type = NLA_U32 },
 | 
				
			||||||
| 
						 | 
					@ -551,7 +542,7 @@ static int qfq_delete_class(struct Qdisc *sch, unsigned long arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qfq_purge_queue(cl);
 | 
						qdisc_purge_queue(cl->qdisc);
 | 
				
			||||||
	qdisc_class_hash_remove(&q->clhash, &cl->common);
 | 
						qdisc_class_hash_remove(&q->clhash, &cl->common);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_unlock(sch);
 | 
						sch_tree_unlock(sch);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -233,8 +233,7 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt,
 | 
				
			||||||
	q->flags = ctl->flags;
 | 
						q->flags = ctl->flags;
 | 
				
			||||||
	q->limit = ctl->limit;
 | 
						q->limit = ctl->limit;
 | 
				
			||||||
	if (child) {
 | 
						if (child) {
 | 
				
			||||||
		qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
 | 
							qdisc_tree_flush_backlog(q->qdisc);
 | 
				
			||||||
					  q->qdisc->qstats.backlog);
 | 
					 | 
				
			||||||
		old_child = q->qdisc;
 | 
							old_child = q->qdisc;
 | 
				
			||||||
		q->qdisc = child;
 | 
							q->qdisc = child;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -521,8 +521,7 @@ static int sfb_change(struct Qdisc *sch, struct nlattr *opt,
 | 
				
			||||||
		qdisc_hash_add(child, true);
 | 
							qdisc_hash_add(child, true);
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
 | 
						qdisc_tree_flush_backlog(q->qdisc);
 | 
				
			||||||
				  q->qdisc->qstats.backlog);
 | 
					 | 
				
			||||||
	qdisc_put(q->qdisc);
 | 
						qdisc_put(q->qdisc);
 | 
				
			||||||
	q->qdisc = child;
 | 
						q->qdisc = child;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -391,8 +391,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sch_tree_lock(sch);
 | 
						sch_tree_lock(sch);
 | 
				
			||||||
	if (child) {
 | 
						if (child) {
 | 
				
			||||||
		qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
 | 
							qdisc_tree_flush_backlog(q->qdisc);
 | 
				
			||||||
					  q->qdisc->qstats.backlog);
 | 
					 | 
				
			||||||
		qdisc_put(q->qdisc);
 | 
							qdisc_put(q->qdisc);
 | 
				
			||||||
		q->qdisc = child;
 | 
							q->qdisc = child;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue