iosm_ipc_task_queue.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2020-21 Intel Corporation.
  4. */
  5. #include "iosm_ipc_imem.h"
  6. #include "iosm_ipc_task_queue.h"
  7. /* Actual tasklet function, will be called whenever tasklet is scheduled.
  8. * Calls event handler involves callback for each element in the message queue
  9. */
  10. static void ipc_task_queue_handler(unsigned long data)
  11. {
  12. struct ipc_task_queue *ipc_task = (struct ipc_task_queue *)data;
  13. unsigned int q_rpos = ipc_task->q_rpos;
  14. /* Loop over the input queue contents. */
  15. while (q_rpos != ipc_task->q_wpos) {
  16. /* Get the current first queue element. */
  17. struct ipc_task_queue_args *args = &ipc_task->args[q_rpos];
  18. /* Process the input message. */
  19. if (args->func)
  20. args->response = args->func(args->ipc_imem, args->arg,
  21. args->msg, args->size);
  22. /* Signal completion for synchronous calls */
  23. if (args->completion)
  24. complete(args->completion);
  25. /* Free message if copy was allocated. */
  26. if (args->is_copy)
  27. kfree(args->msg);
  28. /* Set invalid queue element. Technically
  29. * spin_lock_irqsave is not required here as
  30. * the array element has been processed already
  31. * so we can assume that immediately after processing
  32. * ipc_task element, queue will not rotate again to
  33. * ipc_task same element within such short time.
  34. */
  35. args->completion = NULL;
  36. args->func = NULL;
  37. args->msg = NULL;
  38. args->size = 0;
  39. args->is_copy = false;
  40. /* calculate the new read ptr and update the volatile read
  41. * ptr
  42. */
  43. q_rpos = (q_rpos + 1) % IPC_THREAD_QUEUE_SIZE;
  44. ipc_task->q_rpos = q_rpos;
  45. }
  46. }
  47. /* Free memory alloc and trigger completions left in the queue during dealloc */
  48. static void ipc_task_queue_cleanup(struct ipc_task_queue *ipc_task)
  49. {
  50. unsigned int q_rpos = ipc_task->q_rpos;
  51. while (q_rpos != ipc_task->q_wpos) {
  52. struct ipc_task_queue_args *args = &ipc_task->args[q_rpos];
  53. if (args->completion)
  54. complete(args->completion);
  55. if (args->is_copy)
  56. kfree(args->msg);
  57. q_rpos = (q_rpos + 1) % IPC_THREAD_QUEUE_SIZE;
  58. ipc_task->q_rpos = q_rpos;
  59. }
  60. }
  61. /* Add a message to the queue and trigger the ipc_task. */
  62. static int
  63. ipc_task_queue_add_task(struct iosm_imem *ipc_imem,
  64. int arg, void *msg,
  65. int (*func)(struct iosm_imem *ipc_imem, int arg,
  66. void *msg, size_t size),
  67. size_t size, bool is_copy, bool wait)
  68. {
  69. struct tasklet_struct *ipc_tasklet = ipc_imem->ipc_task->ipc_tasklet;
  70. struct ipc_task_queue *ipc_task = &ipc_imem->ipc_task->ipc_queue;
  71. struct completion completion;
  72. unsigned int pos, nextpos;
  73. unsigned long flags;
  74. int result = -EIO;
  75. init_completion(&completion);
  76. /* tasklet send may be called from both interrupt or thread
  77. * context, therefore protect queue operation by spinlock
  78. */
  79. spin_lock_irqsave(&ipc_task->q_lock, flags);
  80. pos = ipc_task->q_wpos;
  81. nextpos = (pos + 1) % IPC_THREAD_QUEUE_SIZE;
  82. /* Get next queue position. */
  83. if (nextpos != ipc_task->q_rpos) {
  84. /* Get the reference to the queue element and save the passed
  85. * values.
  86. */
  87. ipc_task->args[pos].arg = arg;
  88. ipc_task->args[pos].msg = msg;
  89. ipc_task->args[pos].func = func;
  90. ipc_task->args[pos].ipc_imem = ipc_imem;
  91. ipc_task->args[pos].size = size;
  92. ipc_task->args[pos].is_copy = is_copy;
  93. ipc_task->args[pos].completion = wait ? &completion : NULL;
  94. ipc_task->args[pos].response = -1;
  95. /* apply write barrier so that ipc_task->q_rpos elements
  96. * are updated before ipc_task->q_wpos is being updated.
  97. */
  98. smp_wmb();
  99. /* Update the status of the free queue space. */
  100. ipc_task->q_wpos = nextpos;
  101. result = 0;
  102. }
  103. spin_unlock_irqrestore(&ipc_task->q_lock, flags);
  104. if (result == 0) {
  105. tasklet_schedule(ipc_tasklet);
  106. if (wait) {
  107. wait_for_completion(&completion);
  108. result = ipc_task->args[pos].response;
  109. }
  110. } else {
  111. dev_err(ipc_imem->ipc_task->dev, "queue is full");
  112. }
  113. return result;
  114. }
  115. int ipc_task_queue_send_task(struct iosm_imem *imem,
  116. int (*func)(struct iosm_imem *ipc_imem, int arg,
  117. void *msg, size_t size),
  118. int arg, void *msg, size_t size, bool wait)
  119. {
  120. bool is_copy = false;
  121. void *copy = msg;
  122. int ret = -ENOMEM;
  123. if (size > 0) {
  124. copy = kmemdup(msg, size, GFP_ATOMIC);
  125. if (!copy)
  126. goto out;
  127. is_copy = true;
  128. }
  129. ret = ipc_task_queue_add_task(imem, arg, copy, func,
  130. size, is_copy, wait);
  131. if (ret < 0) {
  132. dev_err(imem->ipc_task->dev,
  133. "add task failed for %ps %d, %p, %zu, %d", func, arg,
  134. copy, size, is_copy);
  135. if (is_copy)
  136. kfree(copy);
  137. goto out;
  138. }
  139. ret = 0;
  140. out:
  141. return ret;
  142. }
  143. int ipc_task_init(struct ipc_task *ipc_task)
  144. {
  145. struct ipc_task_queue *ipc_queue = &ipc_task->ipc_queue;
  146. ipc_task->ipc_tasklet = kzalloc_obj(*ipc_task->ipc_tasklet);
  147. if (!ipc_task->ipc_tasklet)
  148. return -ENOMEM;
  149. /* Initialize the spinlock needed to protect the message queue of the
  150. * ipc_task
  151. */
  152. spin_lock_init(&ipc_queue->q_lock);
  153. tasklet_init(ipc_task->ipc_tasklet, ipc_task_queue_handler,
  154. (unsigned long)ipc_queue);
  155. return 0;
  156. }
  157. void ipc_task_deinit(struct ipc_task *ipc_task)
  158. {
  159. tasklet_kill(ipc_task->ipc_tasklet);
  160. kfree(ipc_task->ipc_tasklet);
  161. /* This will free/complete any outstanding messages,
  162. * without calling the actual handler
  163. */
  164. ipc_task_queue_cleanup(&ipc_task->ipc_queue);
  165. }