summaryrefslogtreecommitdiffstats
path: root/net/sched/sch_cbq.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sched/sch_cbq.c')
-rw-r--r--net/sched/sch_cbq.c78
1 files changed, 52 insertions, 26 deletions
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 525eb3a6d625..f42025d53cfe 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1132,7 +1132,8 @@ static const struct nla_policy cbq_policy[TCA_CBQ_MAX + 1] = {
[TCA_CBQ_POLICE] = { .len = sizeof(struct tc_cbq_police) },
};
-static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
+static int cbq_init(struct Qdisc *sch, struct nlattr *opt,
+ struct netlink_ext_ack *extack)
{
struct cbq_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_CBQ_MAX + 1];
@@ -1143,22 +1144,27 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
hrtimer_init(&q->delay_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
q->delay_timer.function = cbq_undelay;
- if (!opt)
+ if (!opt) {
+ NL_SET_ERR_MSG(extack, "CBQ options are required for this operation");
return -EINVAL;
+ }
- err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL);
+ err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, extack);
if (err < 0)
return err;
- if (tb[TCA_CBQ_RTAB] == NULL || tb[TCA_CBQ_RATE] == NULL)
+ if (!tb[TCA_CBQ_RTAB] || !tb[TCA_CBQ_RATE]) {
+ NL_SET_ERR_MSG(extack, "Rate specification missing or incomplete");
return -EINVAL;
+ }
r = nla_data(tb[TCA_CBQ_RATE]);
- if ((q->link.R_tab = qdisc_get_rtab(r, tb[TCA_CBQ_RTAB])) == NULL)
+ q->link.R_tab = qdisc_get_rtab(r, tb[TCA_CBQ_RTAB], extack);
+ if (!q->link.R_tab)
return -EINVAL;
- err = tcf_block_get(&q->link.block, &q->link.filter_list, sch);
+ err = tcf_block_get(&q->link.block, &q->link.filter_list, sch, extack);
if (err)
goto put_rtab;
@@ -1170,7 +1176,7 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
q->link.common.classid = sch->handle;
q->link.qdisc = sch;
q->link.q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
- sch->handle);
+ sch->handle, NULL);
if (!q->link.q)
q->link.q = &noop_qdisc;
else
@@ -1369,13 +1375,13 @@ cbq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
}
static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
- struct Qdisc **old)
+ struct Qdisc **old, struct netlink_ext_ack *extack)
{
struct cbq_class *cl = (struct cbq_class *)arg;
if (new == NULL) {
- new = qdisc_create_dflt(sch->dev_queue,
- &pfifo_qdisc_ops, cl->common.classid);
+ new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
+ cl->common.classid, extack);
if (new == NULL)
return -ENOBUFS;
}
@@ -1450,7 +1456,7 @@ static void cbq_destroy(struct Qdisc *sch)
static int
cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **tca,
- unsigned long *arg)
+ unsigned long *arg, struct netlink_ext_ack *extack)
{
int err;
struct cbq_sched_data *q = qdisc_priv(sch);
@@ -1460,29 +1466,37 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
struct cbq_class *parent;
struct qdisc_rate_table *rtab = NULL;
- if (opt == NULL)
+ if (!opt) {
+ NL_SET_ERR_MSG(extack, "Mandatory qdisc options missing");
return -EINVAL;
+ }
- err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL);
+ err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, extack);
if (err < 0)
return err;
- if (tb[TCA_CBQ_OVL_STRATEGY] || tb[TCA_CBQ_POLICE])
+ if (tb[TCA_CBQ_OVL_STRATEGY] || tb[TCA_CBQ_POLICE]) {
+ NL_SET_ERR_MSG(extack, "Neither overlimit strategy nor policing attributes can be used for changing class params");
return -EOPNOTSUPP;
+ }
if (cl) {
/* Check parent */
if (parentid) {
if (cl->tparent &&
- cl->tparent->common.classid != parentid)
+ cl->tparent->common.classid != parentid) {
+ NL_SET_ERR_MSG(extack, "Invalid parent id");
return -EINVAL;
- if (!cl->tparent && parentid != TC_H_ROOT)
+ }
+ if (!cl->tparent && parentid != TC_H_ROOT) {
+ NL_SET_ERR_MSG(extack, "Parent must be root");
return -EINVAL;
+ }
}
if (tb[TCA_CBQ_RATE]) {
rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]),
- tb[TCA_CBQ_RTAB]);
+ tb[TCA_CBQ_RTAB], extack);
if (rtab == NULL)
return -EINVAL;
}
@@ -1494,6 +1508,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
qdisc_root_sleeping_running(sch),
tca[TCA_RATE]);
if (err) {
+ NL_SET_ERR_MSG(extack, "Failed to replace specified rate estimator");
qdisc_put_rtab(rtab);
return err;
}
@@ -1532,19 +1547,23 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
if (parentid == TC_H_ROOT)
return -EINVAL;
- if (tb[TCA_CBQ_WRROPT] == NULL || tb[TCA_CBQ_RATE] == NULL ||
- tb[TCA_CBQ_LSSOPT] == NULL)
+ if (!tb[TCA_CBQ_WRROPT] || !tb[TCA_CBQ_RATE] || !tb[TCA_CBQ_LSSOPT]) {
+ NL_SET_ERR_MSG(extack, "One of the following attributes MUST be specified: WRR, rate or link sharing");
return -EINVAL;
+ }
- rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]), tb[TCA_CBQ_RTAB]);
+ rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]), tb[TCA_CBQ_RTAB],
+ extack);
if (rtab == NULL)
return -EINVAL;
if (classid) {
err = -EINVAL;
if (TC_H_MAJ(classid ^ sch->handle) ||
- cbq_class_lookup(q, classid))
+ cbq_class_lookup(q, classid)) {
+ NL_SET_ERR_MSG(extack, "Specified class not found");
goto failure;
+ }
} else {
int i;
classid = TC_H_MAKE(sch->handle, 0x8000);
@@ -1556,8 +1575,10 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
break;
}
err = -ENOSR;
- if (i >= 0x8000)
+ if (i >= 0x8000) {
+ NL_SET_ERR_MSG(extack, "Unable to generate classid");
goto failure;
+ }
classid = classid|q->hgenerator;
}
@@ -1565,8 +1586,10 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
if (parentid) {
parent = cbq_class_lookup(q, parentid);
err = -EINVAL;
- if (parent == NULL)
+ if (!parent) {
+ NL_SET_ERR_MSG(extack, "Failed to find parentid");
goto failure;
+ }
}
err = -ENOBUFS;
@@ -1574,7 +1597,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
if (cl == NULL)
goto failure;
- err = tcf_block_get(&cl->block, &cl->filter_list, sch);
+ err = tcf_block_get(&cl->block, &cl->filter_list, sch, extack);
if (err) {
kfree(cl);
return err;
@@ -1586,6 +1609,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
qdisc_root_sleeping_running(sch),
tca[TCA_RATE]);
if (err) {
+ NL_SET_ERR_MSG(extack, "Couldn't create new estimator");
tcf_block_put(cl->block);
kfree(cl);
goto failure;
@@ -1594,7 +1618,8 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
cl->R_tab = rtab;
rtab = NULL;
- cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid);
+ cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid,
+ NULL);
if (!cl->q)
cl->q = &noop_qdisc;
else
@@ -1678,7 +1703,8 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg)
return 0;
}
-static struct tcf_block *cbq_tcf_block(struct Qdisc *sch, unsigned long arg)
+static struct tcf_block *cbq_tcf_block(struct Qdisc *sch, unsigned long arg,
+ struct netlink_ext_ack *extack)
{
struct cbq_sched_data *q = qdisc_priv(sch);
struct cbq_class *cl = (struct cbq_class *)arg;