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

Commit c6e14f9e authored by Subash Abhinov Kasiviswanathan's avatar Subash Abhinov Kasiviswanathan Committed by Sharath Chandra Vurukala
Browse files

net_sched: Add flow control support to prio qdisc



Add enable_flow flag to the prio qdisc. Packet flow is enabled by
default, but can be disabled from userspace (e.g. IPROUTE2 tc tool).
This allows for suspending packet dequeue on a per-qdisc basis,
which is needed to support Quality of Service (QOS).

Export a function that will look up desired qdisc and call it's
registered change function to enable/disable flow. This API also
returns the size of the qdisc in order to be able to collect data on
the size of the qdisc before doing flow control operations. This is
required to effectively diagnose the state of the queues when
debugging flow control.

The PRIO qdisc supports flow control, such that packet
dequeue can be disabled based on boolean flag 'enable_flow'.
When flow is re-enabled, the latency for new packets
arriving at network driver is high.  To reduce the delay in
scheduling packets, the qdisc will now invoke
__netif_schedule() to expedite dequeue.  This significantly
reduces the latency of packets arriving at network driver.

Change-Id: I0e9096e4241d459540028558fdec18ece460d517
Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Signed-off-by: default avatarSharath Chandra Vurukala <sharathv@codeaurora.org>
parent bed17f6d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -133,6 +133,8 @@ static inline __be16 tc_skb_protocol(const struct sk_buff *skb)
	return skb->protocol;
}

extern int tc_qdisc_flow_control(struct net_device *dev, u32 tcm_handle,
				  int flow_enable);
/* Calculate maximal size of packet seen by hard_start_xmit
   routine of this device.
 */
+3 −0
Original line number Diff line number Diff line
@@ -147,8 +147,11 @@ struct tc_skbprio_qopt {
struct tc_prio_qopt {
	int	bands;			/* Number of bands */
	__u8	priomap[TC_PRIO_MAX+1];	/* Map: logical priority -> PRIO band */
	__u8	enable_flow;		/* Enable dequeue */
};

#define TCQ_PRIO_FLOW_CONTROL 1

/* MULTIQ section */

struct tc_multiq_qopt {
+34 −0
Original line number Diff line number Diff line
@@ -1404,6 +1404,40 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
	return 0;
}

/*
 * enable/disable flow on qdisc.
 */
int
tc_qdisc_flow_control(struct net_device *dev, u32 tcm_handle, int enable_flow)
{
	struct Qdisc *q;
	int qdisc_len = 0;
	struct __qdisc_change_req {
		struct nlattr attr;
		struct tc_prio_qopt data;
	} req =	{
		.attr = {sizeof(struct __qdisc_change_req), TCA_OPTIONS},
		.data = {3, {1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, 1}
		};

	/* override flow bit */
	req.data.enable_flow = enable_flow;

	/* look up using tcm handle */
	q = qdisc_lookup(dev, tcm_handle);

	/* call registered change function */
	if (likely(q && q->ops)) {
		if (likely(q->ops->change)) {
			qdisc_len = q->q.qlen;
			if (q->ops->change(q, &req.attr, NULL))
				pr_err("%s(): qdisc change failed\n", __func__);
		}
	}
	return qdisc_len;
}
EXPORT_SYMBOL(tc_qdisc_flow_control);

/*
 * Create/change qdisc.
 */
+21 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>
@@ -28,6 +29,7 @@ struct prio_sched_data {
	struct tcf_block *block;
	u8  prio2band[TC_PRIO_MAX+1];
	struct Qdisc *queues[TCQ_PRIO_BANDS];
	u8 enable_flow;
};


@@ -102,6 +104,9 @@ static struct sk_buff *prio_peek(struct Qdisc *sch)
	struct prio_sched_data *q = qdisc_priv(sch);
	int prio;

	if (!q->enable_flow)
		return NULL;

	for (prio = 0; prio < q->bands; prio++) {
		struct Qdisc *qdisc = q->queues[prio];
		struct sk_buff *skb = qdisc->ops->peek(qdisc);
@@ -116,6 +121,9 @@ static struct sk_buff *prio_dequeue(struct Qdisc *sch)
	struct prio_sched_data *q = qdisc_priv(sch);
	int prio;

	if (!q->enable_flow)
		return NULL;

	for (prio = 0; prio < q->bands; prio++) {
		struct Qdisc *qdisc = q->queues[prio];
		struct sk_buff *skb = qdisc_dequeue_peeked(qdisc);
@@ -140,6 +148,7 @@ prio_reset(struct Qdisc *sch)
		qdisc_reset(q->queues[prio]);
	sch->qstats.backlog = 0;
	sch->q.qlen = 0;
	q->enable_flow = 1;
}

static int prio_offload(struct Qdisc *sch, struct tc_prio_qopt *qopt)
@@ -185,6 +194,7 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt,
	struct Qdisc *queues[TCQ_PRIO_BANDS];
	int oldbands = q->bands, i;
	struct tc_prio_qopt *qopt;
	int flow_change = 0;

	if (nla_len(opt) < sizeof(*qopt))
		return -EINVAL;
@@ -212,6 +222,10 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt,

	prio_offload(sch, qopt);
	sch_tree_lock(sch);
	if (q->enable_flow != qopt->enable_flow) {
		q->enable_flow = qopt->enable_flow;
		flow_change = 1;
	}
	q->bands = qopt->bands;
	memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);

@@ -230,6 +244,12 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt,
	}

	sch_tree_unlock(sch);

	/* Schedule qdisc when flow re-enabled */
	if (flow_change && q->enable_flow) {
		if (!test_bit(__QDISC_STATE_DEACTIVATED, &sch->state))
			__netif_schedule(qdisc_root(sch));
	}
	return 0;
}

@@ -288,6 +308,7 @@ static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
	int err;

	opt.bands = q->bands;
	opt.enable_flow = q->enable_flow;
	memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX + 1);

	err = prio_dump_offload(sch);