cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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, &sectors);
    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}