ccwgroup.c 13 KB

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