#endif
 
 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,
                                         u32 chain_index, bool create,
         */
        if (is_first_reference && !by_act)
                tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
-                               RTM_NEWCHAIN, false);
+                               RTM_NEWCHAIN, false, NULL);
 
        return chain;
 
                         struct tcf_proto *tp, struct tcf_block *block,
                         struct Qdisc *q, u32 parent, void *fh,
                         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 nlmsghdr  *nlh;
                    tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0)
                        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;
+
        return skb->len;
 
 out_nlmsg_trim:
                          struct nlmsghdr *n, struct tcf_proto *tp,
                          struct tcf_block *block, struct Qdisc *q,
                          u32 parent, void *fh, int event, bool unicast,
-                         bool rtnl_held)
+                         bool rtnl_held, struct netlink_ext_ack *extack)
 {
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
 
        if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
                          n->nlmsg_seq, n->nlmsg_flags, event,
-                         false, rtnl_held) <= 0) {
+                         false, rtnl_held, extack) <= 0) {
                kfree_skb(skb);
                return -EINVAL;
        }
 
        if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
                          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");
                kfree_skb(skb);
                return -EINVAL;
 static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
                                 struct tcf_block *block, struct Qdisc *q,
                                 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;
 
        for (tp = tcf_get_next_proto(chain, NULL);
             tp; tp = tcf_get_next_proto(chain, tp))
-               tfilter_notify(net, oskb, n, tp, block,
-                              q, parent, NULL, event, false, true);
+               tfilter_notify(net, oskb, n, tp, block, q, parent, NULL,
+                              event, false, true, extack);
 }
 
 static void tfilter_put(struct tcf_proto *tp, void *fh)
                              flags, extack);
        if (err == 0) {
                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);
                /* q pointer is NULL for shared blocks */
                if (q)
 
        if (prio == 0) {
                tfilter_notify_chain(net, skb, block, q, parent, n,
-                                    chain, RTM_DELTFILTER);
+                                    chain, RTM_DELTFILTER, extack);
                tcf_chain_flush(chain, rtnl_held);
                err = 0;
                goto errout;
 
                tcf_proto_put(tp, rtnl_held, NULL);
                tfilter_notify(net, skb, n, tp, block, q, parent, fh,
-                              RTM_DELTFILTER, false, rtnl_held);
+                              RTM_DELTFILTER, false, rtnl_held, extack);
                err = 0;
                goto errout;
        }
                err = -ENOENT;
        } else {
                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)
                        NL_SET_ERR_MSG(extack, "Failed to send filter notify message");
        }
        return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
                             n, NETLINK_CB(a->cb->skb).portid,
                             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,
                        if (tcf_fill_node(net, skb, tp, block, q, parent, NULL,
                                          NETLINK_CB(cb->skb).portid,
                                          cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                         RTM_NEWTFILTER, false, true) <= 0)
+                                         RTM_NEWTFILTER, false, true, NULL) <= 0)
                                goto errout;
                        cb->args[1] = 1;
                }
                              void *tmplt_priv, u32 chain_index,
                              struct net *net, struct sk_buff *skb,
                              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);
        const struct tcf_proto_ops *ops;
                        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;
+
        return skb->len;
 
 out_nlmsg_trim:
 }
 
 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;
        struct tcf_block *block = chain->block;
 
        if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
                               chain->index, net, skb, block, portid,
-                              seq, flags, event) <= 0) {
+                              seq, flags, event, extack) <= 0) {
                kfree_skb(skb);
                return -EINVAL;
        }
                return -ENOBUFS;
 
        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);
                return -EINVAL;
        }
                }
 
                tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
-                               RTM_NEWCHAIN, false);
+                               RTM_NEWCHAIN, false, extack);
                break;
        case RTM_DELCHAIN:
                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. */
                tcf_chain_flush(chain, true);
                /* In case the chain was successfully deleted, put a reference
                break;
        case RTM_GETCHAIN:
                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)
                        NL_SET_ERR_MSG(extack, "Failed to send chain notify message");
                break;
                                         chain->index, net, skb, block,
                                         NETLINK_CB(cb->skb).portid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                        RTM_NEWCHAIN);
+                                        RTM_NEWCHAIN, NULL);
                if (err <= 0)
                        break;
                index++;
 
 }
 
 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_queue __percpu *cpu_qstats = NULL;
        if (gnet_stats_finish_copy(&d) < 0)
                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;
+
        return skb->len;
 
 out_nlmsg_trim:
 
 static int qdisc_notify(struct net *net, struct sk_buff *oskb,
                        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;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
 
        if (old && !tc_qdisc_dump_ignore(old, false)) {
                if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq,
-                                 0, RTM_DELQDISC) < 0)
+                                 0, RTM_DELQDISC, extack) < 0)
                        goto err_out;
        }
        if (new && !tc_qdisc_dump_ignore(new, false)) {
                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;
        }
 
 
 static void notify_and_destroy(struct net *net, struct sk_buff *skb,
                               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)
-               qdisc_notify(net, skb, n, clid, old, new);
+               qdisc_notify(net, skb, n, clid, old, new, extack);
 
        if (old)
                qdisc_put(old);
                                qdisc_refcount_inc(new);
                        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)
                                new->ops->attach(new);
                } 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)
                err = cops->graft(parent, cl, new, &old, extack);
                if (err)
                        return err;
-               notify_and_destroy(net, skb, n, classid, old, new);
+               notify_and_destroy(net, skb, n, classid, old, new, extack);
        }
        return 0;
 }
                if (err != 0)
                        return err;
        } else {
-               qdisc_notify(net, skb, n, clid, NULL, q);
+               qdisc_notify(net, skb, n, clid, NULL, q, NULL);
        }
        return 0;
 }
        }
        err = qdisc_change(q, tca, extack);
        if (err == 0)
-               qdisc_notify(net, skb, n, clid, NULL, q);
+               qdisc_notify(net, skb, n, clid, NULL, q, extack);
        return err;
 
 create_n_graft:
                if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
                    tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
                                  cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                 RTM_NEWQDISC) <= 0)
+                                 RTM_NEWQDISC, NULL) <= 0)
                        goto done;
                q_idx++;
        }
                if (!tc_qdisc_dump_ignore(q, dump_invisible) &&
                    tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid,
                                  cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                 RTM_NEWQDISC) <= 0)
+                                 RTM_NEWQDISC, NULL) <= 0)
                        goto done;
                q_idx++;
        }
  ************************************************/
 
 static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q,
-                         unsigned long cl,
-                         u32 portid, u32 seq, u16 flags, int event)
+                         unsigned long cl, u32 portid, u32 seq, u16 flags,
+                         int event, struct netlink_ext_ack *extack)
 {
        struct tcmsg *tcm;
        struct nlmsghdr  *nlh;
        if (gnet_stats_finish_copy(&d) < 0)
                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;
+
        return skb->len;
 
 out_nlmsg_trim:
 
 static int tclass_notify(struct net *net, struct sk_buff *oskb,
                         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;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
        if (!skb)
                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);
                return -EINVAL;
        }
                return -ENOBUFS;
 
        if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0,
-                          RTM_DELTCLASS) < 0) {
+                          RTM_DELTCLASS, extack) < 0) {
                kfree_skb(skb);
                return -EINVAL;
        }
                        tc_bind_tclass(q, portid, clid, 0);
                        goto out;
                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;
                default:
                        err = -EINVAL;
        if (cops->change)
                err = cops->change(q, clid, portid, tca, &new_cl, extack);
        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. */
                if (cl != new_cl)
                        tc_bind_tclass(q, portid, clid, new_cl);
 
        return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).portid,
                              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,