ccwgroup.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. * drivers/s390/cio/ccwgroup.c
  3. * bus driver for ccwgroup
  4. *
  5. * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  6. * IBM Corporation
  7. * Author(s): Arnd Bergmann (arndb@de.ibm.com)
  8. * Cornelia Huck (cornelia.huck@de.ibm.com)
  9. */
  10. #include <linux/module.h>
  11. #include <linux/errno.h>
  12. #include <linux/slab.h>
  13. #include <linux/list.h>
  14. #include <linux/device.h>
  15. #include <linux/init.h>
  16. #include <linux/ctype.h>
  17. #include <linux/dcache.h>
  18. #include <asm/ccwdev.h>
  19. #include <asm/ccwgroup.h>
  20. /* In Linux 2.4, we had a channel device layer called "chandev"
  21. * that did all sorts of obscure stuff for networking devices.
  22. * This is another driver that serves as a replacement for just
  23. * one of its functions, namely the translation of single subchannels
  24. * to devices that use multiple subchannels.
  25. */
  26. /* a device matches a driver if all its slave devices match the same
  27. * entry of the driver */
  28. static int
  29. ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
  30. {
  31. struct ccwgroup_device *gdev;
  32. struct ccwgroup_driver *gdrv;
  33. gdev = to_ccwgroupdev(dev);
  34. gdrv = to_ccwgroupdrv(drv);
  35. if (gdev->creator_id == gdrv->driver_id)
  36. return 1;
  37. return 0;
  38. }
  39. static int
  40. ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
  41. {
  42. /* TODO */
  43. return 0;
  44. }
  45. static struct bus_type ccwgroup_bus_type;
  46. static void
  47. __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
  48. {
  49. int i;
  50. char str[8];
  51. for (i = 0; i < gdev->count; i++) {
  52. sprintf(str, "cdev%d", i);
  53. sysfs_remove_link(&gdev->dev.kobj, str);
  54. sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
  55. }
  56. }
  57. /*
  58. * Provide an 'ungroup' attribute so the user can remove group devices no
  59. * longer needed or accidentially created. Saves memory :)
  60. */
  61. static void ccwgroup_ungroup_callback(struct device *dev)
  62. {
  63. struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
  64. mutex_lock(&gdev->reg_mutex);
  65. if (device_is_registered(&gdev->dev)) {
  66. __ccwgroup_remove_symlinks(gdev);
  67. device_unregister(dev);
  68. }
  69. mutex_unlock(&gdev->reg_mutex);
  70. }
  71. static ssize_t
  72. ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  73. {
  74. struct ccwgroup_device *gdev;
  75. int rc;
  76. gdev = to_ccwgroupdev(dev);
  77. if (gdev->state != CCWGROUP_OFFLINE)
  78. return -EINVAL;
  79. /* Note that we cannot unregister the device from one of its
  80. * attribute methods, so we have to use this roundabout approach.
  81. */
  82. rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
  83. if (rc)
  84. count = rc;
  85. return count;
  86. }
  87. static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
  88. static void
  89. ccwgroup_release (struct device *dev)
  90. {
  91. struct ccwgroup_device *gdev;
  92. int i;
  93. gdev = to_ccwgroupdev(dev);
  94. for (i = 0; i < gdev->count; i++) {
  95. if (gdev->cdev[i]) {
  96. if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
  97. dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
  98. put_device(&gdev->cdev[i]->dev);
  99. }
  100. }
  101. kfree(gdev);
  102. }
  103. static int
  104. __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
  105. {
  106. char str[8];
  107. int i, rc;
  108. for (i = 0; i < gdev->count; i++) {
  109. rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj,
  110. "group_device");
  111. if (rc) {
  112. for (--i; i >= 0; i--)
  113. sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
  114. "group_device");
  115. return rc;
  116. }
  117. }
  118. for (i = 0; i < gdev->count; i++) {
  119. sprintf(str, "cdev%d", i);
  120. rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj,
  121. str);
  122. if (rc) {
  123. for (--i; i >= 0; i--) {
  124. sprintf(str, "cdev%d", i);
  125. sysfs_remove_link(&gdev->dev.kobj, str);
  126. }
  127. for (i = 0; i < gdev->count; i++)
  128. sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
  129. "group_device");
  130. return rc;
  131. }
  132. }
  133. return 0;
  134. }
  135. static int __get_next_bus_id(const char **buf, char *bus_id)
  136. {
  137. int rc, len;
  138. char *start, *end;
  139. start = (char *)*buf;
  140. end = strchr(start, ',');
  141. if (!end) {
  142. /* Last entry. Strip trailing newline, if applicable. */
  143. end = strchr(start, '\n');
  144. if (end)
  145. *end = '\0';
  146. len = strlen(start) + 1;
  147. } else {
  148. len = end - start + 1;
  149. end++;
  150. }
  151. if (len < BUS_ID_SIZE) {
  152. strlcpy(bus_id, start, len);
  153. rc = 0;
  154. } else
  155. rc = -EINVAL;
  156. *buf = end;
  157. return rc;
  158. }
  159. static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
  160. {
  161. int cssid, ssid, devno;
  162. /* Must be of form %x.%x.%04x */
  163. if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
  164. return 0;
  165. return 1;
  166. }
  167. /**
  168. * ccwgroup_create_from_string() - create and register a ccw group device
  169. * @root: parent device for the new device
  170. * @creator_id: identifier of creating driver
  171. * @cdrv: ccw driver of slave devices
  172. * @num_devices: number of slave devices
  173. * @buf: buffer containing comma separated bus ids of slave devices
  174. *
  175. * Create and register a new ccw group device as a child of @root. Slave
  176. * devices are obtained from the list of bus ids given in @buf and must all
  177. * belong to @cdrv.
  178. * Returns:
  179. * %0 on success and an error code on failure.
  180. * Context:
  181. * non-atomic
  182. */
  183. int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
  184. struct ccw_driver *cdrv, int num_devices,
  185. const char *buf)
  186. {
  187. struct ccwgroup_device *gdev;
  188. int rc, i;
  189. char tmp_bus_id[BUS_ID_SIZE];
  190. const char *curr_buf;
  191. gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
  192. GFP_KERNEL);
  193. if (!gdev)
  194. return -ENOMEM;
  195. atomic_set(&gdev->onoff, 0);
  196. mutex_init(&gdev->reg_mutex);
  197. mutex_lock(&gdev->reg_mutex);
  198. gdev->creator_id = creator_id;
  199. gdev->count = num_devices;
  200. gdev->dev.bus = &ccwgroup_bus_type;
  201. gdev->dev.parent = root;
  202. gdev->dev.release = ccwgroup_release;
  203. device_initialize(&gdev->dev);
  204. curr_buf = buf;
  205. for (i = 0; i < num_devices && curr_buf; i++) {
  206. rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
  207. if (rc != 0)
  208. goto error;
  209. if (!__is_valid_bus_id(tmp_bus_id)) {
  210. rc = -EINVAL;
  211. goto error;
  212. }
  213. gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id);
  214. /*
  215. * All devices have to be of the same type in
  216. * order to be grouped.
  217. */
  218. if (!gdev->cdev[i]
  219. || gdev->cdev[i]->id.driver_info !=
  220. gdev->cdev[0]->id.driver_info) {
  221. rc = -EINVAL;
  222. goto error;
  223. }
  224. /* Don't allow a device to belong to more than one group. */
  225. if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
  226. rc = -EINVAL;
  227. goto error;
  228. }
  229. dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
  230. }
  231. /* Check for sufficient number of bus ids. */
  232. if (i < num_devices && !curr_buf) {
  233. rc = -EINVAL;
  234. goto error;
  235. }
  236. /* Check for trailing stuff. */
  237. if (i == num_devices && strlen(curr_buf) > 0) {
  238. rc = -EINVAL;
  239. goto error;
  240. }
  241. dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
  242. rc = device_add(&gdev->dev);
  243. if (rc)
  244. goto error;
  245. get_device(&gdev->dev);
  246. rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
  247. if (rc) {
  248. device_unregister(&gdev->dev);
  249. goto error;
  250. }
  251. rc = __ccwgroup_create_symlinks(gdev);
  252. if (!rc) {
  253. mutex_unlock(&gdev->reg_mutex);
  254. put_device(&gdev->dev);
  255. return 0;
  256. }
  257. device_remove_file(&gdev->dev, &dev_attr_ungroup);
  258. device_unregister(&gdev->dev);
  259. error:
  260. for (i = 0; i < num_devices; i++)
  261. if (gdev->cdev[i]) {
  262. if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
  263. dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
  264. put_device(&gdev->cdev[i]->dev);
  265. gdev->cdev[i] = NULL;
  266. }
  267. mutex_unlock(&gdev->reg_mutex);
  268. put_device(&gdev->dev);
  269. return rc;
  270. }
  271. EXPORT_SYMBOL(ccwgroup_create_from_string);
  272. static int __init
  273. init_ccwgroup (void)
  274. {
  275. return bus_register (&ccwgroup_bus_type);
  276. }
  277. static void __exit
  278. cleanup_ccwgroup (void)
  279. {
  280. bus_unregister (&ccwgroup_bus_type);
  281. }
  282. module_init(init_ccwgroup);
  283. module_exit(cleanup_ccwgroup);
  284. /************************** driver stuff ******************************/
  285. static int
  286. ccwgroup_set_online(struct ccwgroup_device *gdev)
  287. {
  288. struct ccwgroup_driver *gdrv;
  289. int ret;
  290. if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
  291. return -EAGAIN;
  292. if (gdev->state == CCWGROUP_ONLINE) {
  293. ret = 0;
  294. goto out;
  295. }
  296. if (!gdev->dev.driver) {
  297. ret = -EINVAL;
  298. goto out;
  299. }
  300. gdrv = to_ccwgroupdrv (gdev->dev.driver);
  301. if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0))
  302. goto out;
  303. gdev->state = CCWGROUP_ONLINE;
  304. out:
  305. atomic_set(&gdev->onoff, 0);
  306. return ret;
  307. }
  308. static int
  309. ccwgroup_set_offline(struct ccwgroup_device *gdev)
  310. {
  311. struct ccwgroup_driver *gdrv;
  312. int ret;
  313. if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
  314. return -EAGAIN;
  315. if (gdev->state == CCWGROUP_OFFLINE) {
  316. ret = 0;
  317. goto out;
  318. }
  319. if (!gdev->dev.driver) {
  320. ret = -EINVAL;
  321. goto out;
  322. }
  323. gdrv = to_ccwgroupdrv (gdev->dev.driver);
  324. if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0))
  325. goto out;
  326. gdev->state = CCWGROUP_OFFLINE;
  327. out:
  328. atomic_set(&gdev->onoff, 0);
  329. return ret;
  330. }
  331. static ssize_t
  332. ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  333. {
  334. struct ccwgroup_device *gdev;
  335. struct ccwgroup_driver *gdrv;
  336. unsigned long value;
  337. int ret;
  338. gdev = to_ccwgroupdev(dev);
  339. if (!dev->driver)
  340. return count;
  341. gdrv = to_ccwgroupdrv (gdev->dev.driver);
  342. if (!try_module_get(gdrv->owner))
  343. return -EINVAL;
  344. ret = strict_strtoul(buf, 0, &value);
  345. if (ret)
  346. goto out;
  347. ret = count;
  348. if (value == 1)
  349. ccwgroup_set_online(gdev);
  350. else if (value == 0)
  351. ccwgroup_set_offline(gdev);
  352. else
  353. ret = -EINVAL;
  354. out:
  355. module_put(gdrv->owner);
  356. return ret;
  357. }
  358. static ssize_t
  359. ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf)
  360. {
  361. int online;
  362. online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);
  363. return sprintf(buf, online ? "1\n" : "0\n");
  364. }
  365. static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
  366. static int
  367. ccwgroup_probe (struct device *dev)
  368. {
  369. struct ccwgroup_device *gdev;
  370. struct ccwgroup_driver *gdrv;
  371. int ret;
  372. gdev = to_ccwgroupdev(dev);
  373. gdrv = to_ccwgroupdrv(dev->driver);
  374. if ((ret = device_create_file(dev, &dev_attr_online)))
  375. return ret;
  376. ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
  377. if (ret)
  378. device_remove_file(dev, &dev_attr_online);
  379. return ret;
  380. }
  381. static int
  382. ccwgroup_remove (struct device *dev)
  383. {
  384. struct ccwgroup_device *gdev;
  385. struct ccwgroup_driver *gdrv;
  386. gdev = to_ccwgroupdev(dev);
  387. gdrv = to_ccwgroupdrv(dev->driver);
  388. device_remove_file(dev, &dev_attr_online);
  389. if (gdrv && gdrv->remove)
  390. gdrv->remove(gdev);
  391. return 0;
  392. }
  393. static void ccwgroup_shutdown(struct device *dev)
  394. {
  395. struct ccwgroup_device *gdev;
  396. struct ccwgroup_driver *gdrv;
  397. gdev = to_ccwgroupdev(dev);
  398. gdrv = to_ccwgroupdrv(dev->driver);
  399. if (gdrv && gdrv->shutdown)
  400. gdrv->shutdown(gdev);
  401. }
  402. static struct bus_type ccwgroup_bus_type = {
  403. .name = "ccwgroup",
  404. .match = ccwgroup_bus_match,
  405. .uevent = ccwgroup_uevent,
  406. .probe = ccwgroup_probe,
  407. .remove = ccwgroup_remove,
  408. .shutdown = ccwgroup_shutdown,
  409. };
  410. /**
  411. * ccwgroup_driver_register() - register a ccw group driver
  412. * @cdriver: driver to be registered
  413. *
  414. * This function is mainly a wrapper around driver_register().
  415. */
  416. int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
  417. {
  418. /* register our new driver with the core */
  419. cdriver->driver.bus = &ccwgroup_bus_type;
  420. cdriver->driver.name = cdriver->name;
  421. cdriver->driver.owner = cdriver->owner;
  422. return driver_register(&cdriver->driver);
  423. }
  424. static int
  425. __ccwgroup_match_all(struct device *dev, void *data)
  426. {
  427. return 1;
  428. }
  429. /**
  430. * ccwgroup_driver_unregister() - deregister a ccw group driver
  431. * @cdriver: driver to be deregistered
  432. *
  433. * This function is mainly a wrapper around driver_unregister().
  434. */
  435. void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
  436. {
  437. struct device *dev;
  438. /* We don't want ccwgroup devices to live longer than their driver. */
  439. get_driver(&cdriver->driver);
  440. while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
  441. __ccwgroup_match_all))) {
  442. struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
  443. mutex_lock(&gdev->reg_mutex);
  444. __ccwgroup_remove_symlinks(gdev);
  445. device_unregister(dev);
  446. mutex_unlock(&gdev->reg_mutex);
  447. put_device(dev);
  448. }
  449. put_driver(&cdriver->driver);
  450. driver_unregister(&cdriver->driver);
  451. }
  452. /**
  453. * ccwgroup_probe_ccwdev() - probe function for slave devices
  454. * @cdev: ccw device to be probed
  455. *
  456. * This is a dummy probe function for ccw devices that are slave devices in
  457. * a ccw group device.
  458. * Returns:
  459. * always %0
  460. */
  461. int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
  462. {
  463. return 0;
  464. }
  465. static struct ccwgroup_device *
  466. __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
  467. {
  468. struct ccwgroup_device *gdev;
  469. gdev = dev_get_drvdata(&cdev->dev);
  470. if (gdev) {
  471. if (get_device(&gdev->dev)) {
  472. mutex_lock(&gdev->reg_mutex);
  473. if (device_is_registered(&gdev->dev))
  474. return gdev;
  475. mutex_unlock(&gdev->reg_mutex);
  476. put_device(&gdev->dev);
  477. }
  478. return NULL;
  479. }
  480. return NULL;
  481. }
  482. /**
  483. * ccwgroup_remove_ccwdev() - remove function for slave devices
  484. * @cdev: ccw device to be removed
  485. *
  486. * This is a remove function for ccw devices that are slave devices in a ccw
  487. * group device. It sets the ccw device offline and also deregisters the
  488. * embedding ccw group device.
  489. */
  490. void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
  491. {
  492. struct ccwgroup_device *gdev;
  493. /* Ignore offlining errors, device is gone anyway. */
  494. ccw_device_set_offline(cdev);
  495. /* If one of its devices is gone, the whole group is done for. */
  496. gdev = __ccwgroup_get_gdev_by_cdev(cdev);
  497. if (gdev) {
  498. __ccwgroup_remove_symlinks(gdev);
  499. device_unregister(&gdev->dev);
  500. mutex_unlock(&gdev->reg_mutex);
  501. put_device(&gdev->dev);
  502. }
  503. }
  504. MODULE_LICENSE("GPL");
  505. EXPORT_SYMBOL(ccwgroup_driver_register);
  506. EXPORT_SYMBOL(ccwgroup_driver_unregister);
  507. EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
  508. EXPORT_SYMBOL(ccwgroup_remove_ccwdev);