rnbd-clt-sysfs.c 16 KB

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