rnbd-clt-sysfs.c 16 KB

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