xref: /OK3568_Linux_fs/kernel/drivers/block/rnbd/rnbd-clt-sysfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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, &sectors);
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