mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 14:12:06 +00:00
net: sched: sch_api: handle generic qdisc errors
This patch adds extack support for generic qdisc handling. The extack will be set deeper to each called function which is not part of netdev core api. 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>
This commit is contained in:
parent
ac8ef4ab73
commit
0921559811
@ -449,7 +449,8 @@ static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = {
|
||||
[TCA_STAB_DATA] = { .type = NLA_BINARY },
|
||||
};
|
||||
|
||||
static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
|
||||
static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nlattr *tb[TCA_STAB_MAX + 1];
|
||||
struct qdisc_size_table *stab;
|
||||
@ -458,23 +459,29 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt)
|
||||
u16 *tab = NULL;
|
||||
int err;
|
||||
|
||||
err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy, NULL);
|
||||
err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy, extack);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
if (!tb[TCA_STAB_BASE])
|
||||
if (!tb[TCA_STAB_BASE]) {
|
||||
NL_SET_ERR_MSG(extack, "Size table base attribute is missing");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
s = nla_data(tb[TCA_STAB_BASE]);
|
||||
|
||||
if (s->tsize > 0) {
|
||||
if (!tb[TCA_STAB_DATA])
|
||||
if (!tb[TCA_STAB_DATA]) {
|
||||
NL_SET_ERR_MSG(extack, "Size table data attribute is missing");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
tab = nla_data(tb[TCA_STAB_DATA]);
|
||||
tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16);
|
||||
}
|
||||
|
||||
if (tsize != s->tsize || (!tab && tsize > 0))
|
||||
if (tsize != s->tsize || (!tab && tsize > 0)) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid size of size table");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
list_for_each_entry(stab, &qdisc_stab_list, list) {
|
||||
if (memcmp(&stab->szopts, s, sizeof(*s)))
|
||||
@ -899,7 +906,8 @@ static void notify_and_destroy(struct net *net, struct sk_buff *skb,
|
||||
|
||||
static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
|
||||
struct sk_buff *skb, struct nlmsghdr *n, u32 classid,
|
||||
struct Qdisc *new, struct Qdisc *old)
|
||||
struct Qdisc *new, struct Qdisc *old,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct Qdisc *q = old;
|
||||
struct net *net = dev_net(dev);
|
||||
@ -914,8 +922,10 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
|
||||
(new && new->flags & TCQ_F_INGRESS)) {
|
||||
num_q = 1;
|
||||
ingress = 1;
|
||||
if (!dev_ingress_queue(dev))
|
||||
if (!dev_ingress_queue(dev)) {
|
||||
NL_SET_ERR_MSG(extack, "Device does not have an ingress queue");
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->flags & IFF_UP)
|
||||
@ -966,10 +976,12 @@ skip:
|
||||
if (cops && cops->graft) {
|
||||
unsigned long cl = cops->find(parent, classid);
|
||||
|
||||
if (cl)
|
||||
if (cl) {
|
||||
err = cops->graft(parent, cl, new, &old);
|
||||
else
|
||||
} else {
|
||||
NL_SET_ERR_MSG(extack, "Specified class not found");
|
||||
err = -ENOENT;
|
||||
}
|
||||
}
|
||||
if (!err)
|
||||
notify_and_destroy(net, skb, n, classid, old, new);
|
||||
@ -990,7 +1002,8 @@ static struct lock_class_key qdisc_rx_lock;
|
||||
static struct Qdisc *qdisc_create(struct net_device *dev,
|
||||
struct netdev_queue *dev_queue,
|
||||
struct Qdisc *p, u32 parent, u32 handle,
|
||||
struct nlattr **tca, int *errp)
|
||||
struct nlattr **tca, int *errp,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err;
|
||||
struct nlattr *kind = tca[TCA_KIND];
|
||||
@ -1028,8 +1041,10 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
|
||||
#endif
|
||||
|
||||
err = -ENOENT;
|
||||
if (!ops)
|
||||
if (!ops) {
|
||||
NL_SET_ERR_MSG(extack, "Specified qdisc not found");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
sch = qdisc_alloc(dev_queue, ops);
|
||||
if (IS_ERR(sch)) {
|
||||
@ -1086,7 +1101,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
|
||||
}
|
||||
|
||||
if (tca[TCA_STAB]) {
|
||||
stab = qdisc_get_stab(tca[TCA_STAB]);
|
||||
stab = qdisc_get_stab(tca[TCA_STAB], extack);
|
||||
if (IS_ERR(stab)) {
|
||||
err = PTR_ERR(stab);
|
||||
goto err_out4;
|
||||
@ -1097,8 +1112,10 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
|
||||
seqcount_t *running;
|
||||
|
||||
err = -EOPNOTSUPP;
|
||||
if (sch->flags & TCQ_F_MQROOT)
|
||||
if (sch->flags & TCQ_F_MQROOT) {
|
||||
NL_SET_ERR_MSG(extack, "Cannot attach rate estimator to a multi-queue root qdisc");
|
||||
goto err_out4;
|
||||
}
|
||||
|
||||
if (sch->parent != TC_H_ROOT &&
|
||||
!(sch->flags & TCQ_F_INGRESS) &&
|
||||
@ -1113,8 +1130,10 @@ static struct Qdisc *qdisc_create(struct net_device *dev,
|
||||
NULL,
|
||||
running,
|
||||
tca[TCA_RATE]);
|
||||
if (err)
|
||||
if (err) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to generate new estimator");
|
||||
goto err_out4;
|
||||
}
|
||||
}
|
||||
|
||||
qdisc_hash_add(sch, false);
|
||||
@ -1147,21 +1166,24 @@ err_out4:
|
||||
goto err_out3;
|
||||
}
|
||||
|
||||
static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
|
||||
static int qdisc_change(struct Qdisc *sch, struct nlattr **tca,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct qdisc_size_table *ostab, *stab = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (tca[TCA_OPTIONS]) {
|
||||
if (!sch->ops->change)
|
||||
if (!sch->ops->change) {
|
||||
NL_SET_ERR_MSG(extack, "Change operation not supported by specified qdisc");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = sch->ops->change(sch, tca[TCA_OPTIONS]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (tca[TCA_STAB]) {
|
||||
stab = qdisc_get_stab(tca[TCA_STAB]);
|
||||
stab = qdisc_get_stab(tca[TCA_STAB], extack);
|
||||
if (IS_ERR(stab))
|
||||
return PTR_ERR(stab);
|
||||
}
|
||||
@ -1259,8 +1281,10 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
|
||||
if (clid != TC_H_ROOT) {
|
||||
if (TC_H_MAJ(clid) != TC_H_MAJ(TC_H_INGRESS)) {
|
||||
p = qdisc_lookup(dev, TC_H_MAJ(clid));
|
||||
if (!p)
|
||||
if (!p) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified classid");
|
||||
return -ENOENT;
|
||||
}
|
||||
q = qdisc_leaf(p, clid);
|
||||
} else if (dev_ingress_queue(dev)) {
|
||||
q = dev_ingress_queue(dev)->qdisc_sleeping;
|
||||
@ -1268,26 +1292,38 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
|
||||
} else {
|
||||
q = dev->qdisc;
|
||||
}
|
||||
if (!q)
|
||||
if (!q) {
|
||||
NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (tcm->tcm_handle && q->handle != tcm->tcm_handle)
|
||||
if (tcm->tcm_handle && q->handle != tcm->tcm_handle) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid handle");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
q = qdisc_lookup(dev, tcm->tcm_handle);
|
||||
if (!q)
|
||||
if (!q) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to find qdisc with specified handle");
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
|
||||
if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id)) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid qdisc name");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (n->nlmsg_type == RTM_DELQDISC) {
|
||||
if (!clid)
|
||||
if (!clid) {
|
||||
NL_SET_ERR_MSG(extack, "Classid cannot be zero");
|
||||
return -EINVAL;
|
||||
if (q->handle == 0)
|
||||
}
|
||||
if (q->handle == 0) {
|
||||
NL_SET_ERR_MSG(extack, "Cannot delete qdisc with handle of zero");
|
||||
return -ENOENT;
|
||||
err = qdisc_graft(dev, p, skb, n, clid, NULL, q);
|
||||
}
|
||||
err = qdisc_graft(dev, p, skb, n, clid, NULL, q, extack);
|
||||
if (err != 0)
|
||||
return err;
|
||||
} else {
|
||||
@ -1333,8 +1369,10 @@ replay:
|
||||
if (clid != TC_H_ROOT) {
|
||||
if (clid != TC_H_INGRESS) {
|
||||
p = qdisc_lookup(dev, TC_H_MAJ(clid));
|
||||
if (!p)
|
||||
if (!p) {
|
||||
NL_SET_ERR_MSG(extack, "Failed to find specified qdisc");
|
||||
return -ENOENT;
|
||||
}
|
||||
q = qdisc_leaf(p, clid);
|
||||
} else if (dev_ingress_queue_create(dev)) {
|
||||
q = dev_ingress_queue(dev)->qdisc_sleeping;
|
||||
@ -1349,21 +1387,33 @@ replay:
|
||||
|
||||
if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
|
||||
if (tcm->tcm_handle) {
|
||||
if (q && !(n->nlmsg_flags & NLM_F_REPLACE))
|
||||
if (q && !(n->nlmsg_flags & NLM_F_REPLACE)) {
|
||||
NL_SET_ERR_MSG(extack, "NLM_F_REPLACE needed to override");
|
||||
return -EEXIST;
|
||||
if (TC_H_MIN(tcm->tcm_handle))
|
||||
}
|
||||
if (TC_H_MIN(tcm->tcm_handle)) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid minor handle");
|
||||
return -EINVAL;
|
||||
}
|
||||
q = qdisc_lookup(dev, tcm->tcm_handle);
|
||||
if (!q)
|
||||
if (!q) {
|
||||
NL_SET_ERR_MSG(extack, "No qdisc found for specified handle");
|
||||
goto create_n_graft;
|
||||
if (n->nlmsg_flags & NLM_F_EXCL)
|
||||
}
|
||||
if (n->nlmsg_flags & NLM_F_EXCL) {
|
||||
NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot override");
|
||||
return -EEXIST;
|
||||
}
|
||||
if (tca[TCA_KIND] &&
|
||||
nla_strcmp(tca[TCA_KIND], q->ops->id))
|
||||
nla_strcmp(tca[TCA_KIND], q->ops->id)) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid qdisc name");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (q == p ||
|
||||
(p && check_loop(q, p, 0)))
|
||||
(p && check_loop(q, p, 0))) {
|
||||
NL_SET_ERR_MSG(extack, "Qdisc parent/child loop detected");
|
||||
return -ELOOP;
|
||||
}
|
||||
qdisc_refcount_inc(q);
|
||||
goto graft;
|
||||
} else {
|
||||
@ -1398,33 +1448,45 @@ replay:
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!tcm->tcm_handle)
|
||||
if (!tcm->tcm_handle) {
|
||||
NL_SET_ERR_MSG(extack, "Handle cannot be zero");
|
||||
return -EINVAL;
|
||||
}
|
||||
q = qdisc_lookup(dev, tcm->tcm_handle);
|
||||
}
|
||||
|
||||
/* Change qdisc parameters */
|
||||
if (!q)
|
||||
if (!q) {
|
||||
NL_SET_ERR_MSG(extack, "Specified qdisc not found");
|
||||
return -ENOENT;
|
||||
if (n->nlmsg_flags & NLM_F_EXCL)
|
||||
}
|
||||
if (n->nlmsg_flags & NLM_F_EXCL) {
|
||||
NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot modify");
|
||||
return -EEXIST;
|
||||
if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id))
|
||||
}
|
||||
if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], q->ops->id)) {
|
||||
NL_SET_ERR_MSG(extack, "Invalid qdisc name");
|
||||
return -EINVAL;
|
||||
err = qdisc_change(q, tca);
|
||||
}
|
||||
err = qdisc_change(q, tca, extack);
|
||||
if (err == 0)
|
||||
qdisc_notify(net, skb, n, clid, NULL, q);
|
||||
return err;
|
||||
|
||||
create_n_graft:
|
||||
if (!(n->nlmsg_flags & NLM_F_CREATE))
|
||||
if (!(n->nlmsg_flags & NLM_F_CREATE)) {
|
||||
NL_SET_ERR_MSG(extack, "Qdisc not found. To create specify NLM_F_CREATE flag");
|
||||
return -ENOENT;
|
||||
}
|
||||
if (clid == TC_H_INGRESS) {
|
||||
if (dev_ingress_queue(dev))
|
||||
if (dev_ingress_queue(dev)) {
|
||||
q = qdisc_create(dev, dev_ingress_queue(dev), p,
|
||||
tcm->tcm_parent, tcm->tcm_parent,
|
||||
tca, &err);
|
||||
else
|
||||
tca, &err, extack);
|
||||
} else {
|
||||
NL_SET_ERR_MSG(extack, "Cannot find ingress queue for specified device");
|
||||
err = -ENOENT;
|
||||
}
|
||||
} else {
|
||||
struct netdev_queue *dev_queue;
|
||||
|
||||
@ -1437,7 +1499,7 @@ create_n_graft:
|
||||
|
||||
q = qdisc_create(dev, dev_queue, p,
|
||||
tcm->tcm_parent, tcm->tcm_handle,
|
||||
tca, &err);
|
||||
tca, &err, extack);
|
||||
}
|
||||
if (q == NULL) {
|
||||
if (err == -EAGAIN)
|
||||
@ -1446,7 +1508,7 @@ create_n_graft:
|
||||
}
|
||||
|
||||
graft:
|
||||
err = qdisc_graft(dev, p, skb, n, clid, q, NULL);
|
||||
err = qdisc_graft(dev, p, skb, n, clid, q, NULL, extack);
|
||||
if (err) {
|
||||
if (q)
|
||||
qdisc_destroy(q);
|
||||
|
Loading…
Reference in New Issue
Block a user