0426-vchiq_arm-Avoid-use-of-mutex-in-add_completion.patch 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. From 96d2e8f913ef4e62b93a2fd42412655643d24ad1 Mon Sep 17 00:00:00 2001
  2. From: Phil Elwell <phil@raspberrypi.org>
  3. Date: Mon, 20 Jun 2016 13:51:44 +0100
  4. Subject: [PATCH] vchiq_arm: Avoid use of mutex in add_completion
  5. Claiming the completion_mutex within add_completion did prevent some
  6. messages appearing twice, but provokes a deadlock caused by vcsm using
  7. vchiq within a page fault handler.
  8. Revert the use of completion_mutex, and instead fix the original
  9. problem using more memory barriers.
  10. Signed-off-by: Phil Elwell <phil@raspberrypi.org>
  11. ---
  12. .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 55 +++++++++++-----------
  13. .../vc04_services/interface/vchiq_arm/vchiq_core.c | 14 ++++--
  14. 2 files changed, 37 insertions(+), 32 deletions(-)
  15. --- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
  16. +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c
  17. @@ -64,10 +64,10 @@
  18. #define VCHIQ_MINOR 0
  19. /* Some per-instance constants */
  20. -#define MAX_COMPLETIONS 16
  21. +#define MAX_COMPLETIONS 128
  22. #define MAX_SERVICES 64
  23. #define MAX_ELEMENTS 8
  24. -#define MSG_QUEUE_SIZE 64
  25. +#define MSG_QUEUE_SIZE 128
  26. #define KEEPALIVE_VER 1
  27. #define KEEPALIVE_VER_MIN KEEPALIVE_VER
  28. @@ -208,28 +208,24 @@ add_completion(VCHIQ_INSTANCE_T instance
  29. void *bulk_userdata)
  30. {
  31. VCHIQ_COMPLETION_DATA_T *completion;
  32. + int insert;
  33. DEBUG_INITIALISE(g_state.local)
  34. - mutex_lock(&instance->completion_mutex);
  35. -
  36. - while (instance->completion_insert ==
  37. - (instance->completion_remove + MAX_COMPLETIONS)) {
  38. + insert = instance->completion_insert;
  39. + while ((insert - instance->completion_remove) >= MAX_COMPLETIONS) {
  40. /* Out of space - wait for the client */
  41. DEBUG_TRACE(SERVICE_CALLBACK_LINE);
  42. vchiq_log_trace(vchiq_arm_log_level,
  43. "add_completion - completion queue full");
  44. DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT);
  45. - mutex_unlock(&instance->completion_mutex);
  46. if (down_interruptible(&instance->remove_event) != 0) {
  47. vchiq_log_info(vchiq_arm_log_level,
  48. "service_callback interrupted");
  49. return VCHIQ_RETRY;
  50. }
  51. - mutex_lock(&instance->completion_mutex);
  52. if (instance->closing) {
  53. - mutex_unlock(&instance->completion_mutex);
  54. vchiq_log_info(vchiq_arm_log_level,
  55. "service_callback closing");
  56. return VCHIQ_SUCCESS;
  57. @@ -237,9 +233,7 @@ add_completion(VCHIQ_INSTANCE_T instance
  58. DEBUG_TRACE(SERVICE_CALLBACK_LINE);
  59. }
  60. - completion =
  61. - &instance->completions[instance->completion_insert &
  62. - (MAX_COMPLETIONS - 1)];
  63. + completion = &instance->completions[insert & (MAX_COMPLETIONS - 1)];
  64. completion->header = header;
  65. completion->reason = reason;
  66. @@ -260,12 +254,9 @@ add_completion(VCHIQ_INSTANCE_T instance
  67. wmb();
  68. if (reason == VCHIQ_MESSAGE_AVAILABLE)
  69. - user_service->message_available_pos =
  70. - instance->completion_insert;
  71. -
  72. - instance->completion_insert++;
  73. + user_service->message_available_pos = insert;
  74. - mutex_unlock(&instance->completion_mutex);
  75. + instance->completion_insert = ++insert;
  76. up(&instance->insert_event);
  77. @@ -795,6 +786,7 @@ vchiq_ioctl(struct file *file, unsigned
  78. instance->completion_insert)
  79. && !instance->closing) {
  80. int rc;
  81. +
  82. DEBUG_TRACE(AWAIT_COMPLETION_LINE);
  83. mutex_unlock(&instance->completion_mutex);
  84. rc = down_interruptible(&instance->insert_event);
  85. @@ -809,24 +801,29 @@ vchiq_ioctl(struct file *file, unsigned
  86. }
  87. DEBUG_TRACE(AWAIT_COMPLETION_LINE);
  88. - /* A read memory barrier is needed to stop prefetch of a stale
  89. - ** completion record
  90. - */
  91. - rmb();
  92. -
  93. if (ret == 0) {
  94. int msgbufcount = args.msgbufcount;
  95. + int remove;
  96. +
  97. + remove = instance->completion_remove;
  98. +
  99. for (ret = 0; ret < args.count; ret++) {
  100. VCHIQ_COMPLETION_DATA_T *completion;
  101. VCHIQ_SERVICE_T *service;
  102. USER_SERVICE_T *user_service;
  103. VCHIQ_HEADER_T *header;
  104. - if (instance->completion_remove ==
  105. - instance->completion_insert)
  106. +
  107. + if (remove == instance->completion_insert)
  108. break;
  109. +
  110. completion = &instance->completions[
  111. - instance->completion_remove &
  112. - (MAX_COMPLETIONS - 1)];
  113. + remove & (MAX_COMPLETIONS - 1)];
  114. +
  115. +
  116. + /* A read memory barrier is needed to prevent
  117. + ** the prefetch of a stale completion record
  118. + */
  119. + rmb();
  120. service = completion->service_userdata;
  121. user_service = service->base.userdata;
  122. @@ -903,7 +900,11 @@ vchiq_ioctl(struct file *file, unsigned
  123. break;
  124. }
  125. - instance->completion_remove++;
  126. + /* Ensure that the above copy has completed
  127. + ** before advancing the remove pointer. */
  128. + mb();
  129. +
  130. + instance->completion_remove = ++remove;
  131. }
  132. if (msgbufcount != args.msgbufcount) {
  133. --- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c
  134. +++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c
  135. @@ -610,15 +610,15 @@ process_free_queue(VCHIQ_STATE_T *state)
  136. BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)];
  137. int slot_queue_available;
  138. - /* Use a read memory barrier to ensure that any state that may have
  139. - ** been modified by another thread is not masked by stale prefetched
  140. - ** values. */
  141. - rmb();
  142. -
  143. /* Find slots which have been freed by the other side, and return them
  144. ** to the available queue. */
  145. slot_queue_available = state->slot_queue_available;
  146. + /* Use a memory barrier to ensure that any state that may have been
  147. + ** modified by another thread is not masked by stale prefetched
  148. + ** values. */
  149. + mb();
  150. +
  151. while (slot_queue_available != local->slot_queue_recycle) {
  152. unsigned int pos;
  153. int slot_index = local->slot_queue[slot_queue_available++ &
  154. @@ -626,6 +626,8 @@ process_free_queue(VCHIQ_STATE_T *state)
  155. char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index);
  156. int data_found = 0;
  157. + rmb();
  158. +
  159. vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%x %x %x",
  160. state->id, slot_index, (unsigned int)data,
  161. local->slot_queue_recycle, slot_queue_available);
  162. @@ -741,6 +743,8 @@ process_free_queue(VCHIQ_STATE_T *state)
  163. up(&state->data_quota_event);
  164. }
  165. + mb();
  166. +
  167. state->slot_queue_available = slot_queue_available;
  168. up(&state->slot_available_event);
  169. }