Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88da48f4 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'sched_skb_free_defer'



Eric Dumazet says:

====================
net_sched: defer skb freeing while changing qdiscs

qdiscs/classes are changed under RTNL protection and often
while blocking BH and root qdisc spinlock.

When lots of skbs need to be dropped, we free
them under these locks causing TX/RX freezes,
and more generally latency spikes.

I saw spikes of 50+ ms on quite fast hardware...

This patch series adds a simple queue protected by RTNL
where skbs can be placed until RTNL is released.

Note that this might also serve in the future for optional
reinjection of packets when a qdisc is replaced.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 35c55c98 fea02478
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -89,8 +89,9 @@ void net_inc_egress_queue(void);
void net_dec_egress_queue(void);
#endif

extern void rtnetlink_init(void);
extern void __rtnl_unlock(void);
void rtnetlink_init(void);
void __rtnl_unlock(void);
void rtnl_kfree_skbs(struct sk_buff *head, struct sk_buff *tail);

#define ASSERT_RTNL() do { \
	if (unlikely(!rtnl_is_locked())) { \
+12 −4
Original line number Diff line number Diff line
@@ -683,19 +683,21 @@ static inline struct sk_buff *qdisc_dequeue_peeked(struct Qdisc *sch)
	return skb;
}

static inline void __qdisc_reset_queue(struct Qdisc *sch,
				       struct sk_buff_head *list)
static inline void __qdisc_reset_queue(struct sk_buff_head *list)
{
	/*
	 * We do not know the backlog in bytes of this list, it
	 * is up to the caller to correct it
	 */
	__skb_queue_purge(list);
	if (!skb_queue_empty(list)) {
		rtnl_kfree_skbs(list->next, list->prev);
		__skb_queue_head_init(list);
	}
}

static inline void qdisc_reset_queue(struct Qdisc *sch)
{
	__qdisc_reset_queue(sch, &sch->q);
	__qdisc_reset_queue(&sch->q);
	sch->qstats.backlog = 0;
}

@@ -716,6 +718,12 @@ static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new,
	return old;
}

static inline void rtnl_qdisc_drop(struct sk_buff *skb, struct Qdisc *sch)
{
	rtnl_kfree_skbs(skb, skb);
	qdisc_qstats_drop(sch);
}

static inline int qdisc_drop(struct sk_buff *skb, struct Qdisc *sch)
{
	kfree_skb(skb);
+22 −0
Original line number Diff line number Diff line
@@ -71,9 +71,31 @@ void rtnl_lock(void)
}
EXPORT_SYMBOL(rtnl_lock);

static struct sk_buff *defer_kfree_skb_list;
void rtnl_kfree_skbs(struct sk_buff *head, struct sk_buff *tail)
{
	if (head && tail) {
		tail->next = defer_kfree_skb_list;
		defer_kfree_skb_list = head;
	}
}
EXPORT_SYMBOL(rtnl_kfree_skbs);

void __rtnl_unlock(void)
{
	struct sk_buff *head = defer_kfree_skb_list;

	defer_kfree_skb_list = NULL;

	mutex_unlock(&rtnl_mutex);

	while (head) {
		struct sk_buff *next = head->next;

		kfree_skb(head);
		cond_resched();
		head = next;
	}
}

void rtnl_unlock(void)
+4 −4
Original line number Diff line number Diff line
@@ -375,11 +375,11 @@ static void choke_reset(struct Qdisc *sch)
		q->head = (q->head + 1) & q->tab_mask;
		if (!skb)
			continue;
		qdisc_qstats_backlog_dec(sch, skb);
		--sch->q.qlen;
		qdisc_drop(skb, sch);
		rtnl_qdisc_drop(skb, sch);
	}

	sch->q.qlen = 0;
	sch->qstats.backlog = 0;
	memset(q->tab, 0, (q->tab_mask + 1) * sizeof(struct sk_buff *));
	q->head = q->tail = 0;
	red_restart(&q->vars);
@@ -455,7 +455,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt)
				dropped += qdisc_pkt_len(skb);
				qdisc_qstats_backlog_dec(sch, skb);
				--sch->q.qlen;
				qdisc_drop(skb, sch);
				rtnl_qdisc_drop(skb, sch);
			}
			qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped);
			q->head = 0;
+1 −1
Original line number Diff line number Diff line
@@ -174,7 +174,7 @@ static int codel_change(struct Qdisc *sch, struct nlattr *opt)

		dropped += qdisc_pkt_len(skb);
		qdisc_qstats_backlog_dec(sch, skb);
		qdisc_drop(skb, sch);
		rtnl_qdisc_drop(skb, sch);
	}
	qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);

Loading