linux/drivers/net/netdevsim/health.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/debugfs.h>
   5#include <linux/err.h>
   6#include <linux/kernel.h>
   7#include <linux/slab.h>
   8
   9#include "netdevsim.h"
  10
  11static int
  12nsim_dev_empty_reporter_dump(struct devlink_health_reporter *reporter,
  13                             struct devlink_fmsg *fmsg, void *priv_ctx,
  14                             struct netlink_ext_ack *extack)
  15{
  16        return 0;
  17}
  18
  19static int
  20nsim_dev_empty_reporter_diagnose(struct devlink_health_reporter *reporter,
  21                                 struct devlink_fmsg *fmsg,
  22                                 struct netlink_ext_ack *extack)
  23{
  24        return 0;
  25}
  26
  27static const
  28struct devlink_health_reporter_ops nsim_dev_empty_reporter_ops = {
  29        .name = "empty",
  30        .dump = nsim_dev_empty_reporter_dump,
  31        .diagnose = nsim_dev_empty_reporter_diagnose,
  32};
  33
  34struct nsim_dev_dummy_reporter_ctx {
  35        char *break_msg;
  36};
  37
  38static int
  39nsim_dev_dummy_reporter_recover(struct devlink_health_reporter *reporter,
  40                                void *priv_ctx,
  41                                struct netlink_ext_ack *extack)
  42{
  43        struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
  44        struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
  45
  46        if (health->fail_recover) {
  47                /* For testing purposes, user set debugfs fail_recover
  48                 * value to true. Fail right away.
  49                 */
  50                NL_SET_ERR_MSG_MOD(extack, "User setup the recover to fail for testing purposes");
  51                return -EINVAL;
  52        }
  53        if (ctx) {
  54                kfree(health->recovered_break_msg);
  55                health->recovered_break_msg = kstrdup(ctx->break_msg,
  56                                                      GFP_KERNEL);
  57                if (!health->recovered_break_msg)
  58                        return -ENOMEM;
  59        }
  60        return 0;
  61}
  62
  63static int nsim_dev_dummy_fmsg_put(struct devlink_fmsg *fmsg, u32 binary_len)
  64{
  65        char *binary;
  66        int err;
  67        int i;
  68
  69        err = devlink_fmsg_bool_pair_put(fmsg, "test_bool", true);
  70        if (err)
  71                return err;
  72        err = devlink_fmsg_u8_pair_put(fmsg, "test_u8", 1);
  73        if (err)
  74                return err;
  75        err = devlink_fmsg_u32_pair_put(fmsg, "test_u32", 3);
  76        if (err)
  77                return err;
  78        err = devlink_fmsg_u64_pair_put(fmsg, "test_u64", 4);
  79        if (err)
  80                return err;
  81        err = devlink_fmsg_string_pair_put(fmsg, "test_string", "somestring");
  82        if (err)
  83                return err;
  84
  85        binary = kmalloc(binary_len, GFP_KERNEL | __GFP_NOWARN);
  86        if (!binary)
  87                return -ENOMEM;
  88        get_random_bytes(binary, binary_len);
  89        err = devlink_fmsg_binary_pair_put(fmsg, "test_binary", binary, binary_len);
  90        kfree(binary);
  91        if (err)
  92                return err;
  93
  94        err = devlink_fmsg_pair_nest_start(fmsg, "test_nest");
  95        if (err)
  96                return err;
  97        err = devlink_fmsg_obj_nest_start(fmsg);
  98        if (err)
  99                return err;
 100        err = devlink_fmsg_bool_pair_put(fmsg, "nested_test_bool", false);
 101        if (err)
 102                return err;
 103        err = devlink_fmsg_u8_pair_put(fmsg, "nested_test_u8", false);
 104        if (err)
 105                return err;
 106        err = devlink_fmsg_obj_nest_end(fmsg);
 107        if (err)
 108                return err;
 109        err = devlink_fmsg_pair_nest_end(fmsg);
 110        if (err)
 111                return err;
 112
 113        err = devlink_fmsg_arr_pair_nest_end(fmsg);
 114        if (err)
 115                return err;
 116
 117        err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
 118        if (err)
 119                return err;
 120        for (i = 0; i < 10; i++) {
 121                err = devlink_fmsg_u32_put(fmsg, i);
 122                if (err)
 123                        return err;
 124        }
 125        err = devlink_fmsg_arr_pair_nest_end(fmsg);
 126        if (err)
 127                return err;
 128
 129        err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
 130        if (err)
 131                return err;
 132        for (i = 0; i < 10; i++) {
 133                err = devlink_fmsg_obj_nest_start(fmsg);
 134                if (err)
 135                        return err;
 136                err = devlink_fmsg_bool_pair_put(fmsg,
 137                                                 "in_array_nested_test_bool",
 138                                                 false);
 139                if (err)
 140                        return err;
 141                err = devlink_fmsg_u8_pair_put(fmsg,
 142                                               "in_array_nested_test_u8",
 143                                               i);
 144                if (err)
 145                        return err;
 146                err = devlink_fmsg_obj_nest_end(fmsg);
 147                if (err)
 148                        return err;
 149        }
 150        return devlink_fmsg_arr_pair_nest_end(fmsg);
 151}
 152
 153static int
 154nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
 155                             struct devlink_fmsg *fmsg, void *priv_ctx,
 156                             struct netlink_ext_ack *extack)
 157{
 158        struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
 159        struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
 160        int err;
 161
 162        if (ctx) {
 163                err = devlink_fmsg_string_pair_put(fmsg, "break_message",
 164                                                   ctx->break_msg);
 165                if (err)
 166                        return err;
 167        }
 168        return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
 169}
 170
 171static int
 172nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
 173                                 struct devlink_fmsg *fmsg,
 174                                 struct netlink_ext_ack *extack)
 175{
 176        struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
 177        int err;
 178
 179        if (health->recovered_break_msg) {
 180                err = devlink_fmsg_string_pair_put(fmsg,
 181                                                   "recovered_break_message",
 182                                                   health->recovered_break_msg);
 183                if (err)
 184                        return err;
 185        }
 186        return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
 187}
 188
 189static const
 190struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
 191        .name = "dummy",
 192        .recover = nsim_dev_dummy_reporter_recover,
 193        .dump = nsim_dev_dummy_reporter_dump,
 194        .diagnose = nsim_dev_dummy_reporter_diagnose,
 195};
 196
 197static ssize_t nsim_dev_health_break_write(struct file *file,
 198                                           const char __user *data,
 199                                           size_t count, loff_t *ppos)
 200{
 201        struct nsim_dev_health *health = file->private_data;
 202        struct nsim_dev_dummy_reporter_ctx ctx;
 203        char *break_msg;
 204        int err;
 205
 206        break_msg = memdup_user_nul(data, count);
 207        if (IS_ERR(break_msg))
 208                return PTR_ERR(break_msg);
 209
 210        if (break_msg[count - 1] == '\n')
 211                break_msg[count - 1] = '\0';
 212
 213        ctx.break_msg = break_msg;
 214        err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
 215        if (err)
 216                goto out;
 217
 218out:
 219        kfree(break_msg);
 220        return err ?: count;
 221}
 222
 223static const struct file_operations nsim_dev_health_break_fops = {
 224        .open = simple_open,
 225        .write = nsim_dev_health_break_write,
 226        .llseek = generic_file_llseek,
 227        .owner = THIS_MODULE,
 228};
 229
 230int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
 231{
 232        struct nsim_dev_health *health = &nsim_dev->health;
 233        int err;
 234
 235        health->empty_reporter =
 236                devlink_health_reporter_create(devlink,
 237                                               &nsim_dev_empty_reporter_ops,
 238                                               0, health);
 239        if (IS_ERR(health->empty_reporter))
 240                return PTR_ERR(health->empty_reporter);
 241
 242        health->dummy_reporter =
 243                devlink_health_reporter_create(devlink,
 244                                               &nsim_dev_dummy_reporter_ops,
 245                                               0, health);
 246        if (IS_ERR(health->dummy_reporter)) {
 247                err = PTR_ERR(health->dummy_reporter);
 248                goto err_empty_reporter_destroy;
 249        }
 250
 251        health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
 252        if (IS_ERR(health->ddir)) {
 253                err = PTR_ERR(health->ddir);
 254                goto err_dummy_reporter_destroy;
 255        }
 256
 257        health->recovered_break_msg = NULL;
 258        debugfs_create_file("break_health", 0200, health->ddir, health,
 259                            &nsim_dev_health_break_fops);
 260        health->binary_len = 16;
 261        debugfs_create_u32("binary_len", 0600, health->ddir,
 262                           &health->binary_len);
 263        health->fail_recover = false;
 264        debugfs_create_bool("fail_recover", 0600, health->ddir,
 265                            &health->fail_recover);
 266        return 0;
 267
 268err_dummy_reporter_destroy:
 269        devlink_health_reporter_destroy(health->dummy_reporter);
 270err_empty_reporter_destroy:
 271        devlink_health_reporter_destroy(health->empty_reporter);
 272        return err;
 273}
 274
 275void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
 276{
 277        struct nsim_dev_health *health = &nsim_dev->health;
 278
 279        debugfs_remove_recursive(health->ddir);
 280        kfree(health->recovered_break_msg);
 281        devlink_health_reporter_destroy(health->dummy_reporter);
 282        devlink_health_reporter_destroy(health->empty_reporter);
 283}
 284