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