scif_peer_bus.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Intel MIC Platform Software Stack (MPSS)
  3. *
  4. * Copyright(c) 2014 Intel Corporation.
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License, version 2, as
  8. * published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * Intel SCIF driver.
  16. */
  17. #include "scif_main.h"
  18. #include "../bus/scif_bus.h"
  19. #include "scif_peer_bus.h"
  20. static inline struct scif_peer_dev *
  21. dev_to_scif_peer(struct device *dev)
  22. {
  23. return container_of(dev, struct scif_peer_dev, dev);
  24. }
  25. struct bus_type scif_peer_bus = {
  26. .name = "scif_peer_bus",
  27. };
  28. static void scif_peer_release_dev(struct device *d)
  29. {
  30. struct scif_peer_dev *sdev = dev_to_scif_peer(d);
  31. struct scif_dev *scifdev = &scif_dev[sdev->dnode];
  32. scif_cleanup_scifdev(scifdev);
  33. kfree(sdev);
  34. }
  35. static int scif_peer_initialize_device(struct scif_dev *scifdev)
  36. {
  37. struct scif_peer_dev *spdev;
  38. int ret;
  39. spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
  40. if (!spdev) {
  41. ret = -ENOMEM;
  42. goto err;
  43. }
  44. spdev->dev.parent = scifdev->sdev->dev.parent;
  45. spdev->dev.release = scif_peer_release_dev;
  46. spdev->dnode = scifdev->node;
  47. spdev->dev.bus = &scif_peer_bus;
  48. dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode);
  49. device_initialize(&spdev->dev);
  50. get_device(&spdev->dev);
  51. rcu_assign_pointer(scifdev->spdev, spdev);
  52. mutex_lock(&scif_info.conflock);
  53. scif_info.total++;
  54. scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid);
  55. mutex_unlock(&scif_info.conflock);
  56. return 0;
  57. err:
  58. dev_err(&scifdev->sdev->dev,
  59. "dnode %d: initialize_device rc %d\n", scifdev->node, ret);
  60. return ret;
  61. }
  62. static int scif_peer_add_device(struct scif_dev *scifdev)
  63. {
  64. struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev);
  65. char pool_name[16];
  66. int ret;
  67. ret = device_add(&spdev->dev);
  68. put_device(&spdev->dev);
  69. if (ret) {
  70. dev_err(&scifdev->sdev->dev,
  71. "dnode %d: peer device_add failed\n", scifdev->node);
  72. goto put_spdev;
  73. }
  74. scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode);
  75. scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev,
  76. sizeof(struct scif_status), 1,
  77. 0);
  78. if (!scifdev->signal_pool) {
  79. dev_err(&scifdev->sdev->dev,
  80. "dnode %d: dmam_pool_create failed\n", scifdev->node);
  81. ret = -ENOMEM;
  82. goto del_spdev;
  83. }
  84. dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode);
  85. return 0;
  86. del_spdev:
  87. device_del(&spdev->dev);
  88. put_spdev:
  89. RCU_INIT_POINTER(scifdev->spdev, NULL);
  90. synchronize_rcu();
  91. put_device(&spdev->dev);
  92. mutex_lock(&scif_info.conflock);
  93. scif_info.total--;
  94. mutex_unlock(&scif_info.conflock);
  95. return ret;
  96. }
  97. void scif_add_peer_device(struct work_struct *work)
  98. {
  99. struct scif_dev *scifdev = container_of(work, struct scif_dev,
  100. peer_add_work);
  101. scif_peer_add_device(scifdev);
  102. }
  103. /*
  104. * Peer device registration is split into a device_initialize and a device_add.
  105. * The reason for doing this is as follows: First, peer device registration
  106. * itself cannot be done in the message processing thread and must be delegated
  107. * to another workqueue, otherwise if SCIF client probe, called during peer
  108. * device registration, calls scif_connect(..), it will block the message
  109. * processing thread causing a deadlock. Next, device_initialize is done in the
  110. * "top-half" message processing thread and device_add in the "bottom-half"
  111. * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing
  112. * concurrently with SCIF_INIT message processing is unable to get a reference
  113. * on the peer device, thereby failing the connect request.
  114. */
  115. void scif_peer_register_device(struct scif_dev *scifdev)
  116. {
  117. int ret;
  118. mutex_lock(&scifdev->lock);
  119. ret = scif_peer_initialize_device(scifdev);
  120. if (ret)
  121. goto exit;
  122. schedule_work(&scifdev->peer_add_work);
  123. exit:
  124. mutex_unlock(&scifdev->lock);
  125. }
  126. int scif_peer_unregister_device(struct scif_dev *scifdev)
  127. {
  128. struct scif_peer_dev *spdev;
  129. mutex_lock(&scifdev->lock);
  130. /* Flush work to ensure device register is complete */
  131. flush_work(&scifdev->peer_add_work);
  132. /*
  133. * Continue holding scifdev->lock since theoretically unregister_device
  134. * can be called simultaneously from multiple threads
  135. */
  136. spdev = rcu_dereference(scifdev->spdev);
  137. if (!spdev) {
  138. mutex_unlock(&scifdev->lock);
  139. return -ENODEV;
  140. }
  141. RCU_INIT_POINTER(scifdev->spdev, NULL);
  142. synchronize_rcu();
  143. mutex_unlock(&scifdev->lock);
  144. dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode);
  145. device_unregister(&spdev->dev);
  146. mutex_lock(&scif_info.conflock);
  147. scif_info.total--;
  148. mutex_unlock(&scif_info.conflock);
  149. return 0;
  150. }
  151. int scif_peer_bus_init(void)
  152. {
  153. return bus_register(&scif_peer_bus);
  154. }
  155. void scif_peer_bus_exit(void)
  156. {
  157. bus_unregister(&scif_peer_bus);
  158. }