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