Commit 0349b877 authored by Hangbin Liu's avatar Hangbin Liu Committed by Paolo Abeni

sched: add new attr TCA_EXT_WARN_MSG to report tc extact message

We will report extack message if there is an error via netlink_ack(). But
if the rule is not to be exclusively executed by the hardware, extack is not
passed along and offloading failures don't get logged.

In commit 81c7288b ("sched: cls: enable verbose logging") Marcelo
made cls could log verbose info for offloading failures, which helps
improving Open vSwitch debuggability when using flower offloading.

It would also be helpful if userspace monitor tools, like "tc monitor",
could log this kind of message, as it doesn't require vswitchd log level
adjusment. Let's add a new tc attributes to report the extack message so
the monitor program could receive the failures. e.g.

  # tc monitor
  added chain dev enp3s0f1np1 parent ffff: chain 0
  added filter dev enp3s0f1np1 ingress protocol all pref 49152 flower chain 0 handle 0x1
    ct_state +trk+new
    not_in_hw
          action order 1: gact action drop
           random type none pass val 0
           index 1 ref 1 bind 1

  Warning: mlx5_core: matching on ct_state +new isn't supported.

In this patch I only report the extack message on add/del operations.
It doesn't look like we need to report the extack message on get/dump
operations.

Note this message not only reporte to multicast groups, it could also
be reported unicast, which may affect the current usersapce tool's behaivor.
Suggested-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarHangbin Liu <liuhangbin@gmail.com>
Acked-by: default avatarJakub Kicinski <kuba@kernel.org>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Link: https://lore.kernel.org/r/20230113034353.2766735-1-liuhangbin@gmail.comSigned-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 86ce04f3
...@@ -635,6 +635,7 @@ enum { ...@@ -635,6 +635,7 @@ enum {
TCA_INGRESS_BLOCK, TCA_INGRESS_BLOCK,
TCA_EGRESS_BLOCK, TCA_EGRESS_BLOCK,
TCA_DUMP_FLAGS, TCA_DUMP_FLAGS,
TCA_EXT_WARN_MSG,
__TCA_MAX __TCA_MAX
}; };
......
...@@ -1582,7 +1582,7 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p, ...@@ -1582,7 +1582,7 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p,
static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],
u32 portid, u32 seq, u16 flags, int event, int bind, u32 portid, u32 seq, u16 flags, int event, int bind,
int ref) int ref, struct netlink_ext_ack *extack)
{ {
struct tcamsg *t; struct tcamsg *t;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
...@@ -1606,7 +1606,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[], ...@@ -1606,7 +1606,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
if (extack && extack->_msg &&
nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
goto out_nlmsg_trim;
nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_len = skb_tail_pointer(skb) - b;
return skb->len; return skb->len;
out_nlmsg_trim: out_nlmsg_trim:
...@@ -1625,7 +1630,7 @@ tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, ...@@ -1625,7 +1630,7 @@ tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
if (!skb) if (!skb)
return -ENOBUFS; return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
0, 1) <= 0) { 0, 1, NULL) <= 0) {
NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action"); NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
...@@ -1799,7 +1804,7 @@ tcf_reoffload_del_notify(struct net *net, struct tc_action *action) ...@@ -1799,7 +1804,7 @@ tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
if (!skb) if (!skb)
return -ENOBUFS; return -ENOBUFS;
if (tca_get_fill(skb, actions, 0, 0, 0, RTM_DELACTION, 0, 1) <= 0) { if (tca_get_fill(skb, actions, 0, 0, 0, RTM_DELACTION, 0, 1, NULL) <= 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -1886,7 +1891,7 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[], ...@@ -1886,7 +1891,7 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
return -ENOBUFS; return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION, if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
0, 2) <= 0) { 0, 2, extack) <= 0) {
NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes"); NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes");
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
...@@ -1965,7 +1970,7 @@ tcf_add_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[], ...@@ -1965,7 +1970,7 @@ tcf_add_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
return -ENOBUFS; return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags, if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
RTM_NEWACTION, 0, 0) <= 0) { RTM_NEWACTION, 0, 0, extack) <= 0) {
NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action"); NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
......
...@@ -488,7 +488,8 @@ static struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block, ...@@ -488,7 +488,8 @@ static struct tcf_chain *tcf_chain_lookup_rcu(const struct tcf_block *block,
#endif #endif
static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
u32 seq, u16 flags, int event, bool unicast); u32 seq, u16 flags, int event, bool unicast,
struct netlink_ext_ack *extack);
static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, static struct tcf_chain *__tcf_chain_get(struct tcf_block *block,
u32 chain_index, bool create, u32 chain_index, bool create,
...@@ -521,7 +522,7 @@ static struct tcf_chain *__tcf_chain_get(struct tcf_block *block, ...@@ -521,7 +522,7 @@ static struct tcf_chain *__tcf_chain_get(struct tcf_block *block,
*/ */
if (is_first_reference && !by_act) if (is_first_reference && !by_act)
tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
RTM_NEWCHAIN, false); RTM_NEWCHAIN, false, NULL);
return chain; return chain;
...@@ -1817,7 +1818,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, ...@@ -1817,7 +1818,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
struct tcf_proto *tp, struct tcf_block *block, struct tcf_proto *tp, struct tcf_block *block,
struct Qdisc *q, u32 parent, void *fh, struct Qdisc *q, u32 parent, void *fh,
u32 portid, u32 seq, u16 flags, int event, u32 portid, u32 seq, u16 flags, int event,
bool terse_dump, bool rtnl_held) bool terse_dump, bool rtnl_held,
struct netlink_ext_ack *extack)
{ {
struct tcmsg *tcm; struct tcmsg *tcm;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
...@@ -1857,7 +1859,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, ...@@ -1857,7 +1859,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0) tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0)
goto nla_put_failure; goto nla_put_failure;
} }
if (extack && extack->_msg &&
nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
goto nla_put_failure;
nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_len = skb_tail_pointer(skb) - b;
return skb->len; return skb->len;
out_nlmsg_trim: out_nlmsg_trim:
...@@ -1871,7 +1879,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, ...@@ -1871,7 +1879,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct tcf_proto *tp, struct nlmsghdr *n, struct tcf_proto *tp,
struct tcf_block *block, struct Qdisc *q, struct tcf_block *block, struct Qdisc *q,
u32 parent, void *fh, int event, bool unicast, u32 parent, void *fh, int event, bool unicast,
bool rtnl_held) bool rtnl_held, struct netlink_ext_ack *extack)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
...@@ -1883,7 +1891,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, ...@@ -1883,7 +1891,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
n->nlmsg_seq, n->nlmsg_flags, event, n->nlmsg_seq, n->nlmsg_flags, event,
false, rtnl_held) <= 0) { false, rtnl_held, extack) <= 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -1912,7 +1920,7 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, ...@@ -1912,7 +1920,7 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid, if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER, n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER,
false, rtnl_held) <= 0) { false, rtnl_held, extack) <= 0) {
NL_SET_ERR_MSG(extack, "Failed to build del event notification"); NL_SET_ERR_MSG(extack, "Failed to build del event notification");
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
...@@ -1938,14 +1946,15 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, ...@@ -1938,14 +1946,15 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
struct tcf_block *block, struct Qdisc *q, struct tcf_block *block, struct Qdisc *q,
u32 parent, struct nlmsghdr *n, u32 parent, struct nlmsghdr *n,
struct tcf_chain *chain, int event) struct tcf_chain *chain, int event,
struct netlink_ext_ack *extack)
{ {
struct tcf_proto *tp; struct tcf_proto *tp;
for (tp = tcf_get_next_proto(chain, NULL); for (tp = tcf_get_next_proto(chain, NULL);
tp; tp = tcf_get_next_proto(chain, tp)) tp; tp = tcf_get_next_proto(chain, tp))
tfilter_notify(net, oskb, n, tp, block, tfilter_notify(net, oskb, n, tp, block, q, parent, NULL,
q, parent, NULL, event, false, true); event, false, true, extack);
} }
static void tfilter_put(struct tcf_proto *tp, void *fh) static void tfilter_put(struct tcf_proto *tp, void *fh)
...@@ -2156,7 +2165,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2156,7 +2165,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
flags, extack); flags, extack);
if (err == 0) { if (err == 0) {
tfilter_notify(net, skb, n, tp, block, q, parent, fh, tfilter_notify(net, skb, n, tp, block, q, parent, fh,
RTM_NEWTFILTER, false, rtnl_held); RTM_NEWTFILTER, false, rtnl_held, extack);
tfilter_put(tp, fh); tfilter_put(tp, fh);
/* q pointer is NULL for shared blocks */ /* q pointer is NULL for shared blocks */
if (q) if (q)
...@@ -2284,7 +2293,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2284,7 +2293,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
if (prio == 0) { if (prio == 0) {
tfilter_notify_chain(net, skb, block, q, parent, n, tfilter_notify_chain(net, skb, block, q, parent, n,
chain, RTM_DELTFILTER); chain, RTM_DELTFILTER, extack);
tcf_chain_flush(chain, rtnl_held); tcf_chain_flush(chain, rtnl_held);
err = 0; err = 0;
goto errout; goto errout;
...@@ -2308,7 +2317,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2308,7 +2317,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
tcf_proto_put(tp, rtnl_held, NULL); tcf_proto_put(tp, rtnl_held, NULL);
tfilter_notify(net, skb, n, tp, block, q, parent, fh, tfilter_notify(net, skb, n, tp, block, q, parent, fh,
RTM_DELTFILTER, false, rtnl_held); RTM_DELTFILTER, false, rtnl_held, extack);
err = 0; err = 0;
goto errout; goto errout;
} }
...@@ -2452,7 +2461,7 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2452,7 +2461,7 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
err = -ENOENT; err = -ENOENT;
} else { } else {
err = tfilter_notify(net, skb, n, tp, block, q, parent, err = tfilter_notify(net, skb, n, tp, block, q, parent,
fh, RTM_NEWTFILTER, true, rtnl_held); fh, RTM_NEWTFILTER, true, rtnl_held, NULL);
if (err < 0) if (err < 0)
NL_SET_ERR_MSG(extack, "Failed to send filter notify message"); NL_SET_ERR_MSG(extack, "Failed to send filter notify message");
} }
...@@ -2490,7 +2499,7 @@ static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) ...@@ -2490,7 +2499,7 @@ static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent, return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
n, NETLINK_CB(a->cb->skb).portid, n, NETLINK_CB(a->cb->skb).portid,
a->cb->nlh->nlmsg_seq, NLM_F_MULTI, a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWTFILTER, a->terse_dump, true); RTM_NEWTFILTER, a->terse_dump, true, NULL);
} }
static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
...@@ -2524,7 +2533,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, ...@@ -2524,7 +2533,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
if (tcf_fill_node(net, skb, tp, block, q, parent, NULL, if (tcf_fill_node(net, skb, tp, block, q, parent, NULL,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWTFILTER, false, true) <= 0) RTM_NEWTFILTER, false, true, NULL) <= 0)
goto errout; goto errout;
cb->args[1] = 1; cb->args[1] = 1;
} }
...@@ -2667,7 +2676,8 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, ...@@ -2667,7 +2676,8 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
void *tmplt_priv, u32 chain_index, void *tmplt_priv, u32 chain_index,
struct net *net, struct sk_buff *skb, struct net *net, struct sk_buff *skb,
struct tcf_block *block, struct tcf_block *block,
u32 portid, u32 seq, u16 flags, int event) u32 portid, u32 seq, u16 flags, int event,
struct netlink_ext_ack *extack)
{ {
unsigned char *b = skb_tail_pointer(skb); unsigned char *b = skb_tail_pointer(skb);
const struct tcf_proto_ops *ops; const struct tcf_proto_ops *ops;
...@@ -2704,7 +2714,12 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, ...@@ -2704,7 +2714,12 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
goto nla_put_failure; goto nla_put_failure;
} }
if (extack && extack->_msg &&
nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
goto out_nlmsg_trim;
nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_len = skb_tail_pointer(skb) - b;
return skb->len; return skb->len;
out_nlmsg_trim: out_nlmsg_trim:
...@@ -2714,7 +2729,8 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops, ...@@ -2714,7 +2729,8 @@ static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
} }
static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
u32 seq, u16 flags, int event, bool unicast) u32 seq, u16 flags, int event, bool unicast,
struct netlink_ext_ack *extack)
{ {
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
struct tcf_block *block = chain->block; struct tcf_block *block = chain->block;
...@@ -2728,7 +2744,7 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb, ...@@ -2728,7 +2744,7 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv, if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
chain->index, net, skb, block, portid, chain->index, net, skb, block, portid,
seq, flags, event) <= 0) { seq, flags, event, extack) <= 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -2756,7 +2772,7 @@ static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops, ...@@ -2756,7 +2772,7 @@ static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
return -ENOBUFS; return -ENOBUFS;
if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb, if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb,
block, portid, seq, flags, RTM_DELCHAIN) <= 0) { block, portid, seq, flags, RTM_DELCHAIN, NULL) <= 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -2908,11 +2924,11 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2908,11 +2924,11 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
} }
tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL, tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
RTM_NEWCHAIN, false); RTM_NEWCHAIN, false, extack);
break; break;
case RTM_DELCHAIN: case RTM_DELCHAIN:
tfilter_notify_chain(net, skb, block, q, parent, n, tfilter_notify_chain(net, skb, block, q, parent, n,
chain, RTM_DELTFILTER); chain, RTM_DELTFILTER, extack);
/* Flush the chain first as the user requested chain removal. */ /* Flush the chain first as the user requested chain removal. */
tcf_chain_flush(chain, true); tcf_chain_flush(chain, true);
/* In case the chain was successfully deleted, put a reference /* In case the chain was successfully deleted, put a reference
...@@ -2922,7 +2938,7 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2922,7 +2938,7 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
break; break;
case RTM_GETCHAIN: case RTM_GETCHAIN:
err = tc_chain_notify(chain, skb, n->nlmsg_seq, err = tc_chain_notify(chain, skb, n->nlmsg_seq,
n->nlmsg_flags, n->nlmsg_type, true); n->nlmsg_flags, n->nlmsg_type, true, extack);
if (err < 0) if (err < 0)
NL_SET_ERR_MSG(extack, "Failed to send chain notify message"); NL_SET_ERR_MSG(extack, "Failed to send chain notify message");
break; break;
...@@ -3022,7 +3038,7 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3022,7 +3038,7 @@ static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
chain->index, net, skb, block, chain->index, net, skb, block,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWCHAIN); RTM_NEWCHAIN, NULL);
if (err <= 0) if (err <= 0)
break; break;
index++; index++;
......
...@@ -902,7 +902,8 @@ static void qdisc_offload_graft_root(struct net_device *dev, ...@@ -902,7 +902,8 @@ static void qdisc_offload_graft_root(struct net_device *dev,
} }
static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
u32 portid, u32 seq, u16 flags, int event) u32 portid, u32 seq, u16 flags, int event,
struct netlink_ext_ack *extack)
{ {
struct gnet_stats_basic_sync __percpu *cpu_bstats = NULL; struct gnet_stats_basic_sync __percpu *cpu_bstats = NULL;
struct gnet_stats_queue __percpu *cpu_qstats = NULL; struct gnet_stats_queue __percpu *cpu_qstats = NULL;
...@@ -970,7 +971,12 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, ...@@ -970,7 +971,12 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
if (gnet_stats_finish_copy(&d) < 0) if (gnet_stats_finish_copy(&d) < 0)
goto nla_put_failure; goto nla_put_failure;
if (extack && extack->_msg &&
nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
goto out_nlmsg_trim;
nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_len = skb_tail_pointer(skb) - b;
return skb->len; return skb->len;
out_nlmsg_trim: out_nlmsg_trim:
...@@ -991,7 +997,8 @@ static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) ...@@ -991,7 +997,8 @@ static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible)
static int qdisc_notify(struct net *net, struct sk_buff *oskb, static int qdisc_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, u32 clid, struct nlmsghdr *n, u32 clid,
struct Qdisc *old, struct Qdisc *new) struct Qdisc *old, struct Qdisc *new,
struct netlink_ext_ack *extack)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
...@@ -1002,12 +1009,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, ...@@ -1002,12 +1009,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
if (old && !tc_qdisc_dump_ignore(old, false)) { if (old && !tc_qdisc_dump_ignore(old, false)) {
if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq,
0, RTM_DELQDISC) < 0) 0, RTM_DELQDISC, extack) < 0)
goto err_out; goto err_out;
} }
if (new && !tc_qdisc_dump_ignore(new, false)) { if (new && !tc_qdisc_dump_ignore(new, false)) {
if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq,
old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) old ? NLM_F_REPLACE : 0, RTM_NEWQDISC, extack) < 0)
goto err_out; goto err_out;
} }
...@@ -1022,10 +1029,11 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, ...@@ -1022,10 +1029,11 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb,
static void notify_and_destroy(struct net *net, struct sk_buff *skb, static void notify_and_destroy(struct net *net, struct sk_buff *skb,
struct nlmsghdr *n, u32 clid, struct nlmsghdr *n, u32 clid,
struct Qdisc *old, struct Qdisc *new) struct Qdisc *old, struct Qdisc *new,
struct netlink_ext_ack *extack)
{ {
if (new || old) if (new || old)
qdisc_notify(net, skb, n, clid, old, new); qdisc_notify(net, skb, n, clid, old, new, extack);
if (old) if (old)
qdisc_put(old); qdisc_put(old);
...@@ -1105,12 +1113,12 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, ...@@ -1105,12 +1113,12 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
qdisc_refcount_inc(new); qdisc_refcount_inc(new);
rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc); rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc);
notify_and_destroy(net, skb, n, classid, old, new); notify_and_destroy(net, skb, n, classid, old, new, extack);
if (new && new->ops->attach) if (new && new->ops->attach)
new->ops->attach(new); new->ops->attach(new);
} else { } else {
notify_and_destroy(net, skb, n, classid, old, new); notify_and_destroy(net, skb, n, classid, old, new, extack);
} }
if (dev->flags & IFF_UP) if (dev->flags & IFF_UP)
...@@ -1141,7 +1149,7 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, ...@@ -1141,7 +1149,7 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent,
err = cops->graft(parent, cl, new, &old, extack); err = cops->graft(parent, cl, new, &old, extack);
if (err) if (err)
return err; return err;
notify_and_destroy(net, skb, n, classid, old, new); notify_and_destroy(net, skb, n, classid, old, new, extack);
} }
return 0; return 0;
} }
...@@ -1509,7 +1517,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1509,7 +1517,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
if (err != 0) if (err != 0)
return err; return err;
} else { } else {
qdisc_notify(net, skb, n, clid, NULL, q); qdisc_notify(net, skb, n, clid, NULL, q, NULL);
} }
return 0; return 0;
} }
...@@ -1648,7 +1656,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1648,7 +1656,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
} }
err = qdisc_change(q, tca, extack); err = qdisc_change(q, tca, extack);
if (err == 0) if (err == 0)
qdisc_notify(net, skb, n, clid, NULL, q); qdisc_notify(net, skb, n, clid, NULL, q, extack);
return err; return err;
create_n_graft: create_n_graft:
...@@ -1715,7 +1723,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, ...@@ -1715,7 +1723,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
if (!tc_qdisc_dump_ignore(q, dump_invisible) && if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC) <= 0) RTM_NEWQDISC, NULL) <= 0)
goto done; goto done;
q_idx++; q_idx++;
} }
...@@ -1737,7 +1745,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, ...@@ -1737,7 +1745,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb,
if (!tc_qdisc_dump_ignore(q, dump_invisible) && if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWQDISC) <= 0) RTM_NEWQDISC, NULL) <= 0)
goto done; goto done;
q_idx++; q_idx++;
} }
...@@ -1810,8 +1818,8 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1810,8 +1818,8 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb)
************************************************/ ************************************************/
static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
unsigned long cl, unsigned long cl, u32 portid, u32 seq, u16 flags,
u32 portid, u32 seq, u16 flags, int event) int event, struct netlink_ext_ack *extack)
{ {
struct tcmsg *tcm; struct tcmsg *tcm;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
...@@ -1846,7 +1854,12 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, ...@@ -1846,7 +1854,12 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
if (gnet_stats_finish_copy(&d) < 0) if (gnet_stats_finish_copy(&d) < 0)
goto nla_put_failure; goto nla_put_failure;
if (extack && extack->_msg &&
nla_put_string(skb, TCA_EXT_WARN_MSG, extack->_msg))
goto out_nlmsg_trim;
nlh->nlmsg_len = skb_tail_pointer(skb) - b; nlh->nlmsg_len = skb_tail_pointer(skb) - b;
return skb->len; return skb->len;
out_nlmsg_trim: out_nlmsg_trim:
...@@ -1857,7 +1870,7 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, ...@@ -1857,7 +1870,7 @@ static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
static int tclass_notify(struct net *net, struct sk_buff *oskb, static int tclass_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct Qdisc *q, struct nlmsghdr *n, struct Qdisc *q,
unsigned long cl, int event) unsigned long cl, int event, struct netlink_ext_ack *extack)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
...@@ -1866,7 +1879,7 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb, ...@@ -1866,7 +1879,7 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb,
if (!skb) if (!skb)
return -ENOBUFS; return -ENOBUFS;
if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) { if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event, extack) < 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -1893,7 +1906,7 @@ static int tclass_del_notify(struct net *net, ...@@ -1893,7 +1906,7 @@ static int tclass_del_notify(struct net *net,
return -ENOBUFS; return -ENOBUFS;
if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
RTM_DELTCLASS) < 0) { RTM_DELTCLASS, extack) < 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -2100,7 +2113,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2100,7 +2113,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
tc_bind_tclass(q, portid, clid, 0); tc_bind_tclass(q, portid, clid, 0);
goto out; goto out;
case RTM_GETTCLASS: case RTM_GETTCLASS:
err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS); err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS, extack);
goto out; goto out;
default: default:
err = -EINVAL; err = -EINVAL;
...@@ -2118,7 +2131,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -2118,7 +2131,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
if (cops->change) if (cops->change)
err = cops->change(q, clid, portid, tca, &new_cl, extack); err = cops->change(q, clid, portid, tca, &new_cl, extack);
if (err == 0) { if (err == 0) {
tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS); tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS, extack);
/* We just create a new class, need to do reverse binding. */ /* We just create a new class, need to do reverse binding. */
if (cl != new_cl) if (cl != new_cl)
tc_bind_tclass(q, portid, clid, new_cl); tc_bind_tclass(q, portid, clid, new_cl);
...@@ -2140,7 +2153,7 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, ...@@ -2140,7 +2153,7 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl,
return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid, return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid,
a->cb->nlh->nlmsg_seq, NLM_F_MULTI, a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWTCLASS); RTM_NEWTCLASS, NULL);
} }
static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment