gunyah_irqfd.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #include <linux/eventfd.h>
  6. #include <linux/file.h>
  7. #include <linux/fs.h>
  8. #include <linux/gunyah.h>
  9. #include <linux/gunyah_vm_mgr.h>
  10. #include <linux/module.h>
  11. #include <linux/poll.h>
  12. #include <linux/printk.h>
  13. #include <uapi/linux/gunyah.h>
  14. struct gh_irqfd {
  15. struct gh_resource *ghrsc;
  16. struct gh_vm_resource_ticket ticket;
  17. struct gh_vm_function_instance *f;
  18. bool level;
  19. struct eventfd_ctx *ctx;
  20. wait_queue_entry_t wait;
  21. poll_table pt;
  22. };
  23. static int irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key)
  24. {
  25. struct gh_irqfd *irqfd = container_of(wait, struct gh_irqfd, wait);
  26. __poll_t flags = key_to_poll(key);
  27. int ret = 0;
  28. if (flags & EPOLLIN) {
  29. if (irqfd->ghrsc) {
  30. ret = gh_hypercall_bell_send(irqfd->ghrsc->capid, 1, NULL);
  31. if (ret)
  32. pr_err_ratelimited("Failed to inject interrupt %d: %d\n",
  33. irqfd->ticket.label, ret);
  34. } else
  35. pr_err_ratelimited("Premature injection of interrupt\n");
  36. }
  37. return 0;
  38. }
  39. static void irqfd_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh, poll_table *pt)
  40. {
  41. struct gh_irqfd *irq_ctx = container_of(pt, struct gh_irqfd, pt);
  42. add_wait_queue(wqh, &irq_ctx->wait);
  43. }
  44. static bool gh_irqfd_populate(struct gh_vm_resource_ticket *ticket, struct gh_resource *ghrsc)
  45. {
  46. struct gh_irqfd *irqfd = container_of(ticket, struct gh_irqfd, ticket);
  47. int ret;
  48. if (irqfd->ghrsc) {
  49. pr_warn("irqfd%d already got a Gunyah resource. Check if multiple resources with same label were configured.\n",
  50. irqfd->ticket.label);
  51. return false;
  52. }
  53. irqfd->ghrsc = ghrsc;
  54. if (irqfd->level) {
  55. /* Configure the bell to trigger when bit 0 is asserted (see
  56. * irq_wakeup) and for bell to automatically clear bit 0 once
  57. * received by the VM (ack_mask). need to make sure bit 0 is cleared right away,
  58. * otherwise the line will never be deasserted. Emulating edge
  59. * trigger interrupt does not need to set either mask
  60. * because irq is listed only once per gh_hypercall_bell_send
  61. */
  62. ret = gh_hypercall_bell_set_mask(irqfd->ghrsc->capid, 1, 1);
  63. if (ret)
  64. pr_warn("irq %d couldn't be set as level triggered. Might cause IRQ storm if asserted\n",
  65. irqfd->ticket.label);
  66. }
  67. return true;
  68. }
  69. static void gh_irqfd_unpopulate(struct gh_vm_resource_ticket *ticket, struct gh_resource *ghrsc)
  70. {
  71. struct gh_irqfd *irqfd = container_of(ticket, struct gh_irqfd, ticket);
  72. u64 cnt;
  73. eventfd_ctx_remove_wait_queue(irqfd->ctx, &irqfd->wait, &cnt);
  74. }
  75. static long gh_irqfd_bind(struct gh_vm_function_instance *f)
  76. {
  77. struct gh_fn_irqfd_arg *args = f->argp;
  78. struct gh_irqfd *irqfd;
  79. __poll_t events;
  80. struct fd fd;
  81. long r;
  82. if (f->arg_size != sizeof(*args))
  83. return -EINVAL;
  84. /* All other flag bits are reserved for future use */
  85. if (args->flags & ~GH_IRQFD_FLAGS_LEVEL)
  86. return -EINVAL;
  87. irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL);
  88. if (!irqfd)
  89. return -ENOMEM;
  90. irqfd->f = f;
  91. f->data = irqfd;
  92. fd = fdget(args->fd);
  93. if (!fd.file) {
  94. kfree(irqfd);
  95. return -EBADF;
  96. }
  97. irqfd->ctx = eventfd_ctx_fileget(fd.file);
  98. if (IS_ERR(irqfd->ctx)) {
  99. r = PTR_ERR(irqfd->ctx);
  100. goto err_fdput;
  101. }
  102. if (args->flags & GH_IRQFD_FLAGS_LEVEL)
  103. irqfd->level = true;
  104. init_waitqueue_func_entry(&irqfd->wait, irqfd_wakeup);
  105. init_poll_funcptr(&irqfd->pt, irqfd_ptable_queue_proc);
  106. irqfd->ticket.resource_type = GH_RESOURCE_TYPE_BELL_TX;
  107. irqfd->ticket.label = args->label;
  108. irqfd->ticket.owner = THIS_MODULE;
  109. irqfd->ticket.populate = gh_irqfd_populate;
  110. irqfd->ticket.unpopulate = gh_irqfd_unpopulate;
  111. r = gh_vm_add_resource_ticket(f->ghvm, &irqfd->ticket);
  112. if (r)
  113. goto err_ctx;
  114. events = vfs_poll(fd.file, &irqfd->pt);
  115. if (events & EPOLLIN)
  116. pr_warn("Premature injection of interrupt\n");
  117. fdput(fd);
  118. return 0;
  119. err_ctx:
  120. eventfd_ctx_put(irqfd->ctx);
  121. err_fdput:
  122. fdput(fd);
  123. kfree(irqfd);
  124. return r;
  125. }
  126. static void gh_irqfd_unbind(struct gh_vm_function_instance *f)
  127. {
  128. struct gh_irqfd *irqfd = f->data;
  129. gh_vm_remove_resource_ticket(irqfd->f->ghvm, &irqfd->ticket);
  130. eventfd_ctx_put(irqfd->ctx);
  131. kfree(irqfd);
  132. }
  133. static bool gh_irqfd_compare(const struct gh_vm_function_instance *f,
  134. const void *arg, size_t size)
  135. {
  136. const struct gh_fn_irqfd_arg *instance = f->argp,
  137. *other = arg;
  138. if (sizeof(*other) != size)
  139. return false;
  140. return instance->label == other->label;
  141. }
  142. DECLARE_GH_VM_FUNCTION_INIT(irqfd, GH_FN_IRQFD, 2, gh_irqfd_bind, gh_irqfd_unbind,
  143. gh_irqfd_compare);
  144. MODULE_DESCRIPTION("Gunyah irqfd VM Function");
  145. MODULE_LICENSE("GPL");