summaryrefslogtreecommitdiff
path: root/net/sched
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched')
-rw-r--r--net/sched/sch_api.c39
-rw-r--r--net/sched/sch_prio.c22
2 files changed, 61 insertions, 0 deletions
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 95b560f0b253..b855352167b1 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1173,6 +1173,45 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
}
/*
+ * 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) != 0)
+ pr_err("%s(): qdisc change failed", __func__);
+ } else {
+ WARN_ONCE(1, "%s(): called on queue which does %s",
+ __func__, "not support change() operation");
+ }
+ } else {
+ WARN_ONCE(1, "%s(): called on bad queue", __func__);
+ }
+ return qdisc_len;
+}
+EXPORT_SYMBOL(tc_qdisc_flow_control);
+
+/*
* Create/change qdisc.
*/
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index fee1b15506b2..0d4630b155fe 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -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>
@@ -27,6 +28,7 @@ struct prio_sched_data {
struct tcf_proto __rcu *filter_list;
u8 prio2band[TC_PRIO_MAX+1];
struct Qdisc *queues[TCQ_PRIO_BANDS];
+ u8 enable_flow;
};
@@ -98,6 +100,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);
@@ -112,6 +117,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);
@@ -152,6 +160,7 @@ prio_reset(struct Qdisc *sch)
for (prio = 0; prio < q->bands; prio++)
qdisc_reset(q->queues[prio]);
sch->q.qlen = 0;
+ q->enable_flow = 1;
}
static void
@@ -170,6 +179,7 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
struct prio_sched_data *q = qdisc_priv(sch);
struct tc_prio_qopt *qopt;
int i;
+ int flow_change = 0;
if (nla_len(opt) < sizeof(*qopt))
return -EINVAL;
@@ -184,6 +194,10 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
}
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);
@@ -219,6 +233,13 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
}
}
}
+
+ /* 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;
}
@@ -248,6 +269,7 @@ static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
struct tc_prio_qopt opt;
opt.bands = q->bands;
+ opt.enable_flow = q->enable_flow;
memcpy(&opt.priomap, q->prio2band, TC_PRIO_MAX + 1);
if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))