Merge branch 'extack-nonfatal'

David Ahern says:

====================
net: Allow non-fatal messages to be passed in extack

There are many cases where networking subsystems throw non-fatal warning
messages that end up in dmesg / kernel log to which a user making the
change is completely oblivious. This set makes the extack facility
usable for returning such messages.

The case in point here is spectrum and adding FIB rules which causes an
offload abort. Make the use case more user friendly by letting the user
know that offload is no longer happening because of the rule change.

v2
- kept the offload abort in a work queue entry per Ido's comment
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-11-01 11:50:43 +09:00
commit 985b6e6d03
6 changed files with 99 additions and 65 deletions

View File

@ -5234,7 +5234,6 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
struct fib_rule *rule;
bool replace, append;
int err;
@ -5256,12 +5255,11 @@ static void mlxsw_sp_router_fib4_event_work(struct work_struct *work)
mlxsw_sp_router_fib4_del(mlxsw_sp, &fib_work->fen_info);
fib_info_put(fib_work->fen_info.fi);
break;
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
rule = fib_work->fr_info.rule;
if (!fib4_rule_default(rule) && !rule->l3mdev)
mlxsw_sp_router_fib_abort(mlxsw_sp);
fib_rule_put(rule);
case FIB_EVENT_RULE_ADD:
/* if we get here, a rule was added that we do not support.
* just do the fib_abort
*/
mlxsw_sp_router_fib_abort(mlxsw_sp);
break;
case FIB_EVENT_NH_ADD: /* fall through */
case FIB_EVENT_NH_DEL:
@ -5279,7 +5277,6 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
struct fib_rule *rule;
bool replace;
int err;
@ -5298,12 +5295,11 @@ static void mlxsw_sp_router_fib6_event_work(struct work_struct *work)
mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt);
mlxsw_sp_rt6_release(fib_work->fen6_info.rt);
break;
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
rule = fib_work->fr_info.rule;
if (!fib6_rule_default(rule) && !rule->l3mdev)
mlxsw_sp_router_fib_abort(mlxsw_sp);
fib_rule_put(rule);
case FIB_EVENT_RULE_ADD:
/* if we get here, a rule was added that we do not support.
* just do the fib_abort
*/
mlxsw_sp_router_fib_abort(mlxsw_sp);
break;
}
rtnl_unlock();
@ -5315,7 +5311,6 @@ static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
struct mlxsw_sp_fib_event_work *fib_work =
container_of(work, struct mlxsw_sp_fib_event_work, work);
struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp;
struct fib_rule *rule;
bool replace;
int err;
@ -5347,12 +5342,11 @@ static void mlxsw_sp_router_fibmr_event_work(struct work_struct *work)
&fib_work->ven_info);
dev_put(fib_work->ven_info.dev);
break;
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
rule = fib_work->fr_info.rule;
if (!ipmr_rule_default(rule) && !rule->l3mdev)
mlxsw_sp_router_fib_abort(mlxsw_sp);
fib_rule_put(rule);
case FIB_EVENT_RULE_ADD:
/* if we get here, a rule was added that we do not support.
* just do the fib_abort
*/
mlxsw_sp_router_fib_abort(mlxsw_sp);
break;
}
rtnl_unlock();
@ -5363,7 +5357,6 @@ static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
struct fib_notifier_info *info)
{
struct fib_entry_notifier_info *fen_info;
struct fib_rule_notifier_info *fr_info;
struct fib_nh_notifier_info *fnh_info;
switch (fib_work->event) {
@ -5379,13 +5372,6 @@ static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work,
*/
fib_info_hold(fib_work->fen_info.fi);
break;
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
fr_info = container_of(info, struct fib_rule_notifier_info,
info);
fib_work->fr_info = *fr_info;
fib_rule_get(fib_work->fr_info.rule);
break;
case FIB_EVENT_NH_ADD: /* fall through */
case FIB_EVENT_NH_DEL:
fnh_info = container_of(info, struct fib_nh_notifier_info,
@ -5400,7 +5386,6 @@ static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
struct fib_notifier_info *info)
{
struct fib6_entry_notifier_info *fen6_info;
struct fib_rule_notifier_info *fr_info;
switch (fib_work->event) {
case FIB_EVENT_ENTRY_REPLACE: /* fall through */
@ -5411,13 +5396,6 @@ static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work,
fib_work->fen6_info = *fen6_info;
rt6_hold(fib_work->fen6_info.rt);
break;
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
fr_info = container_of(info, struct fib_rule_notifier_info,
info);
fib_work->fr_info = *fr_info;
fib_rule_get(fib_work->fr_info.rule);
break;
}
}
@ -5437,12 +5415,47 @@ mlxsw_sp_router_fibmr_event(struct mlxsw_sp_fib_event_work *fib_work,
memcpy(&fib_work->ven_info, info, sizeof(fib_work->ven_info));
dev_hold(fib_work->ven_info.dev);
break;
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info));
fib_rule_get(fib_work->fr_info.rule);
}
}
static int mlxsw_sp_router_fib_rule_event(unsigned long event,
struct fib_notifier_info *info,
struct mlxsw_sp *mlxsw_sp)
{
struct netlink_ext_ack *extack = info->extack;
struct fib_rule_notifier_info *fr_info;
struct fib_rule *rule;
int err = 0;
/* nothing to do at the moment */
if (event == FIB_EVENT_RULE_DEL)
return 0;
if (mlxsw_sp->router->aborted)
return 0;
fr_info = container_of(info, struct fib_rule_notifier_info, info);
rule = fr_info->rule;
switch (info->family) {
case AF_INET:
if (!fib4_rule_default(rule) && !rule->l3mdev)
err = -1;
break;
case AF_INET6:
if (!fib6_rule_default(rule) && !rule->l3mdev)
err = -1;
break;
case RTNL_FAMILY_IPMR:
if (!ipmr_rule_default(rule) && !rule->l3mdev)
err = -1;
break;
}
if (err < 0)
NL_SET_ERR_MSG(extack, "spectrum: FIB rules not supported. Aborting offload");
return err;
}
/* Called with rcu_read_lock() */
@ -5452,17 +5465,28 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb,
struct mlxsw_sp_fib_event_work *fib_work;
struct fib_notifier_info *info = ptr;
struct mlxsw_sp_router *router;
int err;
if (!net_eq(info->net, &init_net) ||
(info->family != AF_INET && info->family != AF_INET6 &&
info->family != RTNL_FAMILY_IPMR))
return NOTIFY_DONE;
router = container_of(nb, struct mlxsw_sp_router, fib_nb);
switch (event) {
case FIB_EVENT_RULE_ADD: /* fall through */
case FIB_EVENT_RULE_DEL:
err = mlxsw_sp_router_fib_rule_event(event, info,
router->mlxsw_sp);
if (!err)
return NOTIFY_DONE;
}
fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC);
if (WARN_ON(!fib_work))
return NOTIFY_BAD;
router = container_of(nb, struct mlxsw_sp_router, fib_nb);
fib_work->mlxsw_sp = router->mlxsw_sp;
fib_work->event = event;

View File

@ -9,6 +9,7 @@
struct fib_notifier_info {
struct net *net;
int family;
struct netlink_ext_ack *extack;
};
enum fib_event_type {

View File

@ -314,10 +314,12 @@ static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net,
static int call_fib_rule_notifiers(struct net *net,
enum fib_event_type event_type,
struct fib_rule *rule,
struct fib_rules_ops *ops)
struct fib_rules_ops *ops,
struct netlink_ext_ack *extack)
{
struct fib_rule_notifier_info info = {
.info.family = ops->family,
.info.extack = extack,
.rule = rule,
};
@ -609,7 +611,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
if (rule->tun_id)
ip_tunnel_need_metadata();
call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops);
call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops, extack);
notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
flush_route_cache(ops);
rules_ops_put(ops);
@ -749,7 +751,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
}
}
call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops);
call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops,
NULL);
notify_rule_change(RTM_DELRULE, rule, ops, nlh,
NETLINK_CB(skb).portid);
fib_rule_put(rule);

View File

@ -102,9 +102,11 @@ static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net,
static int call_fib_entry_notifiers(struct net *net,
enum fib_event_type event_type, u32 dst,
int dst_len, struct fib_alias *fa)
int dst_len, struct fib_alias *fa,
struct netlink_ext_ack *extack)
{
struct fib_entry_notifier_info info = {
.info.extack = extack,
.dst = dst,
.dst_len = dst_len,
.fi = fa->fa_info,
@ -1214,7 +1216,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
new_fa->fa_default = -1;
call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE,
key, plen, new_fa);
key, plen, new_fa, extack);
rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
tb->tb_id, &cfg->fc_nlinfo, nlflags);
@ -1269,7 +1271,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
tb->tb_num_default++;
rt_cache_flush(cfg->fc_nlinfo.nl_net);
call_fib_entry_notifiers(net, event, key, plen, new_fa);
call_fib_entry_notifiers(net, event, key, plen, new_fa, extack);
rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
&cfg->fc_nlinfo, nlflags);
succeeded:
@ -1569,7 +1571,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb,
return -ESRCH;
call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen,
fa_to_delete);
fa_to_delete, extack);
rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id,
&cfg->fc_nlinfo, 0);
@ -1886,7 +1888,8 @@ int fib_table_flush(struct net *net, struct fib_table *tb)
call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
n->key,
KEYLENGTH - fa->fa_slen, fa);
KEYLENGTH - fa->fa_slen, fa,
NULL);
hlist_del_rcu(&fa->fa_list);
fib_release_info(fa->fa_info);
alias_free_mem_rcu(fa);

View File

@ -353,9 +353,11 @@ static int call_fib6_entry_notifier(struct notifier_block *nb, struct net *net,
static int call_fib6_entry_notifiers(struct net *net,
enum fib_event_type event_type,
struct rt6_info *rt)
struct rt6_info *rt,
struct netlink_ext_ack *extack)
{
struct fib6_entry_notifier_info info = {
.info.extack = extack,
.rt = rt,
};
@ -868,7 +870,8 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn,
*/
static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
struct nl_info *info, struct mx6_config *mxc)
struct nl_info *info, struct mx6_config *mxc,
struct netlink_ext_ack *extack)
{
struct rt6_info *leaf = rcu_dereference_protected(fn->leaf,
lockdep_is_held(&rt->rt6i_table->tb6_lock));
@ -1011,7 +1014,7 @@ add:
rcu_assign_pointer(rt->rt6i_node, fn);
rcu_assign_pointer(*ins, rt);
call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_ADD,
rt);
rt, extack);
if (!info->skip_notify)
inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
@ -1040,7 +1043,7 @@ add:
rt->dst.rt6_next = iter->dst.rt6_next;
rcu_assign_pointer(*ins, rt);
call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE,
rt);
rt, extack);
if (!info->skip_notify)
inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
if (!(fn->fn_flags & RTN_RTINFO)) {
@ -1225,7 +1228,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
}
#endif
err = fib6_add_rt2node(fn, rt, info, mxc);
err = fib6_add_rt2node(fn, rt, info, mxc, extack);
if (!err) {
fib6_update_sernum_upto_root(rt, sernum);
fib6_start_gc(info->nl_net, rt);
@ -1686,7 +1689,7 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
fib6_purge_rt(rt, fn, net);
call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt);
call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL);
if (!info->skip_notify)
inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
rt6_release(rt);

View File

@ -2313,17 +2313,16 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
* requests to cap the error message, and get extra error data if
* requested.
*/
if (nlk_has_extack && extack && extack->_msg)
tlvlen += nla_total_size(strlen(extack->_msg) + 1);
if (err) {
if (!(nlk->flags & NETLINK_F_CAP_ACK))
payload += nlmsg_len(nlh);
else
flags |= NLM_F_CAPPED;
if (nlk_has_extack && extack) {
if (extack->_msg)
tlvlen += nla_total_size(strlen(extack->_msg) + 1);
if (extack->bad_attr)
tlvlen += nla_total_size(sizeof(u32));
}
if (nlk_has_extack && extack && extack->bad_attr)
tlvlen += nla_total_size(sizeof(u32));
} else {
flags |= NLM_F_CAPPED;
@ -2348,10 +2347,11 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
if (nlk_has_extack && extack) {
if (extack->_msg) {
WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
extack->_msg));
}
if (err) {
if (extack->_msg)
WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
extack->_msg));
if (extack->bad_attr &&
!WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
(u8 *)extack->bad_attr >= in_skb->data +