gh_irq_lend.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
  5. *
  6. */
  7. #include <linux/irq.h>
  8. #include <linux/module.h>
  9. #include <linux/notifier.h>
  10. #include <linux/spinlock.h>
  11. #include <dt-bindings/interrupt-controller/arm-gic.h>
  12. #include <linux/gunyah/gh_irq_lend.h>
  13. #include <linux/gunyah/gh_rm_drv.h>
  14. #include "gh_rm_drv_private.h"
  15. struct gh_irq_entry {
  16. gh_vmid_t vmid;
  17. enum gh_vm_names vm_name;
  18. gh_irq_handle_fn_v2 v2_handle;
  19. gh_irq_handle_fn handle;
  20. void *data;
  21. enum {
  22. GH_IRQ_STATE_NONE,
  23. GH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT,
  24. GH_IRQ_STATE_WAIT_LEND,
  25. /* notification states */
  26. GH_IRQ_STATE_RELEASED, /* svm -> pvm */
  27. GH_IRQ_STATE_ACCEPTED, /* svm -> pvm */
  28. GH_IRQ_STATE_LENT, /* pvm -> svm */
  29. } state;
  30. gh_virq_handle_t virq_handle;
  31. };
  32. static struct gh_irq_entry gh_irq_entries[GH_IRQ_LABEL_MAX];
  33. static DEFINE_SPINLOCK(gh_irq_lend_lock);
  34. static int gh_irq_released_accepted_nb_handler(struct notifier_block *this,
  35. unsigned long cmd, void *data)
  36. {
  37. unsigned long flags;
  38. enum gh_irq_label label;
  39. struct gh_irq_entry *entry;
  40. struct gh_rm_notif_vm_irq_released_payload *released;
  41. struct gh_rm_notif_vm_irq_accepted_payload *accepted;
  42. if (cmd != GH_RM_NOTIF_VM_IRQ_RELEASED &&
  43. cmd != GH_RM_NOTIF_VM_IRQ_ACCEPTED)
  44. return NOTIFY_DONE;
  45. spin_lock_irqsave(&gh_irq_lend_lock, flags);
  46. for (label = 0; label < GH_IRQ_LABEL_MAX; label++) {
  47. entry = &gh_irq_entries[label];
  48. if (entry->state != GH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT &&
  49. entry->state != GH_IRQ_STATE_ACCEPTED)
  50. continue;
  51. switch (cmd) {
  52. case GH_RM_NOTIF_VM_IRQ_RELEASED:
  53. released = data;
  54. if (released->virq_handle == entry->virq_handle) {
  55. entry->state = GH_IRQ_STATE_RELEASED;
  56. spin_unlock_irqrestore(&gh_irq_lend_lock,
  57. flags);
  58. entry->v2_handle(entry->data, cmd, label);
  59. return NOTIFY_OK;
  60. }
  61. break;
  62. case GH_RM_NOTIF_VM_IRQ_ACCEPTED:
  63. accepted = data;
  64. if (accepted->virq_handle == entry->virq_handle) {
  65. entry->state = GH_IRQ_STATE_ACCEPTED;
  66. spin_unlock_irqrestore(&gh_irq_lend_lock,
  67. flags);
  68. entry->v2_handle(entry->data, cmd, label);
  69. return NOTIFY_OK;
  70. }
  71. break;
  72. }
  73. }
  74. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  75. return NOTIFY_DONE;
  76. }
  77. static struct notifier_block gh_irq_released_accepted_nb = {
  78. .notifier_call = gh_irq_released_accepted_nb_handler,
  79. };
  80. static int gh_irq_lent_nb_handler(struct notifier_block *this,
  81. unsigned long cmd, void *data)
  82. {
  83. unsigned long flags;
  84. enum gh_irq_label label;
  85. enum gh_vm_names owner_name;
  86. struct gh_irq_entry *entry;
  87. struct gh_rm_notif_vm_irq_lent_payload *lent = data;
  88. int ret;
  89. if (cmd != GH_RM_NOTIF_VM_IRQ_LENT)
  90. return NOTIFY_DONE;
  91. ret = gh_rm_get_vm_name(lent->owner_vmid, &owner_name);
  92. if (ret) {
  93. pr_warn_ratelimited("%s: unknown name for vmid: %d\n", __func__,
  94. lent->owner_vmid);
  95. return ret;
  96. }
  97. spin_lock_irqsave(&gh_irq_lend_lock, flags);
  98. for (label = 0; label < GH_IRQ_LABEL_MAX; label++) {
  99. entry = &gh_irq_entries[label];
  100. if (entry->state != GH_IRQ_STATE_WAIT_LEND &&
  101. entry->state != GH_IRQ_STATE_LENT)
  102. continue;
  103. if (label == lent->virq_label &&
  104. (entry->vm_name == GH_VM_MAX ||
  105. entry->vm_name == owner_name)) {
  106. entry->vmid = lent->owner_vmid;
  107. entry->virq_handle = lent->virq_handle;
  108. entry->state = GH_IRQ_STATE_LENT;
  109. spin_unlock_irqrestore(&gh_irq_lend_lock,
  110. flags);
  111. entry->v2_handle(entry->data, cmd, label);
  112. return NOTIFY_OK;
  113. }
  114. }
  115. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  116. return NOTIFY_DONE;
  117. }
  118. static struct notifier_block gh_irq_lent_nb = {
  119. .notifier_call = gh_irq_lent_nb_handler,
  120. };
  121. /**
  122. * gh_irq_lend_v2: Lend a hardware interrupt to another VM
  123. * @label: vIRQ high-level label
  124. * @name: VM name to send interrupt to
  125. * @irq: Linux IRQ number to lend
  126. * @cb_handle: callback to invoke when other VM release or accept the interrupt
  127. * @data: Argument to pass to cb_handle
  128. *
  129. * Returns 0 on success also the handle corresponding to Linux IRQ#.
  130. * Returns < 0 on error
  131. */
  132. int gh_irq_lend_v2(enum gh_irq_label label, enum gh_vm_names name,
  133. int irq, gh_irq_handle_fn_v2 cb_handle, void *data)
  134. {
  135. int ret, virq;
  136. unsigned long flags;
  137. struct gh_irq_entry *entry;
  138. if (label >= GH_IRQ_LABEL_MAX || !cb_handle)
  139. return -EINVAL;
  140. entry = &gh_irq_entries[label];
  141. if (gh_rm_irq_to_virq(irq, &virq))
  142. return -EINVAL;
  143. spin_lock_irqsave(&gh_irq_lend_lock, flags);
  144. if (entry->state != GH_IRQ_STATE_NONE) {
  145. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  146. return -EINVAL;
  147. }
  148. ret = ghd_rm_get_vmid(name, &entry->vmid);
  149. if (ret) {
  150. entry->state = GH_IRQ_STATE_NONE;
  151. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  152. return ret;
  153. }
  154. entry->v2_handle = cb_handle;
  155. entry->data = data;
  156. entry->state = GH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT;
  157. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  158. return gh_rm_vm_irq_lend(entry->vmid, virq, label, &entry->virq_handle);
  159. }
  160. EXPORT_SYMBOL(gh_irq_lend_v2);
  161. /**
  162. * gh_irq_lend: Lend a hardware interrupt to another VM
  163. * @label: vIRQ high-level label
  164. * @name: VM name to send interrupt to
  165. * @irq: Linux IRQ number to lend
  166. * @cb_handle: callback to invoke when other VM release or accept the interrupt
  167. * @data: Argument to pass to cb_handle
  168. *
  169. * Returns 0 on success also the handle corresponding to Linux IRQ#.
  170. * Returns < 0 on error
  171. */
  172. int gh_irq_lend(enum gh_irq_label label, enum gh_vm_names name,
  173. int irq, gh_irq_handle_fn cb_handle, void *data)
  174. {
  175. struct gh_irq_entry *entry;
  176. if (label >= GH_IRQ_LABEL_MAX || !cb_handle)
  177. return -EINVAL;
  178. entry = &gh_irq_entries[label];
  179. entry->handle = cb_handle;
  180. return 0;
  181. }
  182. EXPORT_SYMBOL(gh_irq_lend);
  183. /**
  184. * gh_irq_lend_notify: Pass the irq handle to other VM for accept
  185. * @label: vIRQ high-level label
  186. *
  187. * Returns 0 on success, < 0 on error
  188. */
  189. int gh_irq_lend_notify(enum gh_irq_label label)
  190. {
  191. struct gh_irq_entry *entry;
  192. if (label >= GH_IRQ_LABEL_MAX)
  193. return -EINVAL;
  194. entry = &gh_irq_entries[label];
  195. if (entry->state == GH_IRQ_STATE_NONE)
  196. return -EINVAL;
  197. return gh_rm_vm_irq_lend_notify(entry->vmid, entry->virq_handle);
  198. }
  199. EXPORT_SYMBOL(gh_irq_lend_notify);
  200. /**
  201. * gh_irq_reclaim: Reclaim a hardware interrupt after other VM
  202. * has released.
  203. * @label: vIRQ high-level label
  204. *
  205. * This function should be called inside or after on_release()
  206. * callback from gh_irq_lend.
  207. * This function is not thread-safe. Do not race with another gh_irq_reclaim
  208. * with same label
  209. */
  210. int gh_irq_reclaim(enum gh_irq_label label)
  211. {
  212. int ret;
  213. struct gh_irq_entry *entry;
  214. if (label >= GH_IRQ_LABEL_MAX)
  215. return -EINVAL;
  216. entry = &gh_irq_entries[label];
  217. if (entry->state != GH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT &&
  218. (entry->state != GH_IRQ_STATE_RELEASED))
  219. return -EINVAL;
  220. ret = gh_rm_vm_irq_reclaim(entry->virq_handle);
  221. if (!ret)
  222. entry->state = GH_IRQ_STATE_NONE;
  223. return ret;
  224. }
  225. EXPORT_SYMBOL(gh_irq_reclaim);
  226. /**
  227. * gh_irq_wait_for_lend_v2: Register to claim a lent interrupt from another VM
  228. * @label: vIRQ high-level label
  229. * @name: Lender's VM name. If don't care, then use GH_VM_MAX
  230. * @on_lend: callback to invoke when other VM lends the interrupt
  231. * @data: Argument to pass to on_lend
  232. */
  233. int gh_irq_wait_for_lend_v2(enum gh_irq_label label, enum gh_vm_names name,
  234. gh_irq_handle_fn_v2 on_lend, void *data)
  235. {
  236. unsigned long flags;
  237. struct gh_irq_entry *entry;
  238. if (label >= GH_IRQ_LABEL_MAX || !on_lend)
  239. return -EINVAL;
  240. entry = &gh_irq_entries[label];
  241. spin_lock_irqsave(&gh_irq_lend_lock, flags);
  242. if (entry->state != GH_IRQ_STATE_NONE) {
  243. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  244. return -EINVAL;
  245. }
  246. entry->vm_name = name;
  247. entry->v2_handle = on_lend;
  248. entry->data = data;
  249. entry->state = GH_IRQ_STATE_WAIT_LEND;
  250. spin_unlock_irqrestore(&gh_irq_lend_lock, flags);
  251. return 0;
  252. }
  253. EXPORT_SYMBOL(gh_irq_wait_for_lend_v2);
  254. /**
  255. * gh_irq_wait_lend: Register to claim a lent interrupt from another VM
  256. * @label: vIRQ high-level label
  257. * @name: Lender's VM name. If don't care, then use GH_VM_MAX
  258. * @on_lend: callback to invoke when other VM lends the interrupt
  259. * @data: Argument to pass to on_lend
  260. */
  261. int gh_irq_wait_for_lend(enum gh_irq_label label, enum gh_vm_names name,
  262. gh_irq_handle_fn on_lend, void *data)
  263. {
  264. return 0;
  265. }
  266. EXPORT_SYMBOL(gh_irq_wait_for_lend);
  267. /**
  268. * gh_irq_accept: Register to receive interrupts with a lent vIRQ
  269. * @label: vIRQ high-level label
  270. * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1
  271. * @type: IRQ flags to use when allowing RM to choose the IRQ. If irq parameter
  272. * is specified, then type is unused.
  273. *
  274. * Returns the Linux IRQ# that vIRQ was registered to on success.
  275. * Returns <0 on error
  276. * This function is not thread-safe w.r.t. IRQ lend state. Do not race with
  277. * gh_irq_release or another gh_irq_accept with same label.
  278. */
  279. int gh_irq_accept(enum gh_irq_label label, int irq, int type)
  280. {
  281. struct gh_irq_entry *entry;
  282. int virq;
  283. if (label >= GH_IRQ_LABEL_MAX)
  284. return -EINVAL;
  285. entry = &gh_irq_entries[label];
  286. if (entry->state != GH_IRQ_STATE_LENT)
  287. return -EINVAL;
  288. if (irq != -1) {
  289. if (gh_rm_irq_to_virq(irq, &virq))
  290. return -EINVAL;
  291. } else
  292. virq = -1;
  293. virq = gh_rm_vm_irq_accept(entry->virq_handle, virq);
  294. if (virq < 0)
  295. return virq;
  296. if (irq == -1)
  297. irq = gh_rm_virq_to_irq(virq, type);
  298. entry->state = GH_IRQ_STATE_ACCEPTED;
  299. return irq;
  300. }
  301. EXPORT_SYMBOL(gh_irq_accept);
  302. /**
  303. * gh_irq_accept_notify: Notify the lend vm (pvm) that IRQ is accepted
  304. * @label: vIRQ high-level label
  305. * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1
  306. *
  307. * Returns the Linux IRQ# that vIRQ was registered to on success.
  308. * Returns <0 on error
  309. * This function is not thread-safe w.r.t. IRQ lend state. Do not race with
  310. * gh_irq_release or another gh_irq_accept with same label.
  311. */
  312. int gh_irq_accept_notify(enum gh_irq_label label)
  313. {
  314. struct gh_irq_entry *entry;
  315. if (label >= GH_IRQ_LABEL_MAX)
  316. return -EINVAL;
  317. entry = &gh_irq_entries[label];
  318. if (entry->state != GH_IRQ_STATE_ACCEPTED)
  319. return -EINVAL;
  320. return gh_rm_vm_irq_accept_notify(entry->vmid,
  321. entry->virq_handle);
  322. }
  323. EXPORT_SYMBOL(gh_irq_accept_notify);
  324. /**
  325. * gh_irq_release: Release a lent interrupt
  326. * @label: vIRQ high-level label
  327. * This function is not thread-safe w.r.t. IRQ lend state. Do not race with
  328. * gh_irq_accept or another gh_irq_release with same label.
  329. */
  330. int gh_irq_release(enum gh_irq_label label)
  331. {
  332. int ret;
  333. struct gh_irq_entry *entry;
  334. if (label >= GH_IRQ_LABEL_MAX)
  335. return -EINVAL;
  336. entry = &gh_irq_entries[label];
  337. if (entry->state != GH_IRQ_STATE_ACCEPTED)
  338. return -EINVAL;
  339. ret = gh_rm_vm_irq_release(entry->virq_handle);
  340. if (!ret)
  341. entry->state = GH_IRQ_STATE_WAIT_LEND;
  342. return ret;
  343. }
  344. EXPORT_SYMBOL(gh_irq_release);
  345. int gh_irq_release_notify(enum gh_irq_label label)
  346. {
  347. struct gh_irq_entry *entry;
  348. if (label >= GH_IRQ_LABEL_MAX)
  349. return -EINVAL;
  350. entry = &gh_irq_entries[label];
  351. if (entry->state != GH_IRQ_STATE_ACCEPTED &&
  352. entry->state != GH_IRQ_STATE_WAIT_LEND)
  353. return -EINVAL;
  354. return gh_rm_vm_irq_release_notify(entry->vmid,
  355. entry->virq_handle);
  356. }
  357. EXPORT_SYMBOL(gh_irq_release_notify);
  358. static int __init gh_irq_lend_init(void)
  359. {
  360. int ret;
  361. ret = gh_rm_register_notifier(&gh_irq_lent_nb);
  362. if (ret)
  363. return ret;
  364. return gh_rm_register_notifier(&gh_irq_released_accepted_nb);
  365. }
  366. module_init(gh_irq_lend_init);
  367. static void gh_irq_lend_exit(void)
  368. {
  369. gh_rm_unregister_notifier(&gh_irq_lent_nb);
  370. gh_rm_unregister_notifier(&gh_irq_released_accepted_nb);
  371. }
  372. module_exit(gh_irq_lend_exit);
  373. MODULE_LICENSE("GPL");
  374. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Gunyah IRQ Lending Library");