hab_vchan.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  5. */
  6. #include "hab.h"
  7. struct virtual_channel *
  8. hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan,
  9. int openid)
  10. {
  11. int id;
  12. struct virtual_channel *vchan;
  13. if (!pchan || !ctx)
  14. return NULL;
  15. vchan = kzalloc(sizeof(*vchan), GFP_KERNEL);
  16. if (!vchan)
  17. return NULL;
  18. /* This should be the first thing we do in this function */
  19. idr_preload(GFP_KERNEL);
  20. spin_lock_bh(&pchan->vid_lock);
  21. id = idr_alloc(&pchan->vchan_idr, vchan, 1,
  22. (HAB_VCID_ID_MASK >> HAB_VCID_ID_SHIFT) + 1, GFP_NOWAIT);
  23. spin_unlock_bh(&pchan->vid_lock);
  24. idr_preload_end();
  25. if (id <= 0) {
  26. pr_err("idr failed %d\n", id);
  27. kfree(vchan);
  28. return NULL;
  29. }
  30. mb(); /* id must be generated done before pchan_get */
  31. hab_pchan_get(pchan);
  32. vchan->pchan = pchan;
  33. /* vchan need both vcid and openid to be properly located */
  34. vchan->session_id = openid;
  35. write_lock(&pchan->vchans_lock);
  36. list_add_tail(&vchan->pnode, &pchan->vchannels);
  37. pchan->vcnt++;
  38. write_unlock(&pchan->vchans_lock);
  39. vchan->id = ((id << HAB_VCID_ID_SHIFT) & HAB_VCID_ID_MASK) |
  40. ((pchan->habdev->id << HAB_VCID_MMID_SHIFT) &
  41. HAB_VCID_MMID_MASK) |
  42. ((pchan->dom_id << HAB_VCID_DOMID_SHIFT) &
  43. HAB_VCID_DOMID_MASK);
  44. spin_lock_init(&vchan->rx_lock);
  45. INIT_LIST_HEAD(&vchan->rx_list);
  46. init_waitqueue_head(&vchan->rx_queue);
  47. kref_init(&vchan->refcount);
  48. vchan->otherend_closed = pchan->closed;
  49. hab_ctx_get(ctx);
  50. vchan->ctx = ctx;
  51. return vchan;
  52. }
  53. static void
  54. hab_vchan_free(struct kref *ref)
  55. {
  56. struct virtual_channel *vchan =
  57. container_of(ref, struct virtual_channel, refcount);
  58. struct hab_message *message, *msg_tmp;
  59. struct physical_channel *pchan = vchan->pchan;
  60. struct uhab_context *ctx = vchan->ctx;
  61. struct virtual_channel *vc, *vc_tmp;
  62. int irqs_disabled = irqs_disabled();
  63. hab_spin_lock(&vchan->rx_lock, irqs_disabled);
  64. list_for_each_entry_safe(message, msg_tmp, &vchan->rx_list, node) {
  65. list_del(&message->node);
  66. hab_msg_free(message);
  67. }
  68. hab_spin_unlock(&vchan->rx_lock, irqs_disabled);
  69. /* release vchan from pchan. no more msg for this vchan */
  70. hab_write_lock(&pchan->vchans_lock, irqs_disabled);
  71. list_for_each_entry_safe(vc, vc_tmp, &pchan->vchannels, pnode) {
  72. if (vchan == vc) {
  73. list_del(&vc->pnode);
  74. /* the ref is held in case of pchan is freed */
  75. pchan->vcnt--;
  76. break;
  77. }
  78. }
  79. hab_write_unlock(&pchan->vchans_lock, irqs_disabled);
  80. /* the release vchan from ctx was done earlier in vchan close() */
  81. hab_ctx_put(ctx); /* now ctx is not needed from this vchan's view */
  82. /* release idr at the last so same idr will not be used early */
  83. hab_spin_lock(&pchan->vid_lock, irqs_disabled);
  84. idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
  85. hab_spin_unlock(&pchan->vid_lock, irqs_disabled);
  86. hab_pchan_put(pchan); /* no more need for pchan from this vchan */
  87. kfree(vchan);
  88. }
  89. /*
  90. * only for msg recv path to retrieve vchan from vcid and openid based on
  91. * pchan's vchan list
  92. */
  93. struct virtual_channel*
  94. hab_vchan_get(struct physical_channel *pchan, struct hab_header *header)
  95. {
  96. struct virtual_channel *vchan;
  97. uint32_t vchan_id = HAB_HEADER_GET_ID(*header);
  98. uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header);
  99. size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
  100. uint32_t payload_type = HAB_HEADER_GET_TYPE(*header);
  101. int irqs_disabled = irqs_disabled();
  102. hab_spin_lock(&pchan->vid_lock, irqs_disabled);
  103. vchan = idr_find(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan_id));
  104. if (vchan) {
  105. if (vchan->session_id != session_id)
  106. /*
  107. * skipped if session is different even vcid
  108. * is the same
  109. */
  110. vchan = NULL;
  111. else if (!vchan->otherend_id /*&& !vchan->session_id*/) {
  112. /*
  113. * not paired vchan can be fetched right after it is
  114. * alloc'ed. so it has to be skipped during search
  115. * for remote msg
  116. */
  117. pr_warn("vcid %x is not paired yet session %d refcnt %d type %d sz %zd\n",
  118. vchan->id, vchan->otherend_id,
  119. get_refcnt(vchan->refcount),
  120. payload_type, sizebytes);
  121. vchan = NULL;
  122. } else if (vchan->otherend_closed || vchan->closed) {
  123. pr_debug("closed already remote %d local %d vcid %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
  124. vchan->otherend_closed, vchan->closed,
  125. vchan->id, vchan->otherend_id,
  126. vchan->session_id, get_refcnt(vchan->refcount),
  127. vchan_id, session_id, payload_type, sizebytes);
  128. vchan = NULL;
  129. } else if (!kref_get_unless_zero(&vchan->refcount)) {
  130. /*
  131. * this happens when refcnt is already zero
  132. * (put from other thread) or there is an actual error
  133. */
  134. pr_err("failed to inc vcid %pK %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
  135. vchan, vchan->id, vchan->otherend_id,
  136. vchan->session_id, get_refcnt(vchan->refcount),
  137. vchan_id, session_id, payload_type, sizebytes);
  138. vchan = NULL;
  139. }
  140. }
  141. hab_spin_unlock(&pchan->vid_lock, irqs_disabled);
  142. return vchan;
  143. }
  144. /* wake up local waiting Q, so stop-vchan can be processed */
  145. void hab_vchan_stop(struct virtual_channel *vchan)
  146. {
  147. if (vchan) {
  148. vchan->otherend_closed = 1;
  149. wake_up(&vchan->rx_queue);
  150. if (vchan->ctx)
  151. if (vchan->pchan->mem_proto == 1)
  152. wake_up_interruptible(&vchan->ctx->imp_wq);
  153. else
  154. wake_up_interruptible(&vchan->ctx->exp_wq);
  155. else
  156. pr_err("NULL ctx for vchan %x\n", vchan->id);
  157. }
  158. }
  159. void hab_vchans_stop(struct physical_channel *pchan)
  160. {
  161. struct virtual_channel *vchan, *tmp;
  162. read_lock(&pchan->vchans_lock);
  163. list_for_each_entry_safe(vchan, tmp, &pchan->vchannels, pnode) {
  164. hab_vchan_stop(vchan);
  165. }
  166. read_unlock(&pchan->vchans_lock);
  167. }
  168. /* send vchan close to remote and stop receiving anything locally */
  169. void hab_vchan_stop_notify(struct virtual_channel *vchan)
  170. {
  171. hab_send_close_msg(vchan);
  172. hab_vchan_stop(vchan);
  173. }
  174. static int hab_vchans_per_pchan_empty(struct physical_channel *pchan)
  175. {
  176. int empty;
  177. read_lock(&pchan->vchans_lock);
  178. empty = list_empty(&pchan->vchannels);
  179. if (!empty) {
  180. struct virtual_channel *vchan;
  181. int vcnt = pchan->vcnt;
  182. list_for_each_entry(vchan, &pchan->vchannels, pnode) {
  183. /* discount open-pending unpaired vchan */
  184. if (!vchan->session_id)
  185. vcnt--;
  186. else
  187. pr_err("vchan %pK %x rm %x sn %d rf %d clsd %d rm clsd %d\n",
  188. vchan, vchan->id,
  189. vchan->otherend_id,
  190. vchan->session_id,
  191. get_refcnt(vchan->refcount),
  192. vchan->closed, vchan->otherend_closed);
  193. }
  194. if (!vcnt)
  195. empty = 1;/* unpaired vchan can exist at init time */
  196. }
  197. read_unlock(&pchan->vchans_lock);
  198. return empty;
  199. }
  200. static int hab_vchans_empty(int vmid)
  201. {
  202. int i, empty = 1;
  203. struct physical_channel *pchan;
  204. struct hab_device *hab_dev;
  205. for (i = 0; i < hab_driver.ndevices; i++) {
  206. hab_dev = &hab_driver.devp[i];
  207. read_lock_bh(&hab_dev->pchan_lock);
  208. list_for_each_entry(pchan, &hab_dev->pchannels, node) {
  209. if (pchan->vmid_remote == vmid) {
  210. if (!hab_vchans_per_pchan_empty(pchan)) {
  211. empty = 0;
  212. pr_info("vmid %d %s's vchans are not closed\n",
  213. vmid, pchan->name);
  214. break;
  215. }
  216. }
  217. }
  218. read_unlock_bh(&hab_dev->pchan_lock);
  219. }
  220. return empty;
  221. }
  222. /*
  223. * block until all vchans of a given GVM are explicitly closed
  224. * with habmm_socket_close() by hab clients themselves
  225. */
  226. void hab_vchans_empty_wait(int vmid)
  227. {
  228. pr_info("waiting for GVM%d's sockets closure\n", vmid);
  229. while (!hab_vchans_empty(vmid))
  230. usleep_range(10000, 12000);
  231. pr_info("all of GVM%d's sockets are closed\n", vmid);
  232. }
  233. int hab_vchan_find_domid(struct virtual_channel *vchan)
  234. {
  235. return vchan ? vchan->pchan->dom_id : -1;
  236. }
  237. void hab_vchan_put(struct virtual_channel *vchan)
  238. {
  239. if (vchan)
  240. kref_put(&vchan->refcount, hab_vchan_free);
  241. }
  242. int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,
  243. char *names, size_t name_size, uint32_t flags)
  244. {
  245. struct virtual_channel *vchan;
  246. vchan = hab_get_vchan_fromvcid(vcid, ctx, 1);
  247. if (!vchan)
  248. return -EINVAL;
  249. if (vchan->otherend_closed) {
  250. hab_vchan_put(vchan);
  251. return -ENODEV;
  252. }
  253. *ids = vchan->pchan->vmid_local |
  254. ((uint64_t)vchan->pchan->vmid_remote) << 32;
  255. names[0] = 0;
  256. names[name_size/2] = 0;
  257. hab_vchan_put(vchan);
  258. return 0;
  259. }