Merge branch 'route-offload-failure'
net: Add support for route offload failure notifications
Ido Schimmel says:
====================
This is a complementary series to the one merged in commit 389cb1ecc8
("Merge branch 'add-notifications-when-route-hardware-flags-change'").
The previous series added RTM_NEWROUTE notifications to user space
whenever a route was successfully installed in hardware or when its
state in hardware changed. This allows routing daemons to delay
advertisement of routes until they are installed in hardware.
However, if route installation failed, a routing daemon will wait
indefinitely for a notification that will never come. The aim of this
series is to provide a failure notification via a new flag
(RTM_F_OFFLOAD_FAILED) in the RTM_NEWROUTE message. Upon such a
notification a routing daemon may decide to withdraw the route from the
FIB.
Series overview:
Patch #1 adds the new RTM_F_OFFLOAD_FAILED flag
Patches #2-#3 and #4-#5 add failure notifications to IPv4 and IPv6,
respectively
Patches #6-#8 teach netdevsim to fail route installation via a new knob
in debugfs
Patch #9 extends mlxsw to mark routes with the new flag
Patch #10 adds test cases for the new notification over netdevsim
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
5ea3c72ccf
@ -180,7 +180,7 @@ min_adv_mss - INTEGER
|
||||
|
||||
fib_notify_on_flag_change - INTEGER
|
||||
Whether to emit RTM_NEWROUTE notifications whenever RTM_F_OFFLOAD/
|
||||
RTM_F_TRAP flags are changed.
|
||||
RTM_F_TRAP/RTM_F_OFFLOAD_FAILED flags are changed.
|
||||
|
||||
After installing a route to the kernel, user space receives an
|
||||
acknowledgment, which means the route was installed in the kernel,
|
||||
@ -197,6 +197,7 @@ fib_notify_on_flag_change - INTEGER
|
||||
|
||||
- 0 - Do not emit notifications.
|
||||
- 1 - Emit notifications.
|
||||
- 2 - Emit notifications only for RTM_F_OFFLOAD_FAILED flag change.
|
||||
|
||||
IP Fragmentation:
|
||||
|
||||
@ -1797,7 +1798,7 @@ nexthop_compat_mode - BOOLEAN
|
||||
|
||||
fib_notify_on_flag_change - INTEGER
|
||||
Whether to emit RTM_NEWROUTE notifications whenever RTM_F_OFFLOAD/
|
||||
RTM_F_TRAP flags are changed.
|
||||
RTM_F_TRAP/RTM_F_OFFLOAD_FAILED flags are changed.
|
||||
|
||||
After installing a route to the kernel, user space receives an
|
||||
acknowledgment, which means the route was installed in the kernel,
|
||||
@ -1814,6 +1815,7 @@ fib_notify_on_flag_change - INTEGER
|
||||
|
||||
- 0 - Do not emit notifications.
|
||||
- 1 - Emit notifications.
|
||||
- 2 - Emit notifications only for RTM_F_OFFLOAD_FAILED flag change.
|
||||
|
||||
IPv6 Fragmentation:
|
||||
|
||||
|
@ -4942,6 +4942,25 @@ mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mlxsw_sp_fib4_offload_failed_flag_set(struct mlxsw_sp *mlxsw_sp,
|
||||
struct fib_entry_notifier_info *fen_info)
|
||||
{
|
||||
u32 *p_dst = (u32 *) &fen_info->dst;
|
||||
struct fib_rt_info fri;
|
||||
|
||||
fri.fi = fen_info->fi;
|
||||
fri.tb_id = fen_info->tb_id;
|
||||
fri.dst = cpu_to_be32(*p_dst);
|
||||
fri.dst_len = fen_info->dst_len;
|
||||
fri.tos = fen_info->tos;
|
||||
fri.type = fen_info->type;
|
||||
fri.offload = false;
|
||||
fri.trap = false;
|
||||
fri.offload_failed = true;
|
||||
fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);
|
||||
}
|
||||
|
||||
static void
|
||||
mlxsw_sp_fib4_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
|
||||
struct mlxsw_sp_fib_entry *fib_entry)
|
||||
@ -4963,6 +4982,7 @@ mlxsw_sp_fib4_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
|
||||
fri.type = fib4_entry->type;
|
||||
fri.offload = should_offload;
|
||||
fri.trap = !should_offload;
|
||||
fri.offload_failed = false;
|
||||
fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);
|
||||
}
|
||||
|
||||
@ -4985,9 +5005,34 @@ mlxsw_sp_fib4_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
|
||||
fri.type = fib4_entry->type;
|
||||
fri.offload = false;
|
||||
fri.trap = false;
|
||||
fri.offload_failed = false;
|
||||
fib_alias_hw_flags_set(mlxsw_sp_net(mlxsw_sp), &fri);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static void
|
||||
mlxsw_sp_fib6_offload_failed_flag_set(struct mlxsw_sp *mlxsw_sp,
|
||||
struct fib6_info **rt_arr,
|
||||
unsigned int nrt6)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* In IPv6 a multipath route is represented using multiple routes, so
|
||||
* we need to set the flags on all of them.
|
||||
*/
|
||||
for (i = 0; i < nrt6; i++)
|
||||
fib6_info_hw_flags_set(mlxsw_sp_net(mlxsw_sp), rt_arr[i],
|
||||
false, false, true);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
mlxsw_sp_fib6_offload_failed_flag_set(struct mlxsw_sp *mlxsw_sp,
|
||||
struct fib6_info **rt_arr,
|
||||
unsigned int nrt6)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static void
|
||||
mlxsw_sp_fib6_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
|
||||
@ -5006,7 +5051,7 @@ mlxsw_sp_fib6_entry_hw_flags_set(struct mlxsw_sp *mlxsw_sp,
|
||||
common);
|
||||
list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list)
|
||||
fib6_info_hw_flags_set(mlxsw_sp_net(mlxsw_sp), mlxsw_sp_rt6->rt,
|
||||
should_offload, !should_offload);
|
||||
should_offload, !should_offload, false);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
@ -5028,7 +5073,7 @@ mlxsw_sp_fib6_entry_hw_flags_clear(struct mlxsw_sp *mlxsw_sp,
|
||||
common);
|
||||
list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list)
|
||||
fib6_info_hw_flags_set(mlxsw_sp_net(mlxsw_sp), mlxsw_sp_rt6->rt,
|
||||
false, false);
|
||||
false, false, false);
|
||||
}
|
||||
#else
|
||||
static void
|
||||
@ -7021,6 +7066,8 @@ static void mlxsw_sp_router_fib4_event_process(struct mlxsw_sp *mlxsw_sp,
|
||||
if (err) {
|
||||
mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
|
||||
mlxsw_sp_router_fib_abort(mlxsw_sp);
|
||||
mlxsw_sp_fib4_offload_failed_flag_set(mlxsw_sp,
|
||||
&fib_event->fen_info);
|
||||
}
|
||||
fib_info_put(fib_event->fen_info.fi);
|
||||
break;
|
||||
@ -7042,6 +7089,7 @@ static void mlxsw_sp_router_fib6_event_process(struct mlxsw_sp *mlxsw_sp,
|
||||
struct mlxsw_sp_fib_entry_op_ctx *op_ctx,
|
||||
struct mlxsw_sp_fib_event *fib_event)
|
||||
{
|
||||
struct mlxsw_sp_fib6_event *fib6_event = &fib_event->fib6_event;
|
||||
int err;
|
||||
|
||||
mlxsw_sp_span_respin(mlxsw_sp);
|
||||
@ -7053,6 +7101,9 @@ static void mlxsw_sp_router_fib6_event_process(struct mlxsw_sp *mlxsw_sp,
|
||||
if (err) {
|
||||
mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
|
||||
mlxsw_sp_router_fib_abort(mlxsw_sp);
|
||||
mlxsw_sp_fib6_offload_failed_flag_set(mlxsw_sp,
|
||||
fib6_event->rt_arr,
|
||||
fib6_event->nrt6);
|
||||
}
|
||||
mlxsw_sp_router_fib6_event_fini(&fib_event->fib6_event);
|
||||
break;
|
||||
@ -7062,6 +7113,9 @@ static void mlxsw_sp_router_fib6_event_process(struct mlxsw_sp *mlxsw_sp,
|
||||
if (err) {
|
||||
mlxsw_sp_fib_entry_op_ctx_priv_put_all(op_ctx);
|
||||
mlxsw_sp_router_fib_abort(mlxsw_sp);
|
||||
mlxsw_sp_fib6_offload_failed_flag_set(mlxsw_sp,
|
||||
fib6_event->rt_arr,
|
||||
fib6_event->nrt6);
|
||||
}
|
||||
mlxsw_sp_router_fib6_event_fini(&fib_event->fib6_event);
|
||||
break;
|
||||
|
@ -1012,23 +1012,25 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
|
||||
nsim_dev->fw_update_status = true;
|
||||
nsim_dev->fw_update_overwrite_mask = 0;
|
||||
|
||||
nsim_dev->fib_data = nsim_fib_create(devlink, extack);
|
||||
if (IS_ERR(nsim_dev->fib_data))
|
||||
return PTR_ERR(nsim_dev->fib_data);
|
||||
|
||||
nsim_devlink_param_load_driverinit_values(devlink);
|
||||
|
||||
err = nsim_dev_dummy_region_init(nsim_dev, devlink);
|
||||
if (err)
|
||||
goto err_fib_destroy;
|
||||
return err;
|
||||
|
||||
err = nsim_dev_traps_init(devlink);
|
||||
if (err)
|
||||
goto err_dummy_region_exit;
|
||||
|
||||
nsim_dev->fib_data = nsim_fib_create(devlink, extack);
|
||||
if (IS_ERR(nsim_dev->fib_data)) {
|
||||
err = PTR_ERR(nsim_dev->fib_data);
|
||||
goto err_traps_exit;
|
||||
}
|
||||
|
||||
err = nsim_dev_health_init(nsim_dev, devlink);
|
||||
if (err)
|
||||
goto err_traps_exit;
|
||||
goto err_fib_destroy;
|
||||
|
||||
err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
|
||||
if (err)
|
||||
@ -1043,12 +1045,12 @@ static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
|
||||
|
||||
err_health_exit:
|
||||
nsim_dev_health_exit(nsim_dev);
|
||||
err_fib_destroy:
|
||||
nsim_fib_destroy(devlink, nsim_dev->fib_data);
|
||||
err_traps_exit:
|
||||
nsim_dev_traps_exit(devlink);
|
||||
err_dummy_region_exit:
|
||||
nsim_dev_dummy_region_exit(nsim_dev);
|
||||
err_fib_destroy:
|
||||
nsim_fib_destroy(devlink, nsim_dev->fib_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1080,15 +1082,9 @@ int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
|
||||
if (err)
|
||||
goto err_devlink_free;
|
||||
|
||||
nsim_dev->fib_data = nsim_fib_create(devlink, NULL);
|
||||
if (IS_ERR(nsim_dev->fib_data)) {
|
||||
err = PTR_ERR(nsim_dev->fib_data);
|
||||
goto err_resources_unregister;
|
||||
}
|
||||
|
||||
err = devlink_register(devlink, &nsim_bus_dev->dev);
|
||||
if (err)
|
||||
goto err_fib_destroy;
|
||||
goto err_resources_unregister;
|
||||
|
||||
err = devlink_params_register(devlink, nsim_devlink_params,
|
||||
ARRAY_SIZE(nsim_devlink_params));
|
||||
@ -1108,9 +1104,15 @@ int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
|
||||
if (err)
|
||||
goto err_traps_exit;
|
||||
|
||||
nsim_dev->fib_data = nsim_fib_create(devlink, NULL);
|
||||
if (IS_ERR(nsim_dev->fib_data)) {
|
||||
err = PTR_ERR(nsim_dev->fib_data);
|
||||
goto err_debugfs_exit;
|
||||
}
|
||||
|
||||
err = nsim_dev_health_init(nsim_dev, devlink);
|
||||
if (err)
|
||||
goto err_debugfs_exit;
|
||||
goto err_fib_destroy;
|
||||
|
||||
err = nsim_bpf_dev_init(nsim_dev);
|
||||
if (err)
|
||||
@ -1128,6 +1130,8 @@ err_bpf_dev_exit:
|
||||
nsim_bpf_dev_exit(nsim_dev);
|
||||
err_health_exit:
|
||||
nsim_dev_health_exit(nsim_dev);
|
||||
err_fib_destroy:
|
||||
nsim_fib_destroy(devlink, nsim_dev->fib_data);
|
||||
err_debugfs_exit:
|
||||
nsim_dev_debugfs_exit(nsim_dev);
|
||||
err_traps_exit:
|
||||
@ -1139,8 +1143,6 @@ err_params_unregister:
|
||||
ARRAY_SIZE(nsim_devlink_params));
|
||||
err_dl_unregister:
|
||||
devlink_unregister(devlink);
|
||||
err_fib_destroy:
|
||||
nsim_fib_destroy(devlink, nsim_dev->fib_data);
|
||||
err_resources_unregister:
|
||||
devlink_resources_unregister(devlink, NULL);
|
||||
err_devlink_free:
|
||||
@ -1157,10 +1159,10 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
|
||||
debugfs_remove(nsim_dev->take_snapshot);
|
||||
nsim_dev_port_del_all(nsim_dev);
|
||||
nsim_dev_health_exit(nsim_dev);
|
||||
nsim_fib_destroy(devlink, nsim_dev->fib_data);
|
||||
nsim_dev_traps_exit(devlink);
|
||||
nsim_dev_dummy_region_exit(nsim_dev);
|
||||
mutex_destroy(&nsim_dev->port_list_lock);
|
||||
nsim_fib_destroy(devlink, nsim_dev->fib_data);
|
||||
}
|
||||
|
||||
void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <net/fib_rules.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/nexthop.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "netdevsim.h"
|
||||
|
||||
@ -53,6 +54,8 @@ struct nsim_fib_data {
|
||||
struct work_struct fib_event_work;
|
||||
struct list_head fib_event_queue;
|
||||
spinlock_t fib_event_queue_lock; /* Protects fib event queue list */
|
||||
struct dentry *ddir;
|
||||
bool fail_route_offload;
|
||||
};
|
||||
|
||||
struct nsim_fib_rt_key {
|
||||
@ -303,6 +306,25 @@ nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
|
||||
return container_of(fib_rt, struct nsim_fib4_rt, common);
|
||||
}
|
||||
|
||||
static void
|
||||
nsim_fib4_rt_offload_failed_flag_set(struct net *net,
|
||||
struct fib_entry_notifier_info *fen_info)
|
||||
{
|
||||
u32 *p_dst = (u32 *)&fen_info->dst;
|
||||
struct fib_rt_info fri;
|
||||
|
||||
fri.fi = fen_info->fi;
|
||||
fri.tb_id = fen_info->tb_id;
|
||||
fri.dst = cpu_to_be32(*p_dst);
|
||||
fri.dst_len = fen_info->dst_len;
|
||||
fri.tos = fen_info->tos;
|
||||
fri.type = fen_info->type;
|
||||
fri.offload = false;
|
||||
fri.trap = false;
|
||||
fri.offload_failed = true;
|
||||
fib_alias_hw_flags_set(net, &fri);
|
||||
}
|
||||
|
||||
static void nsim_fib4_rt_hw_flags_set(struct net *net,
|
||||
const struct nsim_fib4_rt *fib4_rt,
|
||||
bool trap)
|
||||
@ -319,6 +341,7 @@ static void nsim_fib4_rt_hw_flags_set(struct net *net,
|
||||
fri.type = fib4_rt->type;
|
||||
fri.offload = false;
|
||||
fri.trap = trap;
|
||||
fri.offload_failed = false;
|
||||
fib_alias_hw_flags_set(net, &fri);
|
||||
}
|
||||
|
||||
@ -383,6 +406,15 @@ static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
|
||||
struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
|
||||
int err;
|
||||
|
||||
if (data->fail_route_offload) {
|
||||
/* For testing purposes, user set debugfs fail_route_offload
|
||||
* value to true. Simulate hardware programming latency and then
|
||||
* fail.
|
||||
*/
|
||||
msleep(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fib4_rt = nsim_fib4_rt_create(data, fen_info);
|
||||
if (!fib4_rt)
|
||||
return -ENOMEM;
|
||||
@ -405,7 +437,7 @@ static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
|
||||
struct nsim_fib4_rt *fib4_rt;
|
||||
|
||||
fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
|
||||
if (WARN_ON_ONCE(!fib4_rt))
|
||||
if (!fib4_rt)
|
||||
return;
|
||||
|
||||
rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
|
||||
@ -422,6 +454,11 @@ static int nsim_fib4_event(struct nsim_fib_data *data,
|
||||
switch (event) {
|
||||
case FIB_EVENT_ENTRY_REPLACE:
|
||||
err = nsim_fib4_rt_insert(data, fen_info);
|
||||
if (err) {
|
||||
struct net *net = devlink_net(data->devlink);
|
||||
|
||||
nsim_fib4_rt_offload_failed_flag_set(net, fen_info);
|
||||
}
|
||||
break;
|
||||
case FIB_EVENT_ENTRY_DEL:
|
||||
nsim_fib4_rt_remove(data, fen_info);
|
||||
@ -481,7 +518,7 @@ static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
|
||||
struct nsim_fib6_rt_nh *fib6_rt_nh;
|
||||
|
||||
fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
|
||||
if (WARN_ON_ONCE(!fib6_rt_nh))
|
||||
if (!fib6_rt_nh)
|
||||
return;
|
||||
|
||||
fib6_rt->nhs--;
|
||||
@ -563,8 +600,17 @@ static int nsim_fib6_rt_append(struct nsim_fib_data *data,
|
||||
struct nsim_fib6_rt *fib6_rt;
|
||||
int i, err;
|
||||
|
||||
if (data->fail_route_offload) {
|
||||
/* For testing purposes, user set debugfs fail_route_offload
|
||||
* value to true. Simulate hardware programming latency and then
|
||||
* fail.
|
||||
*/
|
||||
msleep(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
|
||||
if (WARN_ON_ONCE(!fib6_rt))
|
||||
if (!fib6_rt)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < fib6_event->nrt6; i++) {
|
||||
@ -585,6 +631,26 @@ err_fib6_rt_nh_del:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
|
||||
struct fib6_info **rt_arr,
|
||||
unsigned int nrt6)
|
||||
|
||||
{
|
||||
struct net *net = devlink_net(data->devlink);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nrt6; i++)
|
||||
fib6_info_hw_flags_set(net, rt_arr[i], false, false, true);
|
||||
}
|
||||
#else
|
||||
static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
|
||||
struct fib6_info **rt_arr,
|
||||
unsigned int nrt6)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
|
||||
const struct nsim_fib6_rt *fib6_rt,
|
||||
@ -594,7 +660,7 @@ static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
|
||||
struct nsim_fib6_rt_nh *fib6_rt_nh;
|
||||
|
||||
list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
|
||||
fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap);
|
||||
fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap, false);
|
||||
}
|
||||
#else
|
||||
static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
|
||||
@ -666,6 +732,15 @@ static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
|
||||
struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
|
||||
int err;
|
||||
|
||||
if (data->fail_route_offload) {
|
||||
/* For testing purposes, user set debugfs fail_route_offload
|
||||
* value to true. Simulate hardware programming latency and then
|
||||
* fail.
|
||||
*/
|
||||
msleep(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr,
|
||||
fib6_event->nrt6);
|
||||
if (IS_ERR(fib6_rt))
|
||||
@ -763,7 +838,7 @@ static int nsim_fib6_event(struct nsim_fib_data *data,
|
||||
struct nsim_fib6_event *fib6_event,
|
||||
unsigned long event)
|
||||
{
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
if (fib6_event->rt_arr[0]->fib6_src.plen)
|
||||
return 0;
|
||||
@ -771,9 +846,13 @@ static int nsim_fib6_event(struct nsim_fib_data *data,
|
||||
switch (event) {
|
||||
case FIB_EVENT_ENTRY_REPLACE:
|
||||
err = nsim_fib6_rt_insert(data, fib6_event);
|
||||
if (err)
|
||||
goto err_rt_offload_failed_flag_set;
|
||||
break;
|
||||
case FIB_EVENT_ENTRY_APPEND:
|
||||
err = nsim_fib6_rt_append(data, fib6_event);
|
||||
if (err)
|
||||
goto err_rt_offload_failed_flag_set;
|
||||
break;
|
||||
case FIB_EVENT_ENTRY_DEL:
|
||||
nsim_fib6_rt_remove(data, fib6_event);
|
||||
@ -782,6 +861,11 @@ static int nsim_fib6_event(struct nsim_fib_data *data,
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_rt_offload_failed_flag_set:
|
||||
nsim_fib6_rt_offload_failed_flag_set(data, fib6_event->rt_arr,
|
||||
fib6_event->nrt6);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1289,10 +1373,29 @@ static void nsim_fib_event_work(struct work_struct *work)
|
||||
mutex_unlock(&data->fib_lock);
|
||||
}
|
||||
|
||||
static int
|
||||
nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
|
||||
{
|
||||
data->ddir = debugfs_create_dir("fib", nsim_dev->ddir);
|
||||
if (IS_ERR(data->ddir))
|
||||
return PTR_ERR(data->ddir);
|
||||
|
||||
data->fail_route_offload = false;
|
||||
debugfs_create_bool("fail_route_offload", 0600, data->ddir,
|
||||
&data->fail_route_offload);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
|
||||
{
|
||||
debugfs_remove_recursive(data->ddir);
|
||||
}
|
||||
|
||||
struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct nsim_fib_data *data;
|
||||
struct nsim_dev *nsim_dev;
|
||||
int err;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
@ -1300,10 +1403,15 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
data->devlink = devlink;
|
||||
|
||||
err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
|
||||
nsim_dev = devlink_priv(devlink);
|
||||
err = nsim_fib_debugfs_init(data, nsim_dev);
|
||||
if (err)
|
||||
goto err_data_free;
|
||||
|
||||
err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
|
||||
if (err)
|
||||
goto err_debugfs_exit;
|
||||
|
||||
mutex_init(&data->fib_lock);
|
||||
INIT_LIST_HEAD(&data->fib_rt_list);
|
||||
err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
|
||||
@ -1364,6 +1472,8 @@ err_rhashtable_nexthop_destroy:
|
||||
rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
|
||||
data);
|
||||
mutex_destroy(&data->fib_lock);
|
||||
err_debugfs_exit:
|
||||
nsim_fib_debugfs_exit(data);
|
||||
err_data_free:
|
||||
kfree(data);
|
||||
return ERR_PTR(err);
|
||||
@ -1391,5 +1501,6 @@ void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
|
||||
WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
|
||||
WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
|
||||
mutex_destroy(&data->fib_lock);
|
||||
nsim_fib_debugfs_exit(data);
|
||||
kfree(data);
|
||||
}
|
||||
|
@ -195,7 +195,8 @@ struct fib6_info {
|
||||
fib6_destroying:1,
|
||||
offload:1,
|
||||
trap:1,
|
||||
unused:2;
|
||||
offload_failed:1,
|
||||
unused:1;
|
||||
|
||||
struct rcu_head rcu;
|
||||
struct nexthop *nh;
|
||||
@ -539,7 +540,7 @@ static inline bool fib6_metric_locked(struct fib6_info *f6i, int metric)
|
||||
return !!(f6i->fib6_metrics->metrics[RTAX_LOCK - 1] & (1 << metric));
|
||||
}
|
||||
void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
|
||||
bool offload, bool trap);
|
||||
bool offload, bool trap, bool offload_failed);
|
||||
|
||||
#if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL)
|
||||
struct bpf_iter__ipv6_route {
|
||||
|
@ -213,7 +213,8 @@ struct fib_rt_info {
|
||||
u8 type;
|
||||
u8 offload:1,
|
||||
trap:1,
|
||||
unused:6;
|
||||
offload_failed:1,
|
||||
unused:5;
|
||||
};
|
||||
|
||||
struct fib_entry_notifier_info {
|
||||
|
@ -319,6 +319,11 @@ enum rt_scope_t {
|
||||
#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */
|
||||
#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */
|
||||
#define RTM_F_TRAP 0x8000 /* route is trapping packets */
|
||||
#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value
|
||||
* is chosen to avoid conflicts with
|
||||
* other flags defined in
|
||||
* include/uapi/linux/ipv6_route.h
|
||||
*/
|
||||
|
||||
/* Reserved table identifiers */
|
||||
|
||||
|
@ -18,7 +18,8 @@ struct fib_alias {
|
||||
s16 fa_default;
|
||||
u8 offload:1,
|
||||
trap:1,
|
||||
unused:6;
|
||||
offload_failed:1,
|
||||
unused:5;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
|
@ -521,6 +521,7 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
|
||||
fri.type = fa->fa_type;
|
||||
fri.offload = fa->offload;
|
||||
fri.trap = fa->trap;
|
||||
fri.offload_failed = fa->offload_failed;
|
||||
err = fib_dump_info(skb, info->portid, seq, event, &fri, nlm_flags);
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in fib_nlmsg_size() */
|
||||
@ -1811,6 +1812,8 @@ offload:
|
||||
rtm->rtm_flags |= RTM_F_OFFLOAD;
|
||||
if (fri->trap)
|
||||
rtm->rtm_flags |= RTM_F_TRAP;
|
||||
if (fri->offload_failed)
|
||||
rtm->rtm_flags |= RTM_F_OFFLOAD_FAILED;
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
|
@ -1047,12 +1047,20 @@ void fib_alias_hw_flags_set(struct net *net, const struct fib_rt_info *fri)
|
||||
if (!fa_match)
|
||||
goto out;
|
||||
|
||||
if (fa_match->offload == fri->offload && fa_match->trap == fri->trap)
|
||||
if (fa_match->offload == fri->offload && fa_match->trap == fri->trap &&
|
||||
fa_match->offload_failed == fri->offload_failed)
|
||||
goto out;
|
||||
|
||||
fa_match->offload = fri->offload;
|
||||
fa_match->trap = fri->trap;
|
||||
|
||||
/* 2 means send notifications only if offload_failed was changed. */
|
||||
if (net->ipv4.sysctl_fib_notify_on_flag_change == 2 &&
|
||||
fa_match->offload_failed == fri->offload_failed)
|
||||
goto out;
|
||||
|
||||
fa_match->offload_failed = fri->offload_failed;
|
||||
|
||||
if (!net->ipv4.sysctl_fib_notify_on_flag_change)
|
||||
goto out;
|
||||
|
||||
@ -1290,6 +1298,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
|
||||
new_fa->fa_default = -1;
|
||||
new_fa->offload = 0;
|
||||
new_fa->trap = 0;
|
||||
new_fa->offload_failed = 0;
|
||||
|
||||
hlist_replace_rcu(&fa->fa_list, &new_fa->fa_list);
|
||||
|
||||
@ -1350,6 +1359,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb,
|
||||
new_fa->fa_default = -1;
|
||||
new_fa->offload = 0;
|
||||
new_fa->trap = 0;
|
||||
new_fa->offload_failed = 0;
|
||||
|
||||
/* Insert new entry to the list. */
|
||||
err = fib_insert_alias(t, tp, l, new_fa, fa, key);
|
||||
@ -2289,6 +2299,7 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,
|
||||
fri.type = fa->fa_type;
|
||||
fri.offload = fa->offload;
|
||||
fri.trap = fa->trap;
|
||||
fri.offload_failed = fa->offload_failed;
|
||||
err = fib_dump_info(skb,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq,
|
||||
|
@ -3304,6 +3304,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
||||
fri.type = rt->rt_type;
|
||||
fri.offload = 0;
|
||||
fri.trap = 0;
|
||||
fri.offload_failed = 0;
|
||||
if (res.fa_head) {
|
||||
struct fib_alias *fa;
|
||||
|
||||
|
@ -1361,7 +1361,7 @@ static struct ctl_table ipv4_net_table[] = {
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_ONE,
|
||||
.extra2 = &two,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
@ -5619,6 +5619,8 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
|
||||
rtm->rtm_flags |= RTM_F_OFFLOAD;
|
||||
if (rt->trap)
|
||||
rtm->rtm_flags |= RTM_F_TRAP;
|
||||
if (rt->offload_failed)
|
||||
rtm->rtm_flags |= RTM_F_OFFLOAD_FAILED;
|
||||
}
|
||||
|
||||
if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
|
||||
@ -6070,17 +6072,25 @@ errout:
|
||||
}
|
||||
|
||||
void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
|
||||
bool offload, bool trap)
|
||||
bool offload, bool trap, bool offload_failed)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (f6i->offload == offload && f6i->trap == trap)
|
||||
if (f6i->offload == offload && f6i->trap == trap &&
|
||||
f6i->offload_failed == offload_failed)
|
||||
return;
|
||||
|
||||
f6i->offload = offload;
|
||||
f6i->trap = trap;
|
||||
|
||||
/* 2 means send notifications only if offload_failed was changed. */
|
||||
if (net->ipv6.sysctl.fib_notify_on_flag_change == 2 &&
|
||||
f6i->offload_failed == offload_failed)
|
||||
return;
|
||||
|
||||
f6i->offload_failed = offload_failed;
|
||||
|
||||
if (!rcu_access_pointer(f6i->fib6_node))
|
||||
/* The route was removed from the tree, do not send
|
||||
* notfication.
|
||||
|
@ -167,7 +167,7 @@ static struct ctl_table ipv6_table_template[] = {
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_ONE,
|
||||
.extra2 = &two,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
@ -7,9 +7,11 @@ ALL_TESTS="
|
||||
ipv4_route_addition_test
|
||||
ipv4_route_deletion_test
|
||||
ipv4_route_replacement_test
|
||||
ipv4_route_offload_failed_test
|
||||
ipv6_route_addition_test
|
||||
ipv6_route_deletion_test
|
||||
ipv6_route_replacement_test
|
||||
ipv6_route_offload_failed_test
|
||||
"
|
||||
|
||||
NETDEVSIM_PATH=/sys/bus/netdevsim/
|
||||
@ -17,9 +19,26 @@ DEV_ADDR=1337
|
||||
DEV=netdevsim${DEV_ADDR}
|
||||
DEVLINK_DEV=netdevsim/${DEV}
|
||||
SYSFS_NET_DIR=/sys/bus/netdevsim/devices/$DEV/net/
|
||||
DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV/
|
||||
NUM_NETIFS=0
|
||||
source $lib_dir/lib.sh
|
||||
|
||||
check_rt_offload_failed()
|
||||
{
|
||||
local outfile=$1; shift
|
||||
local line
|
||||
|
||||
# Make sure that the first notification was emitted without
|
||||
# RTM_F_OFFLOAD_FAILED flag and the second with RTM_F_OFFLOAD_FAILED
|
||||
# flag
|
||||
head -n 1 $outfile | grep -q "rt_offload_failed"
|
||||
if [[ $? -eq 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
head -n 2 $outfile | tail -n 1 | grep -q "rt_offload_failed"
|
||||
}
|
||||
|
||||
check_rt_trap()
|
||||
{
|
||||
local outfile=$1; shift
|
||||
@ -39,15 +58,23 @@ route_notify_check()
|
||||
{
|
||||
local outfile=$1; shift
|
||||
local expected_num_lines=$1; shift
|
||||
local offload_failed=${1:-0}; shift
|
||||
|
||||
# check the monitor results
|
||||
lines=`wc -l $outfile | cut "-d " -f1`
|
||||
test $lines -eq $expected_num_lines
|
||||
check_err $? "$expected_num_lines notifications were expected but $lines were received"
|
||||
|
||||
if [[ $expected_num_lines -eq 2 ]]; then
|
||||
if [[ $expected_num_lines -eq 1 ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $offload_failed -eq 0 ]]; then
|
||||
check_rt_trap $outfile
|
||||
check_err $? "Wrong RTM_F_TRAP flags in notifications"
|
||||
else
|
||||
check_rt_offload_failed $outfile
|
||||
check_err $? "Wrong RTM_F_OFFLOAD_FAILED flags in notifications"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -57,6 +84,7 @@ route_addition_check()
|
||||
local notify=$1; shift
|
||||
local route=$1; shift
|
||||
local expected_num_notifications=$1; shift
|
||||
local offload_failed=${1:-0}; shift
|
||||
|
||||
ip netns exec testns1 sysctl -qw net.$ip.fib_notify_on_flag_change=$notify
|
||||
|
||||
@ -68,7 +96,7 @@ route_addition_check()
|
||||
sleep 1
|
||||
kill %% && wait %% &> /dev/null
|
||||
|
||||
route_notify_check $outfile $expected_num_notifications
|
||||
route_notify_check $outfile $expected_num_notifications $offload_failed
|
||||
rm -f $outfile
|
||||
|
||||
$IP route del $route dev dummy1
|
||||
@ -93,6 +121,13 @@ ipv4_route_addition_test()
|
||||
expected_num_notifications=2
|
||||
route_addition_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
# notify=2 means emit notifications only for failed route installation,
|
||||
# make sure a single notification will be emitted for the programmed
|
||||
# route.
|
||||
notify=2
|
||||
expected_num_notifications=1
|
||||
route_addition_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
log_test "IPv4 route addition"
|
||||
}
|
||||
|
||||
@ -185,11 +220,55 @@ ipv4_route_replacement_test()
|
||||
expected_num_notifications=2
|
||||
route_replacement_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
# notify=2 means emit notifications only for failed route installation,
|
||||
# make sure a single notification will be emitted for the new route.
|
||||
notify=2
|
||||
expected_num_notifications=1
|
||||
route_replacement_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
$IP link del name dummy2
|
||||
|
||||
log_test "IPv4 route replacement"
|
||||
}
|
||||
|
||||
ipv4_route_offload_failed_test()
|
||||
{
|
||||
|
||||
RET=0
|
||||
|
||||
local ip="ipv4"
|
||||
local route=192.0.2.0/24
|
||||
local offload_failed=1
|
||||
|
||||
echo "y"> $DEBUGFS_DIR/fib/fail_route_offload
|
||||
check_err $? "Failed to setup route offload to fail"
|
||||
|
||||
# Make sure a single notification will be emitted for the programmed
|
||||
# route.
|
||||
local notify=0
|
||||
local expected_num_notifications=1
|
||||
route_addition_check $ip $notify $route $expected_num_notifications \
|
||||
$offload_failed
|
||||
|
||||
# Make sure two notifications will be emitted for the new route.
|
||||
notify=1
|
||||
expected_num_notifications=2
|
||||
route_addition_check $ip $notify $route $expected_num_notifications \
|
||||
$offload_failed
|
||||
|
||||
# notify=2 means emit notifications only for failed route installation,
|
||||
# make sure two notifications will be emitted for the new route.
|
||||
notify=2
|
||||
expected_num_notifications=2
|
||||
route_addition_check $ip $notify $route $expected_num_notifications \
|
||||
$offload_failed
|
||||
|
||||
echo "n"> $DEBUGFS_DIR/fib/fail_route_offload
|
||||
check_err $? "Failed to setup route offload not to fail"
|
||||
|
||||
log_test "IPv4 route offload failed"
|
||||
}
|
||||
|
||||
ipv6_route_addition_test()
|
||||
{
|
||||
RET=0
|
||||
@ -208,6 +287,13 @@ ipv6_route_addition_test()
|
||||
expected_num_notifications=2
|
||||
route_addition_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
# notify=2 means emit notifications only for failed route installation,
|
||||
# make sure a single notification will be emitted for the programmed
|
||||
# route.
|
||||
notify=2
|
||||
expected_num_notifications=1
|
||||
route_addition_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
log_test "IPv6 route addition"
|
||||
}
|
||||
|
||||
@ -250,11 +336,55 @@ ipv6_route_replacement_test()
|
||||
expected_num_notifications=2
|
||||
route_replacement_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
# notify=2 means emit notifications only for failed route installation,
|
||||
# make sure a single notification will be emitted for the new route.
|
||||
notify=2
|
||||
expected_num_notifications=1
|
||||
route_replacement_check $ip $notify $route $expected_num_notifications
|
||||
|
||||
$IP link del name dummy2
|
||||
|
||||
log_test "IPv6 route replacement"
|
||||
}
|
||||
|
||||
ipv6_route_offload_failed_test()
|
||||
{
|
||||
|
||||
RET=0
|
||||
|
||||
local ip="ipv6"
|
||||
local route=2001:db8:1::/64
|
||||
local offload_failed=1
|
||||
|
||||
echo "y"> $DEBUGFS_DIR/fib/fail_route_offload
|
||||
check_err $? "Failed to setup route offload to fail"
|
||||
|
||||
# Make sure a single notification will be emitted for the programmed
|
||||
# route.
|
||||
local notify=0
|
||||
local expected_num_notifications=1
|
||||
route_addition_check $ip $notify $route $expected_num_notifications \
|
||||
$offload_failed
|
||||
|
||||
# Make sure two notifications will be emitted for the new route.
|
||||
notify=1
|
||||
expected_num_notifications=2
|
||||
route_addition_check $ip $notify $route $expected_num_notifications \
|
||||
$offload_failed
|
||||
|
||||
# notify=2 means emit notifications only for failed route installation,
|
||||
# make sure two notifications will be emitted for the new route.
|
||||
notify=2
|
||||
expected_num_notifications=2
|
||||
route_addition_check $ip $notify $route $expected_num_notifications \
|
||||
$offload_failed
|
||||
|
||||
echo "n"> $DEBUGFS_DIR/fib/fail_route_offload
|
||||
check_err $? "Failed to setup route offload not to fail"
|
||||
|
||||
log_test "IPv6 route offload failed"
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
modprobe netdevsim &> /dev/null
|
||||
|
Loading…
Reference in New Issue
Block a user