11eb54f8fSJack Wang // SPDX-License-Identifier: GPL-2.0-or-later
21eb54f8fSJack Wang /*
31eb54f8fSJack Wang * RDMA Network Block Driver
41eb54f8fSJack Wang *
51eb54f8fSJack Wang * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
61eb54f8fSJack Wang * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
71eb54f8fSJack Wang * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
81eb54f8fSJack Wang */
91eb54f8fSJack Wang
101eb54f8fSJack Wang #undef pr_fmt
111eb54f8fSJack Wang #define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
121eb54f8fSJack Wang
131eb54f8fSJack Wang #include <linux/types.h>
141eb54f8fSJack Wang #include <linux/ctype.h>
151eb54f8fSJack Wang #include <linux/parser.h>
161eb54f8fSJack Wang #include <linux/module.h>
171eb54f8fSJack Wang #include <linux/in6.h>
181eb54f8fSJack Wang #include <linux/fs.h>
191eb54f8fSJack Wang #include <linux/uaccess.h>
201eb54f8fSJack Wang #include <linux/device.h>
211eb54f8fSJack Wang #include <rdma/ib.h>
221eb54f8fSJack Wang #include <rdma/rdma_cm.h>
231eb54f8fSJack Wang
241eb54f8fSJack Wang #include "rnbd-clt.h"
251eb54f8fSJack Wang
261eb54f8fSJack Wang static struct device *rnbd_dev;
27137380c0SIvan Orlov static const struct class rnbd_dev_class = {
28*6548fce0SLi Zhijian .name = "rnbd-client",
29137380c0SIvan Orlov };
301eb54f8fSJack Wang static struct kobject *rnbd_devs_kobj;
311eb54f8fSJack Wang
321eb54f8fSJack Wang enum {
331eb54f8fSJack Wang RNBD_OPT_ERR = 0,
341eb54f8fSJack Wang RNBD_OPT_DEST_PORT = 1 << 0,
351eb54f8fSJack Wang RNBD_OPT_PATH = 1 << 1,
361eb54f8fSJack Wang RNBD_OPT_DEV_PATH = 1 << 2,
371eb54f8fSJack Wang RNBD_OPT_ACCESS_MODE = 1 << 3,
381eb54f8fSJack Wang RNBD_OPT_SESSNAME = 1 << 6,
392958a995SGioh Kim RNBD_OPT_NR_POLL_QUEUES = 1 << 7,
401eb54f8fSJack Wang };
411eb54f8fSJack Wang
421eb54f8fSJack Wang static const unsigned int rnbd_opt_mandatory[] = {
431eb54f8fSJack Wang RNBD_OPT_DEV_PATH,
441eb54f8fSJack Wang RNBD_OPT_SESSNAME,
451eb54f8fSJack Wang };
461eb54f8fSJack Wang
471eb54f8fSJack Wang static const match_table_t rnbd_opt_tokens = {
481eb54f8fSJack Wang {RNBD_OPT_PATH, "path=%s" },
491eb54f8fSJack Wang {RNBD_OPT_DEV_PATH, "device_path=%s" },
501eb54f8fSJack Wang {RNBD_OPT_DEST_PORT, "dest_port=%d" },
511eb54f8fSJack Wang {RNBD_OPT_ACCESS_MODE, "access_mode=%s" },
521eb54f8fSJack Wang {RNBD_OPT_SESSNAME, "sessname=%s" },
532958a995SGioh Kim {RNBD_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" },
541eb54f8fSJack Wang {RNBD_OPT_ERR, NULL },
551eb54f8fSJack Wang };
561eb54f8fSJack Wang
571eb54f8fSJack Wang struct rnbd_map_options {
581eb54f8fSJack Wang char *sessname;
591eb54f8fSJack Wang struct rtrs_addr *paths;
601eb54f8fSJack Wang size_t *path_cnt;
611eb54f8fSJack Wang char *pathname;
621eb54f8fSJack Wang u16 *dest_port;
631eb54f8fSJack Wang enum rnbd_access_mode *access_mode;
642958a995SGioh Kim u32 *nr_poll_queues;
651eb54f8fSJack Wang };
661eb54f8fSJack Wang
rnbd_clt_parse_map_options(const char * buf,size_t max_path_cnt,struct rnbd_map_options * opt)671eb54f8fSJack Wang static int rnbd_clt_parse_map_options(const char *buf, size_t max_path_cnt,
681eb54f8fSJack Wang struct rnbd_map_options *opt)
691eb54f8fSJack Wang {
701eb54f8fSJack Wang char *options, *sep_opt;
711eb54f8fSJack Wang char *p;
721eb54f8fSJack Wang substring_t args[MAX_OPT_ARGS];
731eb54f8fSJack Wang int opt_mask = 0;
741eb54f8fSJack Wang int token;
751eb54f8fSJack Wang int ret = -EINVAL;
767904022dSDan Carpenter int nr_poll_queues = 0;
777904022dSDan Carpenter int dest_port = 0;
781eb54f8fSJack Wang int p_cnt = 0;
797904022dSDan Carpenter int i;
801eb54f8fSJack Wang
811eb54f8fSJack Wang options = kstrdup(buf, GFP_KERNEL);
821eb54f8fSJack Wang if (!options)
831eb54f8fSJack Wang return -ENOMEM;
841eb54f8fSJack Wang
851eb54f8fSJack Wang sep_opt = strstrip(options);
861eb54f8fSJack Wang while ((p = strsep(&sep_opt, " ")) != NULL) {
871eb54f8fSJack Wang if (!*p)
881eb54f8fSJack Wang continue;
891eb54f8fSJack Wang
901eb54f8fSJack Wang token = match_token(p, rnbd_opt_tokens, args);
911eb54f8fSJack Wang opt_mask |= token;
921eb54f8fSJack Wang
931eb54f8fSJack Wang switch (token) {
941eb54f8fSJack Wang case RNBD_OPT_SESSNAME:
951eb54f8fSJack Wang p = match_strdup(args);
961eb54f8fSJack Wang if (!p) {
971eb54f8fSJack Wang ret = -ENOMEM;
981eb54f8fSJack Wang goto out;
991eb54f8fSJack Wang }
1001eb54f8fSJack Wang if (strlen(p) > NAME_MAX) {
1011eb54f8fSJack Wang pr_err("map_device: sessname too long\n");
1021eb54f8fSJack Wang ret = -EINVAL;
1031eb54f8fSJack Wang kfree(p);
1041eb54f8fSJack Wang goto out;
1051eb54f8fSJack Wang }
10657b93ed4SDima Stepanov strscpy(opt->sessname, p, NAME_MAX);
1071eb54f8fSJack Wang kfree(p);
1081eb54f8fSJack Wang break;
1091eb54f8fSJack Wang
1101eb54f8fSJack Wang case RNBD_OPT_PATH:
1111eb54f8fSJack Wang if (p_cnt >= max_path_cnt) {
1121eb54f8fSJack Wang pr_err("map_device: too many (> %zu) paths provided\n",
1131eb54f8fSJack Wang max_path_cnt);
1141eb54f8fSJack Wang ret = -ENOMEM;
1151eb54f8fSJack Wang goto out;
1161eb54f8fSJack Wang }
1171eb54f8fSJack Wang p = match_strdup(args);
1181eb54f8fSJack Wang if (!p) {
1191eb54f8fSJack Wang ret = -ENOMEM;
1201eb54f8fSJack Wang goto out;
1211eb54f8fSJack Wang }
1221eb54f8fSJack Wang
1231eb54f8fSJack Wang ret = rtrs_addr_to_sockaddr(p, strlen(p),
1241eb54f8fSJack Wang *opt->dest_port,
1251eb54f8fSJack Wang &opt->paths[p_cnt]);
1261eb54f8fSJack Wang if (ret) {
1271eb54f8fSJack Wang pr_err("Can't parse path %s: %d\n", p, ret);
1281eb54f8fSJack Wang kfree(p);
1291eb54f8fSJack Wang goto out;
1301eb54f8fSJack Wang }
1311eb54f8fSJack Wang
1321eb54f8fSJack Wang p_cnt++;
1331eb54f8fSJack Wang
1341eb54f8fSJack Wang kfree(p);
1351eb54f8fSJack Wang break;
1361eb54f8fSJack Wang
1371eb54f8fSJack Wang case RNBD_OPT_DEV_PATH:
1381eb54f8fSJack Wang p = match_strdup(args);
1391eb54f8fSJack Wang if (!p) {
1401eb54f8fSJack Wang ret = -ENOMEM;
1411eb54f8fSJack Wang goto out;
1421eb54f8fSJack Wang }
1431eb54f8fSJack Wang if (strlen(p) > NAME_MAX) {
1441eb54f8fSJack Wang pr_err("map_device: Device path too long\n");
1451eb54f8fSJack Wang ret = -EINVAL;
1461eb54f8fSJack Wang kfree(p);
1471eb54f8fSJack Wang goto out;
1481eb54f8fSJack Wang }
14957b93ed4SDima Stepanov strscpy(opt->pathname, p, NAME_MAX);
1501eb54f8fSJack Wang kfree(p);
1511eb54f8fSJack Wang break;
1521eb54f8fSJack Wang
1531eb54f8fSJack Wang case RNBD_OPT_DEST_PORT:
1541eb54f8fSJack Wang if (match_int(args, &dest_port) || dest_port < 0 ||
1551eb54f8fSJack Wang dest_port > 65535) {
1561eb54f8fSJack Wang pr_err("bad destination port number parameter '%d'\n",
1571eb54f8fSJack Wang dest_port);
1581eb54f8fSJack Wang ret = -EINVAL;
1591eb54f8fSJack Wang goto out;
1601eb54f8fSJack Wang }
1611eb54f8fSJack Wang *opt->dest_port = dest_port;
1621eb54f8fSJack Wang break;
1631eb54f8fSJack Wang
1641eb54f8fSJack Wang case RNBD_OPT_ACCESS_MODE:
1651eb54f8fSJack Wang p = match_strdup(args);
1661eb54f8fSJack Wang if (!p) {
1671eb54f8fSJack Wang ret = -ENOMEM;
1681eb54f8fSJack Wang goto out;
1691eb54f8fSJack Wang }
1701eb54f8fSJack Wang
1711eb54f8fSJack Wang if (!strcmp(p, "ro")) {
1721eb54f8fSJack Wang *opt->access_mode = RNBD_ACCESS_RO;
1731eb54f8fSJack Wang } else if (!strcmp(p, "rw")) {
1741eb54f8fSJack Wang *opt->access_mode = RNBD_ACCESS_RW;
1751eb54f8fSJack Wang } else if (!strcmp(p, "migration")) {
1761eb54f8fSJack Wang *opt->access_mode = RNBD_ACCESS_MIGRATION;
1771eb54f8fSJack Wang } else {
1781eb54f8fSJack Wang pr_err("map_device: Invalid access_mode: '%s'\n",
1791eb54f8fSJack Wang p);
1801eb54f8fSJack Wang ret = -EINVAL;
1811eb54f8fSJack Wang kfree(p);
1821eb54f8fSJack Wang goto out;
1831eb54f8fSJack Wang }
1841eb54f8fSJack Wang
1851eb54f8fSJack Wang kfree(p);
1861eb54f8fSJack Wang break;
1871eb54f8fSJack Wang
1882958a995SGioh Kim case RNBD_OPT_NR_POLL_QUEUES:
1892958a995SGioh Kim if (match_int(args, &nr_poll_queues) || nr_poll_queues < -1 ||
1902958a995SGioh Kim nr_poll_queues > (int)nr_cpu_ids) {
1912958a995SGioh Kim pr_err("bad nr_poll_queues parameter '%d'\n",
1922958a995SGioh Kim nr_poll_queues);
1932958a995SGioh Kim ret = -EINVAL;
1942958a995SGioh Kim goto out;
1952958a995SGioh Kim }
1962958a995SGioh Kim if (nr_poll_queues == -1)
1972958a995SGioh Kim nr_poll_queues = nr_cpu_ids;
1982958a995SGioh Kim *opt->nr_poll_queues = nr_poll_queues;
1992958a995SGioh Kim break;
2002958a995SGioh Kim
2011eb54f8fSJack Wang default:
2021eb54f8fSJack Wang pr_err("map_device: Unknown parameter or missing value '%s'\n",
2031eb54f8fSJack Wang p);
2041eb54f8fSJack Wang ret = -EINVAL;
2051eb54f8fSJack Wang goto out;
2061eb54f8fSJack Wang }
2071eb54f8fSJack Wang }
2081eb54f8fSJack Wang
2091eb54f8fSJack Wang for (i = 0; i < ARRAY_SIZE(rnbd_opt_mandatory); i++) {
2101eb54f8fSJack Wang if ((opt_mask & rnbd_opt_mandatory[i])) {
2111eb54f8fSJack Wang ret = 0;
2121eb54f8fSJack Wang } else {
2131eb54f8fSJack Wang pr_err("map_device: Parameters missing\n");
2141eb54f8fSJack Wang ret = -EINVAL;
2151eb54f8fSJack Wang break;
2161eb54f8fSJack Wang }
2171eb54f8fSJack Wang }
2181eb54f8fSJack Wang
2191eb54f8fSJack Wang out:
2201eb54f8fSJack Wang *opt->path_cnt = p_cnt;
2211eb54f8fSJack Wang kfree(options);
2221eb54f8fSJack Wang return ret;
2231eb54f8fSJack Wang }
2241eb54f8fSJack Wang
state_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)2251eb54f8fSJack Wang static ssize_t state_show(struct kobject *kobj,
2261eb54f8fSJack Wang struct kobj_attribute *attr, char *page)
2271eb54f8fSJack Wang {
2281eb54f8fSJack Wang struct rnbd_clt_dev *dev;
2291eb54f8fSJack Wang
2301eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
2311eb54f8fSJack Wang
2321eb54f8fSJack Wang switch (dev->dev_state) {
2331eb54f8fSJack Wang case DEV_STATE_INIT:
2343087b335SMd Haris Iqbal return sysfs_emit(page, "init\n");
2351eb54f8fSJack Wang case DEV_STATE_MAPPED:
2361eb54f8fSJack Wang /* TODO fix cli tool before changing to proper state */
2373087b335SMd Haris Iqbal return sysfs_emit(page, "open\n");
2381eb54f8fSJack Wang case DEV_STATE_MAPPED_DISCONNECTED:
2391eb54f8fSJack Wang /* TODO fix cli tool before changing to proper state */
2403087b335SMd Haris Iqbal return sysfs_emit(page, "closed\n");
2411eb54f8fSJack Wang case DEV_STATE_UNMAPPED:
2423087b335SMd Haris Iqbal return sysfs_emit(page, "unmapped\n");
2431eb54f8fSJack Wang default:
2443087b335SMd Haris Iqbal return sysfs_emit(page, "unknown\n");
2451eb54f8fSJack Wang }
2461eb54f8fSJack Wang }
2471eb54f8fSJack Wang
2481eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_state_attr = __ATTR_RO(state);
2491eb54f8fSJack Wang
nr_poll_queues_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)2502958a995SGioh Kim static ssize_t nr_poll_queues_show(struct kobject *kobj,
2512958a995SGioh Kim struct kobj_attribute *attr, char *page)
2522958a995SGioh Kim {
2532958a995SGioh Kim struct rnbd_clt_dev *dev;
2542958a995SGioh Kim
2552958a995SGioh Kim dev = container_of(kobj, struct rnbd_clt_dev, kobj);
2562958a995SGioh Kim
2572958a995SGioh Kim return sysfs_emit(page, "%d\n", dev->nr_poll_queues);
2582958a995SGioh Kim }
2592958a995SGioh Kim
2602958a995SGioh Kim static struct kobj_attribute rnbd_clt_nr_poll_queues =
2612958a995SGioh Kim __ATTR_RO(nr_poll_queues);
2622958a995SGioh Kim
mapping_path_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)2631eb54f8fSJack Wang static ssize_t mapping_path_show(struct kobject *kobj,
2641eb54f8fSJack Wang struct kobj_attribute *attr, char *page)
2651eb54f8fSJack Wang {
2661eb54f8fSJack Wang struct rnbd_clt_dev *dev;
2671eb54f8fSJack Wang
2681eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
2691eb54f8fSJack Wang
2703087b335SMd Haris Iqbal return sysfs_emit(page, "%s\n", dev->pathname);
2711eb54f8fSJack Wang }
2721eb54f8fSJack Wang
2731eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_mapping_path_attr =
2741eb54f8fSJack Wang __ATTR_RO(mapping_path);
2751eb54f8fSJack Wang
access_mode_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)2761eb54f8fSJack Wang static ssize_t access_mode_show(struct kobject *kobj,
2771eb54f8fSJack Wang struct kobj_attribute *attr, char *page)
2781eb54f8fSJack Wang {
2791eb54f8fSJack Wang struct rnbd_clt_dev *dev;
2801eb54f8fSJack Wang
2811eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
2821eb54f8fSJack Wang
283d6e94913SGuoqing Jiang return sysfs_emit(page, "%s\n", rnbd_access_modes[dev->access_mode].str);
2841eb54f8fSJack Wang }
2851eb54f8fSJack Wang
2861eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_access_mode =
2871eb54f8fSJack Wang __ATTR_RO(access_mode);
2881eb54f8fSJack Wang
rnbd_clt_unmap_dev_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)2891eb54f8fSJack Wang static ssize_t rnbd_clt_unmap_dev_show(struct kobject *kobj,
2901eb54f8fSJack Wang struct kobj_attribute *attr, char *page)
2911eb54f8fSJack Wang {
2923087b335SMd Haris Iqbal return sysfs_emit(page, "Usage: echo <normal|force> > %s\n",
2931eb54f8fSJack Wang attr->attr.name);
2941eb54f8fSJack Wang }
2951eb54f8fSJack Wang
rnbd_clt_unmap_dev_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)2961eb54f8fSJack Wang static ssize_t rnbd_clt_unmap_dev_store(struct kobject *kobj,
2971eb54f8fSJack Wang struct kobj_attribute *attr,
2981eb54f8fSJack Wang const char *buf, size_t count)
2991eb54f8fSJack Wang {
3001eb54f8fSJack Wang struct rnbd_clt_dev *dev;
3011eb54f8fSJack Wang char *opt, *options;
3021eb54f8fSJack Wang bool force;
3031eb54f8fSJack Wang int err;
3041eb54f8fSJack Wang
3051eb54f8fSJack Wang opt = kstrdup(buf, GFP_KERNEL);
3061eb54f8fSJack Wang if (!opt)
3071eb54f8fSJack Wang return -ENOMEM;
3081eb54f8fSJack Wang
3091eb54f8fSJack Wang options = strstrip(opt);
3101eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
3111eb54f8fSJack Wang if (sysfs_streq(options, "normal")) {
3121eb54f8fSJack Wang force = false;
3131eb54f8fSJack Wang } else if (sysfs_streq(options, "force")) {
3141eb54f8fSJack Wang force = true;
3151eb54f8fSJack Wang } else {
3161eb54f8fSJack Wang rnbd_clt_err(dev,
3171eb54f8fSJack Wang "unmap_device: Invalid value: %s\n",
3181eb54f8fSJack Wang options);
3191eb54f8fSJack Wang err = -EINVAL;
3201eb54f8fSJack Wang goto out;
3211eb54f8fSJack Wang }
3221eb54f8fSJack Wang
3231eb54f8fSJack Wang rnbd_clt_info(dev, "Unmapping device, option: %s.\n",
3241eb54f8fSJack Wang force ? "force" : "normal");
3251eb54f8fSJack Wang
3261eb54f8fSJack Wang /*
3271eb54f8fSJack Wang * We take explicit module reference only for one reason: do not
3281eb54f8fSJack Wang * race with lockless rnbd_destroy_sessions().
3291eb54f8fSJack Wang */
3301eb54f8fSJack Wang if (!try_module_get(THIS_MODULE)) {
3311eb54f8fSJack Wang err = -ENODEV;
3321eb54f8fSJack Wang goto out;
3331eb54f8fSJack Wang }
3341eb54f8fSJack Wang err = rnbd_clt_unmap_device(dev, force, &attr->attr);
3351eb54f8fSJack Wang if (err) {
3361eb54f8fSJack Wang if (err != -EALREADY)
3371eb54f8fSJack Wang rnbd_clt_err(dev, "unmap_device: %d\n", err);
3381eb54f8fSJack Wang goto module_put;
3391eb54f8fSJack Wang }
3401eb54f8fSJack Wang
3411eb54f8fSJack Wang /*
3421eb54f8fSJack Wang * Here device can be vanished!
3431eb54f8fSJack Wang */
3441eb54f8fSJack Wang
3451eb54f8fSJack Wang err = count;
3461eb54f8fSJack Wang
3471eb54f8fSJack Wang module_put:
3481eb54f8fSJack Wang module_put(THIS_MODULE);
3491eb54f8fSJack Wang out:
3501eb54f8fSJack Wang kfree(opt);
3511eb54f8fSJack Wang
3521eb54f8fSJack Wang return err;
3531eb54f8fSJack Wang }
3541eb54f8fSJack Wang
3551eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_unmap_device_attr =
3561eb54f8fSJack Wang __ATTR(unmap_device, 0644, rnbd_clt_unmap_dev_show,
3571eb54f8fSJack Wang rnbd_clt_unmap_dev_store);
3581eb54f8fSJack Wang
rnbd_clt_resize_dev_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)3591eb54f8fSJack Wang static ssize_t rnbd_clt_resize_dev_show(struct kobject *kobj,
3601eb54f8fSJack Wang struct kobj_attribute *attr,
3611eb54f8fSJack Wang char *page)
3621eb54f8fSJack Wang {
3633087b335SMd Haris Iqbal return sysfs_emit(page, "Usage: echo <new size in sectors> > %s\n",
3641eb54f8fSJack Wang attr->attr.name);
3651eb54f8fSJack Wang }
3661eb54f8fSJack Wang
rnbd_clt_resize_dev_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3671eb54f8fSJack Wang static ssize_t rnbd_clt_resize_dev_store(struct kobject *kobj,
3681eb54f8fSJack Wang struct kobj_attribute *attr,
3691eb54f8fSJack Wang const char *buf, size_t count)
3701eb54f8fSJack Wang {
3711eb54f8fSJack Wang int ret;
3721eb54f8fSJack Wang unsigned long sectors;
3731eb54f8fSJack Wang struct rnbd_clt_dev *dev;
3741eb54f8fSJack Wang
3751eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
3761eb54f8fSJack Wang
3771eb54f8fSJack Wang ret = kstrtoul(buf, 0, §ors);
3781eb54f8fSJack Wang if (ret)
3791eb54f8fSJack Wang return ret;
3801eb54f8fSJack Wang
381ae2dfd1dSGuoqing Jiang ret = rnbd_clt_resize_disk(dev, sectors);
3821eb54f8fSJack Wang if (ret)
3831eb54f8fSJack Wang return ret;
3841eb54f8fSJack Wang
3851eb54f8fSJack Wang return count;
3861eb54f8fSJack Wang }
3871eb54f8fSJack Wang
3881eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_resize_dev_attr =
3891eb54f8fSJack Wang __ATTR(resize, 0644, rnbd_clt_resize_dev_show,
3901eb54f8fSJack Wang rnbd_clt_resize_dev_store);
3911eb54f8fSJack Wang
rnbd_clt_remap_dev_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)3921eb54f8fSJack Wang static ssize_t rnbd_clt_remap_dev_show(struct kobject *kobj,
3931eb54f8fSJack Wang struct kobj_attribute *attr, char *page)
3941eb54f8fSJack Wang {
3953087b335SMd Haris Iqbal return sysfs_emit(page, "Usage: echo <1> > %s\n", attr->attr.name);
3961eb54f8fSJack Wang }
3971eb54f8fSJack Wang
rnbd_clt_remap_dev_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)3981eb54f8fSJack Wang static ssize_t rnbd_clt_remap_dev_store(struct kobject *kobj,
3991eb54f8fSJack Wang struct kobj_attribute *attr,
4001eb54f8fSJack Wang const char *buf, size_t count)
4011eb54f8fSJack Wang {
4021eb54f8fSJack Wang struct rnbd_clt_dev *dev;
4031eb54f8fSJack Wang char *opt, *options;
4041eb54f8fSJack Wang int err;
4051eb54f8fSJack Wang
4061eb54f8fSJack Wang opt = kstrdup(buf, GFP_KERNEL);
4071eb54f8fSJack Wang if (!opt)
4081eb54f8fSJack Wang return -ENOMEM;
4091eb54f8fSJack Wang
4101eb54f8fSJack Wang options = strstrip(opt);
4111eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
4121eb54f8fSJack Wang if (!sysfs_streq(options, "1")) {
4131eb54f8fSJack Wang rnbd_clt_err(dev,
4141eb54f8fSJack Wang "remap_device: Invalid value: %s\n",
4151eb54f8fSJack Wang options);
4161eb54f8fSJack Wang err = -EINVAL;
4171eb54f8fSJack Wang goto out;
4181eb54f8fSJack Wang }
4191eb54f8fSJack Wang err = rnbd_clt_remap_device(dev);
4201eb54f8fSJack Wang if (likely(!err))
4211eb54f8fSJack Wang err = count;
4221eb54f8fSJack Wang
4231eb54f8fSJack Wang out:
4241eb54f8fSJack Wang kfree(opt);
4251eb54f8fSJack Wang
4261eb54f8fSJack Wang return err;
4271eb54f8fSJack Wang }
4281eb54f8fSJack Wang
4291eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_remap_device_attr =
4301eb54f8fSJack Wang __ATTR(remap_device, 0644, rnbd_clt_remap_dev_show,
4311eb54f8fSJack Wang rnbd_clt_remap_dev_store);
4321eb54f8fSJack Wang
session_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)4331eb54f8fSJack Wang static ssize_t session_show(struct kobject *kobj, struct kobj_attribute *attr,
4341eb54f8fSJack Wang char *page)
4351eb54f8fSJack Wang {
4361eb54f8fSJack Wang struct rnbd_clt_dev *dev;
4371eb54f8fSJack Wang
4381eb54f8fSJack Wang dev = container_of(kobj, struct rnbd_clt_dev, kobj);
4391eb54f8fSJack Wang
4403087b335SMd Haris Iqbal return sysfs_emit(page, "%s\n", dev->sess->sessname);
4411eb54f8fSJack Wang }
4421eb54f8fSJack Wang
4431eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_session_attr =
4441eb54f8fSJack Wang __ATTR_RO(session);
4451eb54f8fSJack Wang
4461eb54f8fSJack Wang static struct attribute *rnbd_dev_attrs[] = {
4471eb54f8fSJack Wang &rnbd_clt_unmap_device_attr.attr,
4481eb54f8fSJack Wang &rnbd_clt_resize_dev_attr.attr,
4491eb54f8fSJack Wang &rnbd_clt_remap_device_attr.attr,
4501eb54f8fSJack Wang &rnbd_clt_mapping_path_attr.attr,
4511eb54f8fSJack Wang &rnbd_clt_state_attr.attr,
4521eb54f8fSJack Wang &rnbd_clt_session_attr.attr,
4531eb54f8fSJack Wang &rnbd_clt_access_mode.attr,
4542958a995SGioh Kim &rnbd_clt_nr_poll_queues.attr,
4551eb54f8fSJack Wang NULL,
4561eb54f8fSJack Wang };
457050f461eSGreg Kroah-Hartman ATTRIBUTE_GROUPS(rnbd_dev);
4581eb54f8fSJack Wang
rnbd_clt_remove_dev_symlink(struct rnbd_clt_dev * dev)4591eb54f8fSJack Wang void rnbd_clt_remove_dev_symlink(struct rnbd_clt_dev *dev)
4601eb54f8fSJack Wang {
4611eb54f8fSJack Wang /*
462cdb685cbSDanil Kipnis * The module unload rnbd_client_exit path is racing with unmapping of
463cdb685cbSDanil Kipnis * the last single device from the sysfs manually
464cdb685cbSDanil Kipnis * i.e. rnbd_clt_unmap_dev_store() leading to a sysfs warning because
465cdb685cbSDanil Kipnis * of sysfs link already was removed already.
4661eb54f8fSJack Wang */
46712b06533SGioh Kim if (dev->blk_symlink_name) {
46812b06533SGioh Kim if (try_module_get(THIS_MODULE)) {
4691eb54f8fSJack Wang sysfs_remove_link(rnbd_devs_kobj, dev->blk_symlink_name);
470cdb685cbSDanil Kipnis module_put(THIS_MODULE);
471cdb685cbSDanil Kipnis }
47212b06533SGioh Kim /* It should be freed always. */
47312b06533SGioh Kim kfree(dev->blk_symlink_name);
47412b06533SGioh Kim dev->blk_symlink_name = NULL;
47512b06533SGioh Kim }
4761eb54f8fSJack Wang }
4771eb54f8fSJack Wang
4781eb54f8fSJack Wang static struct kobj_type rnbd_dev_ktype = {
4791eb54f8fSJack Wang .sysfs_ops = &kobj_sysfs_ops,
480050f461eSGreg Kroah-Hartman .default_groups = rnbd_dev_groups,
4811eb54f8fSJack Wang };
4821eb54f8fSJack Wang
rnbd_clt_add_dev_kobj(struct rnbd_clt_dev * dev)4831eb54f8fSJack Wang static int rnbd_clt_add_dev_kobj(struct rnbd_clt_dev *dev)
4841eb54f8fSJack Wang {
4851eb54f8fSJack Wang int ret;
4861eb54f8fSJack Wang struct kobject *gd_kobj = &disk_to_dev(dev->gd)->kobj;
4871eb54f8fSJack Wang
4881eb54f8fSJack Wang ret = kobject_init_and_add(&dev->kobj, &rnbd_dev_ktype, gd_kobj, "%s",
4891eb54f8fSJack Wang "rnbd");
490d3a95ccaSGuoqing Jiang if (ret) {
4911eb54f8fSJack Wang rnbd_clt_err(dev, "Failed to create device sysfs dir, err: %d\n",
4921eb54f8fSJack Wang ret);
493d3a95ccaSGuoqing Jiang kobject_put(&dev->kobj);
494d3a95ccaSGuoqing Jiang }
4953ba1c693SMd Haris Iqbal kobject_uevent(gd_kobj, KOBJ_ONLINE);
4961eb54f8fSJack Wang
4971eb54f8fSJack Wang return ret;
4981eb54f8fSJack Wang }
4991eb54f8fSJack Wang
rnbd_clt_map_device_show(struct kobject * kobj,struct kobj_attribute * attr,char * page)5001eb54f8fSJack Wang static ssize_t rnbd_clt_map_device_show(struct kobject *kobj,
5011eb54f8fSJack Wang struct kobj_attribute *attr,
5021eb54f8fSJack Wang char *page)
5031eb54f8fSJack Wang {
5043087b335SMd Haris Iqbal return sysfs_emit(page,
5052958a995SGioh Kim "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",
5061eb54f8fSJack Wang attr->attr.name);
5071eb54f8fSJack Wang }
5081eb54f8fSJack Wang
rnbd_clt_get_path_name(struct rnbd_clt_dev * dev,char * buf,size_t len)5091eb54f8fSJack Wang static int rnbd_clt_get_path_name(struct rnbd_clt_dev *dev, char *buf,
5101eb54f8fSJack Wang size_t len)
5111eb54f8fSJack Wang {
5121eb54f8fSJack Wang int ret;
5131eb54f8fSJack Wang char pathname[NAME_MAX], *s;
5141eb54f8fSJack Wang
51557b93ed4SDima Stepanov strscpy(pathname, dev->pathname, sizeof(pathname));
5161eb54f8fSJack Wang while ((s = strchr(pathname, '/')))
5171eb54f8fSJack Wang s[0] = '!';
5181eb54f8fSJack Wang
5193db7cf55SDima Stepanov ret = snprintf(buf, len, "%s@%s", pathname, dev->sess->sessname);
52091f4acb2SGuoqing Jiang if (ret >= len)
52191f4acb2SGuoqing Jiang return -ENAMETOOLONG;
52291f4acb2SGuoqing Jiang
5231eb54f8fSJack Wang return 0;
5241eb54f8fSJack Wang }
5251eb54f8fSJack Wang
rnbd_clt_add_dev_symlink(struct rnbd_clt_dev * dev)5261eb54f8fSJack Wang static int rnbd_clt_add_dev_symlink(struct rnbd_clt_dev *dev)
5271eb54f8fSJack Wang {
5281eb54f8fSJack Wang struct kobject *gd_kobj = &disk_to_dev(dev->gd)->kobj;
52964e8a6ecSMd Haris Iqbal int ret, len;
53064e8a6ecSMd Haris Iqbal
53164e8a6ecSMd Haris Iqbal len = strlen(dev->pathname) + strlen(dev->sess->sessname) + 2;
53264e8a6ecSMd Haris Iqbal dev->blk_symlink_name = kzalloc(len, GFP_KERNEL);
53364e8a6ecSMd Haris Iqbal if (!dev->blk_symlink_name) {
53464e8a6ecSMd Haris Iqbal rnbd_clt_err(dev, "Failed to allocate memory for blk_symlink_name\n");
535733c15bdSColin Ian King return -ENOMEM;
53664e8a6ecSMd Haris Iqbal }
5371eb54f8fSJack Wang
5381eb54f8fSJack Wang ret = rnbd_clt_get_path_name(dev, dev->blk_symlink_name,
53964e8a6ecSMd Haris Iqbal len);
5401eb54f8fSJack Wang if (ret) {
5411eb54f8fSJack Wang rnbd_clt_err(dev, "Failed to get /sys/block symlink path, err: %d\n",
5421eb54f8fSJack Wang ret);
5431eb54f8fSJack Wang goto out_err;
5441eb54f8fSJack Wang }
5451eb54f8fSJack Wang
5461eb54f8fSJack Wang ret = sysfs_create_link(rnbd_devs_kobj, gd_kobj,
5471eb54f8fSJack Wang dev->blk_symlink_name);
5481eb54f8fSJack Wang if (ret) {
5491eb54f8fSJack Wang rnbd_clt_err(dev, "Creating /sys/block symlink failed, err: %d\n",
5501eb54f8fSJack Wang ret);
5511eb54f8fSJack Wang goto out_err;
5521eb54f8fSJack Wang }
5531eb54f8fSJack Wang
5541eb54f8fSJack Wang return 0;
5551eb54f8fSJack Wang
5561eb54f8fSJack Wang out_err:
55746067844SJack Wang kfree(dev->blk_symlink_name);
55846067844SJack Wang dev->blk_symlink_name = NULL ;
5591eb54f8fSJack Wang return ret;
5601eb54f8fSJack Wang }
5611eb54f8fSJack Wang
rnbd_clt_map_device_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)5621eb54f8fSJack Wang static ssize_t rnbd_clt_map_device_store(struct kobject *kobj,
5631eb54f8fSJack Wang struct kobj_attribute *attr,
5641eb54f8fSJack Wang const char *buf, size_t count)
5651eb54f8fSJack Wang {
5661eb54f8fSJack Wang struct rnbd_clt_dev *dev;
5671eb54f8fSJack Wang struct rnbd_map_options opt;
5681eb54f8fSJack Wang int ret;
5691eb54f8fSJack Wang char pathname[NAME_MAX];
5701eb54f8fSJack Wang char sessname[NAME_MAX];
5711eb54f8fSJack Wang enum rnbd_access_mode access_mode = RNBD_ACCESS_RW;
5721eb54f8fSJack Wang u16 port_nr = RTRS_PORT;
5732958a995SGioh Kim u32 nr_poll_queues = 0;
5741eb54f8fSJack Wang
5751eb54f8fSJack Wang struct sockaddr_storage *addrs;
5761eb54f8fSJack Wang struct rtrs_addr paths[6];
5771eb54f8fSJack Wang size_t path_cnt;
5781eb54f8fSJack Wang
5791eb54f8fSJack Wang opt.sessname = sessname;
5801eb54f8fSJack Wang opt.paths = paths;
5811eb54f8fSJack Wang opt.path_cnt = &path_cnt;
5821eb54f8fSJack Wang opt.pathname = pathname;
5831eb54f8fSJack Wang opt.dest_port = &port_nr;
5841eb54f8fSJack Wang opt.access_mode = &access_mode;
5852958a995SGioh Kim opt.nr_poll_queues = &nr_poll_queues;
5861eb54f8fSJack Wang addrs = kcalloc(ARRAY_SIZE(paths) * 2, sizeof(*addrs), GFP_KERNEL);
5871eb54f8fSJack Wang if (!addrs)
5881eb54f8fSJack Wang return -ENOMEM;
5891eb54f8fSJack Wang
5901eb54f8fSJack Wang for (path_cnt = 0; path_cnt < ARRAY_SIZE(paths); path_cnt++) {
5911eb54f8fSJack Wang paths[path_cnt].src = &addrs[path_cnt * 2];
5921eb54f8fSJack Wang paths[path_cnt].dst = &addrs[path_cnt * 2 + 1];
5931eb54f8fSJack Wang }
5941eb54f8fSJack Wang
5951eb54f8fSJack Wang ret = rnbd_clt_parse_map_options(buf, ARRAY_SIZE(paths), &opt);
5961eb54f8fSJack Wang if (ret)
5971eb54f8fSJack Wang goto out;
5981eb54f8fSJack Wang
5992958a995SGioh Kim pr_info("Mapping device %s on session %s, (access_mode: %s, nr_poll_queues: %d)\n",
6001eb54f8fSJack Wang pathname, sessname,
601d6e94913SGuoqing Jiang rnbd_access_modes[access_mode].str,
6022958a995SGioh Kim nr_poll_queues);
6031eb54f8fSJack Wang
6041eb54f8fSJack Wang dev = rnbd_clt_map_device(sessname, paths, path_cnt, port_nr, pathname,
6052958a995SGioh Kim access_mode, nr_poll_queues);
6061eb54f8fSJack Wang if (IS_ERR(dev)) {
6071eb54f8fSJack Wang ret = PTR_ERR(dev);
6081eb54f8fSJack Wang goto out;
6091eb54f8fSJack Wang }
6101eb54f8fSJack Wang
6111eb54f8fSJack Wang ret = rnbd_clt_add_dev_kobj(dev);
6121eb54f8fSJack Wang if (ret)
6131eb54f8fSJack Wang goto unmap_dev;
6141eb54f8fSJack Wang
6151eb54f8fSJack Wang ret = rnbd_clt_add_dev_symlink(dev);
6161eb54f8fSJack Wang if (ret)
6171eb54f8fSJack Wang goto unmap_dev;
6181eb54f8fSJack Wang
6191eb54f8fSJack Wang kfree(addrs);
6201eb54f8fSJack Wang return count;
6211eb54f8fSJack Wang
6221eb54f8fSJack Wang unmap_dev:
6231eb54f8fSJack Wang rnbd_clt_unmap_device(dev, true, NULL);
6241eb54f8fSJack Wang out:
6251eb54f8fSJack Wang kfree(addrs);
6261eb54f8fSJack Wang return ret;
6271eb54f8fSJack Wang }
6281eb54f8fSJack Wang
6291eb54f8fSJack Wang static struct kobj_attribute rnbd_clt_map_device_attr =
6301eb54f8fSJack Wang __ATTR(map_device, 0644,
6311eb54f8fSJack Wang rnbd_clt_map_device_show, rnbd_clt_map_device_store);
6321eb54f8fSJack Wang
6331eb54f8fSJack Wang static struct attribute *default_attrs[] = {
6341eb54f8fSJack Wang &rnbd_clt_map_device_attr.attr,
6351eb54f8fSJack Wang NULL,
6361eb54f8fSJack Wang };
6371eb54f8fSJack Wang
6381eb54f8fSJack Wang static struct attribute_group default_attr_group = {
6391eb54f8fSJack Wang .attrs = default_attrs,
6401eb54f8fSJack Wang };
6411eb54f8fSJack Wang
6421eb54f8fSJack Wang static const struct attribute_group *default_attr_groups[] = {
6431eb54f8fSJack Wang &default_attr_group,
6441eb54f8fSJack Wang NULL,
6451eb54f8fSJack Wang };
6461eb54f8fSJack Wang
rnbd_clt_create_sysfs_files(void)6471eb54f8fSJack Wang int rnbd_clt_create_sysfs_files(void)
6481eb54f8fSJack Wang {
6491eb54f8fSJack Wang int err;
6501eb54f8fSJack Wang
651137380c0SIvan Orlov err = class_register(&rnbd_dev_class);
652137380c0SIvan Orlov if (err)
653137380c0SIvan Orlov return err;
6541eb54f8fSJack Wang
655137380c0SIvan Orlov rnbd_dev = device_create_with_groups(&rnbd_dev_class, NULL,
6561eb54f8fSJack Wang MKDEV(0, 0), NULL,
6571eb54f8fSJack Wang default_attr_groups, "ctl");
6581eb54f8fSJack Wang if (IS_ERR(rnbd_dev)) {
6591eb54f8fSJack Wang err = PTR_ERR(rnbd_dev);
6601eb54f8fSJack Wang goto cls_destroy;
6611eb54f8fSJack Wang }
6621eb54f8fSJack Wang rnbd_devs_kobj = kobject_create_and_add("devices", &rnbd_dev->kobj);
6631eb54f8fSJack Wang if (!rnbd_devs_kobj) {
6641eb54f8fSJack Wang err = -ENOMEM;
6651eb54f8fSJack Wang goto dev_destroy;
6661eb54f8fSJack Wang }
6671eb54f8fSJack Wang
6681eb54f8fSJack Wang return 0;
6691eb54f8fSJack Wang
6701eb54f8fSJack Wang dev_destroy:
671137380c0SIvan Orlov device_destroy(&rnbd_dev_class, MKDEV(0, 0));
6721eb54f8fSJack Wang cls_destroy:
673137380c0SIvan Orlov class_unregister(&rnbd_dev_class);
6741eb54f8fSJack Wang
6751eb54f8fSJack Wang return err;
6761eb54f8fSJack Wang }
6771eb54f8fSJack Wang
rnbd_clt_destroy_sysfs_files(void)6781eb54f8fSJack Wang void rnbd_clt_destroy_sysfs_files(void)
6791eb54f8fSJack Wang {
6808e43c90aSGuoqing Jiang sysfs_remove_group(&rnbd_dev->kobj, &default_attr_group);
6811eb54f8fSJack Wang kobject_del(rnbd_devs_kobj);
6821eb54f8fSJack Wang kobject_put(rnbd_devs_kobj);
683137380c0SIvan Orlov device_destroy(&rnbd_dev_class, MKDEV(0, 0));
684137380c0SIvan Orlov class_unregister(&rnbd_dev_class);
6851eb54f8fSJack Wang }
686