net: sch: sch_cbq: add extack support
authorAlexander Aring <aring@mojatatu.com>
Wed, 20 Dec 2017 17:35:22 +0000 (12:35 -0500)
committerDavid S. Miller <davem@davemloft.net>
Thu, 21 Dec 2017 17:32:51 +0000 (12:32 -0500)
This patch adds extack support for the cbq qdisc implementation by
adding NL_SET_ERR_MSG in validation of user input.
Also it serves to illustrate a use case of how the infrastructure ops
api changes are to be used by individual qdiscs.

Cc: David Ahern <dsahern@gmail.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Alexander Aring <aring@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/sch_cbq.c

index efe5bf15b031ed787a649bb57c3cd07049348355..f42025d53cfe1e4fcd91931bf14b7328896db1e2 100644 (file)
@@ -1144,15 +1144,19 @@ 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] || !tb[TCA_CBQ_RATE])
+       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]);
 
@@ -1462,24 +1466,32 @@ 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)
+       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]) {
@@ -1496,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;
                        }
@@ -1534,8 +1547,10 @@ 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] || !tb[TCA_CBQ_RATE] || !tb[TCA_CBQ_LSSOPT])
+       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],
                              extack);
@@ -1545,8 +1560,10 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
        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);
@@ -1558,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;
        }
 
@@ -1567,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)
+               if (!parent) {
+                       NL_SET_ERR_MSG(extack, "Failed to find parentid");
                        goto failure;
+               }
        }
 
        err = -ENOBUFS;
@@ -1588,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;