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