netdevsim: fib: Add debugfs to debug route offload failure
authorAmit Cohen <amcohen@nvidia.com>
Sun, 7 Feb 2021 08:22:56 +0000 (10:22 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 9 Feb 2021 00:47:03 +0000 (16:47 -0800)
Add "fail_route_offload" flag to disallow offloading routes.
It is needed to test "offload failed" notifications.

Create the flag as part of nsim_fib_create() under fib directory and set
it to false by default.

When FIB_EVENT_ENTRY_{REPLACE, APPEND} are triggered and
"fail_route_offload" value is true, set the appropriate hardware flag to
make the kernel emit RTM_NEWROUTE notification with RTM_F_OFFLOAD_FAILED
flag.

Signed-off-by: Amit Cohen <amcohen@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/netdevsim/fib.c

index b93bd48..46fb414 100644 (file)
@@ -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)
@@ -384,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;
@@ -423,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);
@@ -564,6 +600,15 @@ 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 (!fib6_rt)
                return -EINVAL;
@@ -586,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,
@@ -667,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))
@@ -764,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;
@@ -772,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);
@@ -783,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;
 }
 
@@ -1290,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);
@@ -1301,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);
@@ -1365,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);
@@ -1392,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);
 }