linux/drivers/block/rnbd/rnbd-clt-sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * RDMA Network Block Driver
   4 *
   5 * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
   6 * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
   7 * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
   8 */
   9
  10#undef pr_fmt
  11#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
  12
  13#include <linux/types.h>
  14#include <linux/ctype.h>
  15#include <linux/parser.h>
  16#include <linux/module.h>
  17#include <linux/in6.h>
  18#include <linux/fs.h>
  19#include <linux/uaccess.h>
  20#include <linux/device.h>
  21#include <rdma/ib.h>
  22#include <rdma/rdma_cm.h>
  23
  24#include "rnbd-clt.h"
  25
  26static struct device *rnbd_dev;
  27static struct class *rnbd_dev_class;
  28static struct kobject *rnbd_devs_kobj;
  29
  30enum {
  31        RNBD_OPT_ERR            = 0,
  32        RNBD_OPT_DEST_PORT      = 1 << 0,
  33        RNBD_OPT_PATH           = 1 << 1,
  34        RNBD_OPT_DEV_PATH       = 1 << 2,
  35        RNBD_OPT_ACCESS_MODE    = 1 << 3,
  36        RNBD_OPT_SESSNAME       = 1 << 6,
  37};
  38
  39static const unsigned int rnbd_opt_mandatory[] = {
  40        RNBD_OPT_DEV_PATH,
  41        RNBD_OPT_SESSNAME,
  42};
  43
  44static const match_table_t rnbd_opt_tokens = {
  45        {RNBD_OPT_PATH,         "path=%s"       },
  46        {RNBD_OPT_DEV_PATH,     "device_path=%s"},
  47        {RNBD_OPT_DEST_PORT,    "dest_port=%d"  },
  48        {RNBD_OPT_ACCESS_MODE,  "access_mode=%s"},
  49        {RNBD_OPT_SESSNAME,     "sessname=%s"   },
  50        {RNBD_OPT_ERR,          NULL            },
  51};
  52
  53struct rnbd_map_options {
  54        char *sessname;
  55        struct rtrs_addr *paths;
  56        size_t *path_cnt;
  57        char *pathname;
  58        u16 *dest_port;
  59        enum rnbd_access_mode *access_mode;
  60};
  61
  62static int rnbd_clt_parse_map_options(const char *buf, size_t max_path_cnt,
  63                                       struct rnbd_map_options *opt)
  64{
  65        char *options, *sep_opt;
  66        char *p;
  67        substring_t args[MAX_OPT_ARGS];
  68        int opt_mask = 0;
  69        int token;
  70        int ret = -EINVAL;
  71        int i, dest_port;
  72        int p_cnt = 0;
  73
  74        options = kstrdup(buf, GFP_KERNEL);
  75        if (!options)
  76                return -ENOMEM;
  77
  78        sep_opt = strstrip(options);
  79        while ((p = strsep(&sep_opt, " ")) != NULL) {
  80                if (!*p)
  81                        continue;
  82
  83                token = match_token(p, rnbd_opt_tokens, args);
  84                opt_mask |= token;
  85
  86                switch (token) {
  87                case RNBD_OPT_SESSNAME:
  88                        p = match_strdup(args);
  89                        if (!p) {
  90                                ret = -ENOMEM;
  91                                goto out;
  92                        }
  93                        if (strlen(p) > NAME_MAX) {
  94                                pr_err("map_device: sessname too long\n");
  95                                ret = -EINVAL;
  96                                kfree(p);
  97                                goto out;
  98                        }
  99                        strlcpy(opt->sessname, p, NAME_MAX);
 100                        kfree(p);
 101                        break;
 102
 103                case RNBD_OPT_PATH:
 104                        if (p_cnt >= max_path_cnt) {
 105                                pr_err("map_device: too many (> %zu) paths provided\n",
 106                                       max_path_cnt);
 107                                ret = -ENOMEM;
 108                                goto out;
 109                        }
 110                        p = match_strdup(args);
 111                        if (!p) {
 112                                ret = -ENOMEM;
 113                                goto out;
 114                        }
 115
 116                        ret = rtrs_addr_to_sockaddr(p, strlen(p),
 117                                                    *opt->dest_port,
 118                                                    &opt->paths[p_cnt]);
 119                        if (ret) {
 120                                pr_err("Can't parse path %s: %d\n", p, ret);
 121                                kfree(p);
 122                                goto out;
 123                        }
 124
 125                        p_cnt++;
 126
 127                        kfree(p);
 128                        break;
 129
 130                case RNBD_OPT_DEV_PATH:
 131                        p = match_strdup(args);
 132                        if (!p) {
 133                                ret = -ENOMEM;
 134                                goto out;
 135                        }
 136                        if (strlen(p) > NAME_MAX) {
 137                                pr_err("map_device: Device path too long\n");
 138                                ret = -EINVAL;
 139                                kfree(p);
 140                                goto out;
 141                        }
 142                        strlcpy(opt->pathname, p, NAME_MAX);
 143                        kfree(p);
 144                        break;
 145
 146                case RNBD_OPT_DEST_PORT:
 147                        if (match_int(args, &dest_port) || dest_port < 0 ||
 148                            dest_port > 65535) {
 149                                pr_err("bad destination port number parameter '%d'\n",
 150                                       dest_port);
 151                                ret = -EINVAL;
 152                                goto out;
 153                        }
 154                        *opt->dest_port = dest_port;
 155                        break;
 156
 157                case RNBD_OPT_ACCESS_MODE:
 158                        p = match_strdup(args);
 159                        if (!p) {
 160                                ret = -ENOMEM;
 161                                goto out;
 162                        }
 163
 164                        if (!strcmp(p, "ro")) {
 165                                *opt->access_mode = RNBD_ACCESS_RO;
 166                        } else if (!strcmp(p, "rw")) {
 167                                *opt->access_mode = RNBD_ACCESS_RW;
 168                        } else if (!strcmp(p, "migration")) {
 169                                *opt->access_mode = RNBD_ACCESS_MIGRATION;
 170                        } else {
 171                                pr_err("map_device: Invalid access_mode: '%s'\n",
 172                                       p);
 173                                ret = -EINVAL;
 174                                kfree(p);
 175                                goto out;
 176                        }
 177
 178                        kfree(p);
 179                        break;
 180
 181                default:
 182                        pr_err("map_device: Unknown parameter or missing value '%s'\n",
 183                               p);
 184                        ret = -EINVAL;
 185                        goto out;
 186                }
 187        }
 188
 189        for (i = 0; i < ARRAY_SIZE(rnbd_opt_mandatory); i++) {
 190                if ((opt_mask & rnbd_opt_mandatory[i])) {
 191                        ret = 0;
 192                } else {
 193                        pr_err("map_device: Parameters missing\n");
 194                        ret = -EINVAL;
 195                        break;
 196                }
 197        }
 198
 199out:
 200        *opt->path_cnt = p_cnt;
 201        kfree(options);
 202        return ret;
 203}
 204
 205static ssize_t state_show(struct kobject *kobj,
 206                          struct kobj_attribute *attr, char *page)
 207{
 208        struct rnbd_clt_dev *dev;
 209
 210        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 211
 212        switch (dev->dev_state) {
 213        case DEV_STATE_INIT:
 214                return snprintf(page, PAGE_SIZE, "init\n");
 215        case DEV_STATE_MAPPED:
 216                /* TODO fix cli tool before changing to proper state */
 217                return snprintf(page, PAGE_SIZE, "open\n");
 218        case DEV_STATE_MAPPED_DISCONNECTED:
 219                /* TODO fix cli tool before changing to proper state */
 220                return snprintf(page, PAGE_SIZE, "closed\n");
 221        case DEV_STATE_UNMAPPED:
 222                return snprintf(page, PAGE_SIZE, "unmapped\n");
 223        default:
 224                return snprintf(page, PAGE_SIZE, "unknown\n");
 225        }
 226}
 227
 228static struct kobj_attribute rnbd_clt_state_attr = __ATTR_RO(state);
 229
 230static ssize_t mapping_path_show(struct kobject *kobj,
 231                                 struct kobj_attribute *attr, char *page)
 232{
 233        struct rnbd_clt_dev *dev;
 234
 235        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 236
 237        return scnprintf(page, PAGE_SIZE, "%s\n", dev->pathname);
 238}
 239
 240static struct kobj_attribute rnbd_clt_mapping_path_attr =
 241        __ATTR_RO(mapping_path);
 242
 243static ssize_t access_mode_show(struct kobject *kobj,
 244                                struct kobj_attribute *attr, char *page)
 245{
 246        struct rnbd_clt_dev *dev;
 247
 248        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 249
 250        return snprintf(page, PAGE_SIZE, "%s\n",
 251                        rnbd_access_mode_str(dev->access_mode));
 252}
 253
 254static struct kobj_attribute rnbd_clt_access_mode =
 255        __ATTR_RO(access_mode);
 256
 257static ssize_t rnbd_clt_unmap_dev_show(struct kobject *kobj,
 258                                        struct kobj_attribute *attr, char *page)
 259{
 260        return scnprintf(page, PAGE_SIZE, "Usage: echo <normal|force> > %s\n",
 261                         attr->attr.name);
 262}
 263
 264static ssize_t rnbd_clt_unmap_dev_store(struct kobject *kobj,
 265                                         struct kobj_attribute *attr,
 266                                         const char *buf, size_t count)
 267{
 268        struct rnbd_clt_dev *dev;
 269        char *opt, *options;
 270        bool force;
 271        int err;
 272
 273        opt = kstrdup(buf, GFP_KERNEL);
 274        if (!opt)
 275                return -ENOMEM;
 276
 277        options = strstrip(opt);
 278        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 279        if (sysfs_streq(options, "normal")) {
 280                force = false;
 281        } else if (sysfs_streq(options, "force")) {
 282                force = true;
 283        } else {
 284                rnbd_clt_err(dev,
 285                              "unmap_device: Invalid value: %s\n",
 286                              options);
 287                err = -EINVAL;
 288                goto out;
 289        }
 290
 291        rnbd_clt_info(dev, "Unmapping device, option: %s.\n",
 292                       force ? "force" : "normal");
 293
 294        /*
 295         * We take explicit module reference only for one reason: do not
 296         * race with lockless rnbd_destroy_sessions().
 297         */
 298        if (!try_module_get(THIS_MODULE)) {
 299                err = -ENODEV;
 300                goto out;
 301        }
 302        err = rnbd_clt_unmap_device(dev, force, &attr->attr);
 303        if (err) {
 304                if (err != -EALREADY)
 305                        rnbd_clt_err(dev, "unmap_device: %d\n",  err);
 306                goto module_put;
 307        }
 308
 309        /*
 310         * Here device can be vanished!
 311         */
 312
 313        err = count;
 314
 315module_put:
 316        module_put(THIS_MODULE);
 317out:
 318        kfree(opt);
 319
 320        return err;
 321}
 322
 323static struct kobj_attribute rnbd_clt_unmap_device_attr =
 324        __ATTR(unmap_device, 0644, rnbd_clt_unmap_dev_show,
 325               rnbd_clt_unmap_dev_store);
 326
 327static ssize_t rnbd_clt_resize_dev_show(struct kobject *kobj,
 328                                         struct kobj_attribute *attr,
 329                                         char *page)
 330{
 331        return scnprintf(page, PAGE_SIZE,
 332                         "Usage: echo <new size in sectors> > %s\n",
 333                         attr->attr.name);
 334}
 335
 336static ssize_t rnbd_clt_resize_dev_store(struct kobject *kobj,
 337                                          struct kobj_attribute *attr,
 338                                          const char *buf, size_t count)
 339{
 340        int ret;
 341        unsigned long sectors;
 342        struct rnbd_clt_dev *dev;
 343
 344        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 345
 346        ret = kstrtoul(buf, 0, &sectors);
 347        if (ret)
 348                return ret;
 349
 350        ret = rnbd_clt_resize_disk(dev, (size_t)sectors);
 351        if (ret)
 352                return ret;
 353
 354        return count;
 355}
 356
 357static struct kobj_attribute rnbd_clt_resize_dev_attr =
 358        __ATTR(resize, 0644, rnbd_clt_resize_dev_show,
 359               rnbd_clt_resize_dev_store);
 360
 361static ssize_t rnbd_clt_remap_dev_show(struct kobject *kobj,
 362                                        struct kobj_attribute *attr, char *page)
 363{
 364        return scnprintf(page, PAGE_SIZE, "Usage: echo <1> > %s\n",
 365                         attr->attr.name);
 366}
 367
 368static ssize_t rnbd_clt_remap_dev_store(struct kobject *kobj,
 369                                         struct kobj_attribute *attr,
 370                                         const char *buf, size_t count)
 371{
 372        struct rnbd_clt_dev *dev;
 373        char *opt, *options;
 374        int err;
 375
 376        opt = kstrdup(buf, GFP_KERNEL);
 377        if (!opt)
 378                return -ENOMEM;
 379
 380        options = strstrip(opt);
 381        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 382        if (!sysfs_streq(options, "1")) {
 383                rnbd_clt_err(dev,
 384                              "remap_device: Invalid value: %s\n",
 385                              options);
 386                err = -EINVAL;
 387                goto out;
 388        }
 389        err = rnbd_clt_remap_device(dev);
 390        if (likely(!err))
 391                err = count;
 392
 393out:
 394        kfree(opt);
 395
 396        return err;
 397}
 398
 399static struct kobj_attribute rnbd_clt_remap_device_attr =
 400        __ATTR(remap_device, 0644, rnbd_clt_remap_dev_show,
 401               rnbd_clt_remap_dev_store);
 402
 403static ssize_t session_show(struct kobject *kobj, struct kobj_attribute *attr,
 404                            char *page)
 405{
 406        struct rnbd_clt_dev *dev;
 407
 408        dev = container_of(kobj, struct rnbd_clt_dev, kobj);
 409
 410        return scnprintf(page, PAGE_SIZE, "%s\n", dev->sess->sessname);
 411}
 412
 413static struct kobj_attribute rnbd_clt_session_attr =
 414        __ATTR_RO(session);
 415
 416static struct attribute *rnbd_dev_attrs[] = {
 417        &rnbd_clt_unmap_device_attr.attr,
 418        &rnbd_clt_resize_dev_attr.attr,
 419        &rnbd_clt_remap_device_attr.attr,
 420        &rnbd_clt_mapping_path_attr.attr,
 421        &rnbd_clt_state_attr.attr,
 422        &rnbd_clt_session_attr.attr,
 423        &rnbd_clt_access_mode.attr,
 424        NULL,
 425};
 426
 427void rnbd_clt_remove_dev_symlink(struct rnbd_clt_dev *dev)
 428{
 429        /*
 430         * The module unload rnbd_client_exit path is racing with unmapping of
 431         * the last single device from the sysfs manually
 432         * i.e. rnbd_clt_unmap_dev_store() leading to a sysfs warning because
 433         * of sysfs link already was removed already.
 434         */
 435        if (dev->blk_symlink_name && try_module_get(THIS_MODULE)) {
 436                sysfs_remove_link(rnbd_devs_kobj, dev->blk_symlink_name);
 437                kfree(dev->blk_symlink_name);
 438                module_put(THIS_MODULE);
 439        }
 440}
 441
 442static struct kobj_type rnbd_dev_ktype = {
 443        .sysfs_ops      = &kobj_sysfs_ops,
 444        .default_attrs  = rnbd_dev_attrs,
 445};
 446
 447static int rnbd_clt_add_dev_kobj(struct rnbd_clt_dev *dev)
 448{
 449        int ret;
 450        struct kobject *gd_kobj = &disk_to_dev(dev->gd)->kobj;
 451
 452        ret = kobject_init_and_add(&dev->kobj, &rnbd_dev_ktype, gd_kobj, "%s",
 453                                   "rnbd");
 454        if (ret) {
 455                rnbd_clt_err(dev, "Failed to create device sysfs dir, err: %d\n",
 456                              ret);
 457                kobject_put(&dev->kobj);
 458        }
 459
 460        return ret;
 461}
 462
 463static ssize_t rnbd_clt_map_device_show(struct kobject *kobj,
 464                                         struct kobj_attribute *attr,
 465                                         char *page)
 466{
 467        return scnprintf(page, PAGE_SIZE,
 468                         "Usage: echo \"[dest_port=server port number] sessname=<name of the rtrs session> path=<[srcaddr@]dstaddr> [path=<[srcaddr@]dstaddr>] device_path=<full path on remote side> [access_mode=<ro|rw|migration>]\" > %s\n\naddr ::= [ ip:<ipv4> | ip:<ipv6> | gid:<gid> ]\n",
 469                         attr->attr.name);
 470}
 471
 472static int rnbd_clt_get_path_name(struct rnbd_clt_dev *dev, char *buf,
 473                                   size_t len)
 474{
 475        int ret;
 476        char pathname[NAME_MAX], *s;
 477
 478        strlcpy(pathname, dev->pathname, sizeof(pathname));
 479        while ((s = strchr(pathname, '/')))
 480                s[0] = '!';
 481
 482        ret = snprintf(buf, len, "%s", pathname);
 483        if (ret >= len)
 484                return -ENAMETOOLONG;
 485
 486        ret = snprintf(buf, len, "%s@%s", buf, dev->sess->sessname);
 487        if (ret >= len)
 488                return -ENAMETOOLONG;
 489
 490        return 0;
 491}
 492
 493static int rnbd_clt_add_dev_symlink(struct rnbd_clt_dev *dev)
 494{
 495        struct kobject *gd_kobj = &disk_to_dev(dev->gd)->kobj;
 496        int ret, len;
 497
 498        len = strlen(dev->pathname) + strlen(dev->sess->sessname) + 2;
 499        dev->blk_symlink_name = kzalloc(len, GFP_KERNEL);
 500        if (!dev->blk_symlink_name) {
 501                rnbd_clt_err(dev, "Failed to allocate memory for blk_symlink_name\n");
 502                return -ENOMEM;
 503        }
 504
 505        ret = rnbd_clt_get_path_name(dev, dev->blk_symlink_name,
 506                                      len);
 507        if (ret) {
 508                rnbd_clt_err(dev, "Failed to get /sys/block symlink path, err: %d\n",
 509                              ret);
 510                goto out_err;
 511        }
 512
 513        ret = sysfs_create_link(rnbd_devs_kobj, gd_kobj,
 514                                dev->blk_symlink_name);
 515        if (ret) {
 516                rnbd_clt_err(dev, "Creating /sys/block symlink failed, err: %d\n",
 517                              ret);
 518                goto out_err;
 519        }
 520
 521        return 0;
 522
 523out_err:
 524        kfree(dev->blk_symlink_name);
 525        dev->blk_symlink_name = NULL ;
 526        return ret;
 527}
 528
 529static ssize_t rnbd_clt_map_device_store(struct kobject *kobj,
 530                                          struct kobj_attribute *attr,
 531                                          const char *buf, size_t count)
 532{
 533        struct rnbd_clt_dev *dev;
 534        struct rnbd_map_options opt;
 535        int ret;
 536        char pathname[NAME_MAX];
 537        char sessname[NAME_MAX];
 538        enum rnbd_access_mode access_mode = RNBD_ACCESS_RW;
 539        u16 port_nr = RTRS_PORT;
 540
 541        struct sockaddr_storage *addrs;
 542        struct rtrs_addr paths[6];
 543        size_t path_cnt;
 544
 545        opt.sessname = sessname;
 546        opt.paths = paths;
 547        opt.path_cnt = &path_cnt;
 548        opt.pathname = pathname;
 549        opt.dest_port = &port_nr;
 550        opt.access_mode = &access_mode;
 551        addrs = kcalloc(ARRAY_SIZE(paths) * 2, sizeof(*addrs), GFP_KERNEL);
 552        if (!addrs)
 553                return -ENOMEM;
 554
 555        for (path_cnt = 0; path_cnt < ARRAY_SIZE(paths); path_cnt++) {
 556                paths[path_cnt].src = &addrs[path_cnt * 2];
 557                paths[path_cnt].dst = &addrs[path_cnt * 2 + 1];
 558        }
 559
 560        ret = rnbd_clt_parse_map_options(buf, ARRAY_SIZE(paths), &opt);
 561        if (ret)
 562                goto out;
 563
 564        pr_info("Mapping device %s on session %s, (access_mode: %s)\n",
 565                pathname, sessname,
 566                rnbd_access_mode_str(access_mode));
 567
 568        dev = rnbd_clt_map_device(sessname, paths, path_cnt, port_nr, pathname,
 569                                  access_mode);
 570        if (IS_ERR(dev)) {
 571                ret = PTR_ERR(dev);
 572                goto out;
 573        }
 574
 575        ret = rnbd_clt_add_dev_kobj(dev);
 576        if (ret)
 577                goto unmap_dev;
 578
 579        ret = rnbd_clt_add_dev_symlink(dev);
 580        if (ret)
 581                goto unmap_dev;
 582
 583        kfree(addrs);
 584        return count;
 585
 586unmap_dev:
 587        rnbd_clt_unmap_device(dev, true, NULL);
 588out:
 589        kfree(addrs);
 590        return ret;
 591}
 592
 593static struct kobj_attribute rnbd_clt_map_device_attr =
 594        __ATTR(map_device, 0644,
 595               rnbd_clt_map_device_show, rnbd_clt_map_device_store);
 596
 597static struct attribute *default_attrs[] = {
 598        &rnbd_clt_map_device_attr.attr,
 599        NULL,
 600};
 601
 602static struct attribute_group default_attr_group = {
 603        .attrs = default_attrs,
 604};
 605
 606static const struct attribute_group *default_attr_groups[] = {
 607        &default_attr_group,
 608        NULL,
 609};
 610
 611int rnbd_clt_create_sysfs_files(void)
 612{
 613        int err;
 614
 615        rnbd_dev_class = class_create(THIS_MODULE, "rnbd-client");
 616        if (IS_ERR(rnbd_dev_class))
 617                return PTR_ERR(rnbd_dev_class);
 618
 619        rnbd_dev = device_create_with_groups(rnbd_dev_class, NULL,
 620                                              MKDEV(0, 0), NULL,
 621                                              default_attr_groups, "ctl");
 622        if (IS_ERR(rnbd_dev)) {
 623                err = PTR_ERR(rnbd_dev);
 624                goto cls_destroy;
 625        }
 626        rnbd_devs_kobj = kobject_create_and_add("devices", &rnbd_dev->kobj);
 627        if (!rnbd_devs_kobj) {
 628                err = -ENOMEM;
 629                goto dev_destroy;
 630        }
 631
 632        return 0;
 633
 634dev_destroy:
 635        device_destroy(rnbd_dev_class, MKDEV(0, 0));
 636cls_destroy:
 637        class_destroy(rnbd_dev_class);
 638
 639        return err;
 640}
 641
 642void rnbd_clt_destroy_default_group(void)
 643{
 644        sysfs_remove_group(&rnbd_dev->kobj, &default_attr_group);
 645}
 646
 647void rnbd_clt_destroy_sysfs_files(void)
 648{
 649        kobject_del(rnbd_devs_kobj);
 650        kobject_put(rnbd_devs_kobj);
 651        device_destroy(rnbd_dev_class, MKDEV(0, 0));
 652        class_destroy(rnbd_dev_class);
 653}
 654