linux/drivers/net/netdevsim/dev.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
   3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
   4 * Copyright (c) 2019 Mellanox Technologies. All rights reserved.
   5 *
   6 * This software is licensed under the GNU General License Version 2,
   7 * June 1991 as shown in the file COPYING in the top-level directory of this
   8 * source tree.
   9 *
  10 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
  11 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  12 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  13 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
  14 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
  15 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
  16 */
  17
  18#include <linux/debugfs.h>
  19#include <linux/device.h>
  20#include <linux/list.h>
  21#include <linux/mutex.h>
  22#include <linux/random.h>
  23#include <linux/rtnetlink.h>
  24#include <net/devlink.h>
  25
  26#include "netdevsim.h"
  27
  28static struct dentry *nsim_dev_ddir;
  29
  30static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
  31{
  32        char dev_ddir_name[16];
  33
  34        sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
  35        nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
  36        if (IS_ERR_OR_NULL(nsim_dev->ddir))
  37                return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL;
  38        nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
  39        if (IS_ERR_OR_NULL(nsim_dev->ports_ddir))
  40                return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL;
  41        debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
  42                            &nsim_dev->fw_update_status);
  43        return 0;
  44}
  45
  46static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
  47{
  48        debugfs_remove_recursive(nsim_dev->ports_ddir);
  49        debugfs_remove_recursive(nsim_dev->ddir);
  50}
  51
  52static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
  53                                      struct nsim_dev_port *nsim_dev_port)
  54{
  55        char port_ddir_name[16];
  56        char dev_link_name[32];
  57
  58        sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
  59        nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
  60                                                 nsim_dev->ports_ddir);
  61        if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
  62                return -ENOMEM;
  63
  64        sprintf(dev_link_name, "../../../" DRV_NAME "%u",
  65                nsim_dev->nsim_bus_dev->dev.id);
  66        debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);
  67
  68        return 0;
  69}
  70
  71static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
  72{
  73        debugfs_remove_recursive(nsim_dev_port->ddir);
  74}
  75
  76static struct net *nsim_devlink_net(struct devlink *devlink)
  77{
  78        return &init_net;
  79}
  80
  81static u64 nsim_dev_ipv4_fib_resource_occ_get(void *priv)
  82{
  83        struct net *net = priv;
  84
  85        return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
  86}
  87
  88static u64 nsim_dev_ipv4_fib_rules_res_occ_get(void *priv)
  89{
  90        struct net *net = priv;
  91
  92        return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
  93}
  94
  95static u64 nsim_dev_ipv6_fib_resource_occ_get(void *priv)
  96{
  97        struct net *net = priv;
  98
  99        return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
 100}
 101
 102static u64 nsim_dev_ipv6_fib_rules_res_occ_get(void *priv)
 103{
 104        struct net *net = priv;
 105
 106        return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
 107}
 108
 109static int nsim_dev_resources_register(struct devlink *devlink)
 110{
 111        struct devlink_resource_size_params params = {
 112                .size_max = (u64)-1,
 113                .size_granularity = 1,
 114                .unit = DEVLINK_RESOURCE_UNIT_ENTRY
 115        };
 116        struct net *net = nsim_devlink_net(devlink);
 117        int err;
 118        u64 n;
 119
 120        /* Resources for IPv4 */
 121        err = devlink_resource_register(devlink, "IPv4", (u64)-1,
 122                                        NSIM_RESOURCE_IPV4,
 123                                        DEVLINK_RESOURCE_ID_PARENT_TOP,
 124                                        &params);
 125        if (err) {
 126                pr_err("Failed to register IPv4 top resource\n");
 127                goto out;
 128        }
 129
 130        n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
 131        err = devlink_resource_register(devlink, "fib", n,
 132                                        NSIM_RESOURCE_IPV4_FIB,
 133                                        NSIM_RESOURCE_IPV4, &params);
 134        if (err) {
 135                pr_err("Failed to register IPv4 FIB resource\n");
 136                return err;
 137        }
 138
 139        n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
 140        err = devlink_resource_register(devlink, "fib-rules", n,
 141                                        NSIM_RESOURCE_IPV4_FIB_RULES,
 142                                        NSIM_RESOURCE_IPV4, &params);
 143        if (err) {
 144                pr_err("Failed to register IPv4 FIB rules resource\n");
 145                return err;
 146        }
 147
 148        /* Resources for IPv6 */
 149        err = devlink_resource_register(devlink, "IPv6", (u64)-1,
 150                                        NSIM_RESOURCE_IPV6,
 151                                        DEVLINK_RESOURCE_ID_PARENT_TOP,
 152                                        &params);
 153        if (err) {
 154                pr_err("Failed to register IPv6 top resource\n");
 155                goto out;
 156        }
 157
 158        n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
 159        err = devlink_resource_register(devlink, "fib", n,
 160                                        NSIM_RESOURCE_IPV6_FIB,
 161                                        NSIM_RESOURCE_IPV6, &params);
 162        if (err) {
 163                pr_err("Failed to register IPv6 FIB resource\n");
 164                return err;
 165        }
 166
 167        n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
 168        err = devlink_resource_register(devlink, "fib-rules", n,
 169                                        NSIM_RESOURCE_IPV6_FIB_RULES,
 170                                        NSIM_RESOURCE_IPV6, &params);
 171        if (err) {
 172                pr_err("Failed to register IPv6 FIB rules resource\n");
 173                return err;
 174        }
 175
 176        devlink_resource_occ_get_register(devlink,
 177                                          NSIM_RESOURCE_IPV4_FIB,
 178                                          nsim_dev_ipv4_fib_resource_occ_get,
 179                                          net);
 180        devlink_resource_occ_get_register(devlink,
 181                                          NSIM_RESOURCE_IPV4_FIB_RULES,
 182                                          nsim_dev_ipv4_fib_rules_res_occ_get,
 183                                          net);
 184        devlink_resource_occ_get_register(devlink,
 185                                          NSIM_RESOURCE_IPV6_FIB,
 186                                          nsim_dev_ipv6_fib_resource_occ_get,
 187                                          net);
 188        devlink_resource_occ_get_register(devlink,
 189                                          NSIM_RESOURCE_IPV6_FIB_RULES,
 190                                          nsim_dev_ipv6_fib_rules_res_occ_get,
 191                                          net);
 192out:
 193        return err;
 194}
 195
 196static int nsim_dev_reload(struct devlink *devlink,
 197                           struct netlink_ext_ack *extack)
 198{
 199        enum nsim_resource_id res_ids[] = {
 200                NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
 201                NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
 202        };
 203        struct net *net = nsim_devlink_net(devlink);
 204        int i;
 205
 206        for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
 207                int err;
 208                u64 val;
 209
 210                err = devlink_resource_size_get(devlink, res_ids[i], &val);
 211                if (!err) {
 212                        err = nsim_fib_set_max(net, res_ids[i], val, extack);
 213                        if (err)
 214                                return err;
 215                }
 216        }
 217
 218        return 0;
 219}
 220
 221#define NSIM_DEV_FLASH_SIZE 500000
 222#define NSIM_DEV_FLASH_CHUNK_SIZE 1000
 223#define NSIM_DEV_FLASH_CHUNK_TIME_MS 10
 224
 225static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
 226                                 const char *component,
 227                                 struct netlink_ext_ack *extack)
 228{
 229        struct nsim_dev *nsim_dev = devlink_priv(devlink);
 230        int i;
 231
 232        if (nsim_dev->fw_update_status) {
 233                devlink_flash_update_begin_notify(devlink);
 234                devlink_flash_update_status_notify(devlink,
 235                                                   "Preparing to flash",
 236                                                   component, 0, 0);
 237        }
 238
 239        for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
 240                if (nsim_dev->fw_update_status)
 241                        devlink_flash_update_status_notify(devlink, "Flashing",
 242                                                           component,
 243                                                           i * NSIM_DEV_FLASH_CHUNK_SIZE,
 244                                                           NSIM_DEV_FLASH_SIZE);
 245                msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
 246        }
 247
 248        if (nsim_dev->fw_update_status) {
 249                devlink_flash_update_status_notify(devlink, "Flashing",
 250                                                   component,
 251                                                   NSIM_DEV_FLASH_SIZE,
 252                                                   NSIM_DEV_FLASH_SIZE);
 253                devlink_flash_update_status_notify(devlink, "Flashing done",
 254                                                   component, 0, 0);
 255                devlink_flash_update_end_notify(devlink);
 256        }
 257
 258        return 0;
 259}
 260
 261static const struct devlink_ops nsim_dev_devlink_ops = {
 262        .reload = nsim_dev_reload,
 263        .flash_update = nsim_dev_flash_update,
 264};
 265
 266static struct nsim_dev *
 267nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
 268{
 269        struct nsim_dev *nsim_dev;
 270        struct devlink *devlink;
 271        int err;
 272
 273        devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
 274        if (!devlink)
 275                return ERR_PTR(-ENOMEM);
 276        nsim_dev = devlink_priv(devlink);
 277        nsim_dev->nsim_bus_dev = nsim_bus_dev;
 278        nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
 279        get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
 280        INIT_LIST_HEAD(&nsim_dev->port_list);
 281        mutex_init(&nsim_dev->port_list_lock);
 282        nsim_dev->fw_update_status = true;
 283
 284        err = nsim_dev_resources_register(devlink);
 285        if (err)
 286                goto err_devlink_free;
 287
 288        err = devlink_register(devlink, &nsim_bus_dev->dev);
 289        if (err)
 290                goto err_resources_unregister;
 291
 292        err = nsim_dev_debugfs_init(nsim_dev);
 293        if (err)
 294                goto err_dl_unregister;
 295
 296        err = nsim_bpf_dev_init(nsim_dev);
 297        if (err)
 298                goto err_debugfs_exit;
 299
 300        return nsim_dev;
 301
 302err_debugfs_exit:
 303        nsim_dev_debugfs_exit(nsim_dev);
 304err_dl_unregister:
 305        devlink_unregister(devlink);
 306err_resources_unregister:
 307        devlink_resources_unregister(devlink, NULL);
 308err_devlink_free:
 309        devlink_free(devlink);
 310        return ERR_PTR(err);
 311}
 312
 313static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
 314{
 315        struct devlink *devlink = priv_to_devlink(nsim_dev);
 316
 317        nsim_bpf_dev_exit(nsim_dev);
 318        nsim_dev_debugfs_exit(nsim_dev);
 319        devlink_unregister(devlink);
 320        devlink_resources_unregister(devlink, NULL);
 321        mutex_destroy(&nsim_dev->port_list_lock);
 322        devlink_free(devlink);
 323}
 324
 325static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
 326                               unsigned int port_index)
 327{
 328        struct nsim_dev_port *nsim_dev_port;
 329        struct devlink_port *devlink_port;
 330        int err;
 331
 332        nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
 333        if (!nsim_dev_port)
 334                return -ENOMEM;
 335        nsim_dev_port->port_index = port_index;
 336
 337        devlink_port = &nsim_dev_port->devlink_port;
 338        devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
 339                               port_index + 1, 0, 0,
 340                               nsim_dev->switch_id.id,
 341                               nsim_dev->switch_id.id_len);
 342        err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
 343                                    port_index);
 344        if (err)
 345                goto err_port_free;
 346
 347        err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
 348        if (err)
 349                goto err_dl_port_unregister;
 350
 351        nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
 352        if (IS_ERR(nsim_dev_port->ns)) {
 353                err = PTR_ERR(nsim_dev_port->ns);
 354                goto err_port_debugfs_exit;
 355        }
 356
 357        devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
 358        list_add(&nsim_dev_port->list, &nsim_dev->port_list);
 359
 360        return 0;
 361
 362err_port_debugfs_exit:
 363        nsim_dev_port_debugfs_exit(nsim_dev_port);
 364err_dl_port_unregister:
 365        devlink_port_unregister(devlink_port);
 366err_port_free:
 367        kfree(nsim_dev_port);
 368        return err;
 369}
 370
 371static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
 372{
 373        struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
 374
 375        list_del(&nsim_dev_port->list);
 376        devlink_port_type_clear(devlink_port);
 377        nsim_destroy(nsim_dev_port->ns);
 378        nsim_dev_port_debugfs_exit(nsim_dev_port);
 379        devlink_port_unregister(devlink_port);
 380        kfree(nsim_dev_port);
 381}
 382
 383static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
 384{
 385        struct nsim_dev_port *nsim_dev_port, *tmp;
 386
 387        list_for_each_entry_safe(nsim_dev_port, tmp,
 388                                 &nsim_dev->port_list, list)
 389                __nsim_dev_port_del(nsim_dev_port);
 390}
 391
 392int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
 393{
 394        struct nsim_dev *nsim_dev;
 395        int i;
 396        int err;
 397
 398        nsim_dev = nsim_dev_create(nsim_bus_dev, nsim_bus_dev->port_count);
 399        if (IS_ERR(nsim_dev))
 400                return PTR_ERR(nsim_dev);
 401        dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
 402
 403        for (i = 0; i < nsim_bus_dev->port_count; i++) {
 404                err = __nsim_dev_port_add(nsim_dev, i);
 405                if (err)
 406                        goto err_port_del_all;
 407        }
 408        return 0;
 409
 410err_port_del_all:
 411        nsim_dev_port_del_all(nsim_dev);
 412        nsim_dev_destroy(nsim_dev);
 413        return err;
 414}
 415
 416void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
 417{
 418        struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
 419
 420        nsim_dev_port_del_all(nsim_dev);
 421        nsim_dev_destroy(nsim_dev);
 422}
 423
 424static struct nsim_dev_port *
 425__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
 426{
 427        struct nsim_dev_port *nsim_dev_port;
 428
 429        list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
 430                if (nsim_dev_port->port_index == port_index)
 431                        return nsim_dev_port;
 432        return NULL;
 433}
 434
 435int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
 436                      unsigned int port_index)
 437{
 438        struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
 439        int err;
 440
 441        mutex_lock(&nsim_dev->port_list_lock);
 442        if (__nsim_dev_port_lookup(nsim_dev, port_index))
 443                err = -EEXIST;
 444        else
 445                err = __nsim_dev_port_add(nsim_dev, port_index);
 446        mutex_unlock(&nsim_dev->port_list_lock);
 447        return err;
 448}
 449
 450int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
 451                      unsigned int port_index)
 452{
 453        struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
 454        struct nsim_dev_port *nsim_dev_port;
 455        int err = 0;
 456
 457        mutex_lock(&nsim_dev->port_list_lock);
 458        nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
 459        if (!nsim_dev_port)
 460                err = -ENOENT;
 461        else
 462                __nsim_dev_port_del(nsim_dev_port);
 463        mutex_unlock(&nsim_dev->port_list_lock);
 464        return err;
 465}
 466
 467int nsim_dev_init(void)
 468{
 469        nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
 470        if (IS_ERR_OR_NULL(nsim_dev_ddir))
 471                return -ENOMEM;
 472        return 0;
 473}
 474
 475void nsim_dev_exit(void)
 476{
 477        debugfs_remove_recursive(nsim_dev_ddir);
 478}
 479