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_start(fmsg, "test_bool_array");
 114        if (err)
 115                return err;
 116        for (i = 0; i < 10; i++) {
 117                err = devlink_fmsg_bool_put(fmsg, true);
 118                if (err)
 119                        return err;
 120        }
 121        err = devlink_fmsg_arr_pair_nest_end(fmsg);
 122        if (err)
 123                return err;
 124
 125        err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u8_array");
 126        if (err)
 127                return err;
 128        for (i = 0; i < 10; i++) {
 129                err = devlink_fmsg_u8_put(fmsg, i);
 130                if (err)
 131                        return err;
 132        }
 133        err = devlink_fmsg_arr_pair_nest_end(fmsg);
 134        if (err)
 135                return err;
 136
 137        err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u32_array");
 138        if (err)
 139                return err;
 140        for (i = 0; i < 10; i++) {
 141                err = devlink_fmsg_u32_put(fmsg, i);
 142                if (err)
 143                        return err;
 144        }
 145        err = devlink_fmsg_arr_pair_nest_end(fmsg);
 146        if (err)
 147                return err;
 148
 149        err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_u64_array");
 150        if (err)
 151                return err;
 152        for (i = 0; i < 10; i++) {
 153                err = devlink_fmsg_u64_put(fmsg, i);
 154                if (err)
 155                        return err;
 156        }
 157        err = devlink_fmsg_arr_pair_nest_end(fmsg);
 158        if (err)
 159                return err;
 160
 161        err = devlink_fmsg_arr_pair_nest_start(fmsg, "test_array_of_objects");
 162        if (err)
 163                return err;
 164        for (i = 0; i < 10; i++) {
 165                err = devlink_fmsg_obj_nest_start(fmsg);
 166                if (err)
 167                        return err;
 168                err = devlink_fmsg_bool_pair_put(fmsg,
 169                                                 "in_array_nested_test_bool",
 170                                                 false);
 171                if (err)
 172                        return err;
 173                err = devlink_fmsg_u8_pair_put(fmsg,
 174                                               "in_array_nested_test_u8",
 175                                               i);
 176                if (err)
 177                        return err;
 178                err = devlink_fmsg_obj_nest_end(fmsg);
 179                if (err)
 180                        return err;
 181        }
 182        return devlink_fmsg_arr_pair_nest_end(fmsg);
 183}
 184
 185static int
 186nsim_dev_dummy_reporter_dump(struct devlink_health_reporter *reporter,
 187                             struct devlink_fmsg *fmsg, void *priv_ctx,
 188                             struct netlink_ext_ack *extack)
 189{
 190        struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
 191        struct nsim_dev_dummy_reporter_ctx *ctx = priv_ctx;
 192        int err;
 193
 194        if (ctx) {
 195                err = devlink_fmsg_string_pair_put(fmsg, "break_message",
 196                                                   ctx->break_msg);
 197                if (err)
 198                        return err;
 199        }
 200        return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
 201}
 202
 203static int
 204nsim_dev_dummy_reporter_diagnose(struct devlink_health_reporter *reporter,
 205                                 struct devlink_fmsg *fmsg,
 206                                 struct netlink_ext_ack *extack)
 207{
 208        struct nsim_dev_health *health = devlink_health_reporter_priv(reporter);
 209        int err;
 210
 211        if (health->recovered_break_msg) {
 212                err = devlink_fmsg_string_pair_put(fmsg,
 213                                                   "recovered_break_message",
 214                                                   health->recovered_break_msg);
 215                if (err)
 216                        return err;
 217        }
 218        return nsim_dev_dummy_fmsg_put(fmsg, health->binary_len);
 219}
 220
 221static const
 222struct devlink_health_reporter_ops nsim_dev_dummy_reporter_ops = {
 223        .name = "dummy",
 224        .recover = nsim_dev_dummy_reporter_recover,
 225        .dump = nsim_dev_dummy_reporter_dump,
 226        .diagnose = nsim_dev_dummy_reporter_diagnose,
 227};
 228
 229static ssize_t nsim_dev_health_break_write(struct file *file,
 230                                           const char __user *data,
 231                                           size_t count, loff_t *ppos)
 232{
 233        struct nsim_dev_health *health = file->private_data;
 234        struct nsim_dev_dummy_reporter_ctx ctx;
 235        char *break_msg;
 236        int err;
 237
 238        break_msg = memdup_user_nul(data, count);
 239        if (IS_ERR(break_msg))
 240                return PTR_ERR(break_msg);
 241
 242        if (break_msg[count - 1] == '\n')
 243                break_msg[count - 1] = '\0';
 244
 245        ctx.break_msg = break_msg;
 246        err = devlink_health_report(health->dummy_reporter, break_msg, &ctx);
 247        if (err)
 248                goto out;
 249
 250out:
 251        kfree(break_msg);
 252        return err ?: count;
 253}
 254
 255static const struct file_operations nsim_dev_health_break_fops = {
 256        .open = simple_open,
 257        .write = nsim_dev_health_break_write,
 258        .llseek = generic_file_llseek,
 259        .owner = THIS_MODULE,
 260};
 261
 262int nsim_dev_health_init(struct nsim_dev *nsim_dev, struct devlink *devlink)
 263{
 264        struct nsim_dev_health *health = &nsim_dev->health;
 265        int err;
 266
 267        health->empty_reporter =
 268                devlink_health_reporter_create(devlink,
 269                                               &nsim_dev_empty_reporter_ops,
 270                                               0, health);
 271        if (IS_ERR(health->empty_reporter))
 272                return PTR_ERR(health->empty_reporter);
 273
 274        health->dummy_reporter =
 275                devlink_health_reporter_create(devlink,
 276                                               &nsim_dev_dummy_reporter_ops,
 277                                               0, health);
 278        if (IS_ERR(health->dummy_reporter)) {
 279                err = PTR_ERR(health->dummy_reporter);
 280                goto err_empty_reporter_destroy;
 281        }
 282
 283        health->ddir = debugfs_create_dir("health", nsim_dev->ddir);
 284        if (IS_ERR(health->ddir)) {
 285                err = PTR_ERR(health->ddir);
 286                goto err_dummy_reporter_destroy;
 287        }
 288
 289        health->recovered_break_msg = NULL;
 290        debugfs_create_file("break_health", 0200, health->ddir, health,
 291                            &nsim_dev_health_break_fops);
 292        health->binary_len = 16;
 293        debugfs_create_u32("binary_len", 0600, health->ddir,
 294                           &health->binary_len);
 295        health->fail_recover = false;
 296        debugfs_create_bool("fail_recover", 0600, health->ddir,
 297                            &health->fail_recover);
 298        return 0;
 299
 300err_dummy_reporter_destroy:
 301        devlink_health_reporter_destroy(health->dummy_reporter);
 302err_empty_reporter_destroy:
 303        devlink_health_reporter_destroy(health->empty_reporter);
 304        return err;
 305}
 306
 307void nsim_dev_health_exit(struct nsim_dev *nsim_dev)
 308{
 309        struct nsim_dev_health *health = &nsim_dev->health;
 310
 311        debugfs_remove_recursive(health->ddir);
 312        kfree(health->recovered_break_msg);
 313        devlink_health_reporter_destroy(health->dummy_reporter);
 314        devlink_health_reporter_destroy(health->empty_reporter);
 315}
 316