comm.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include "comm.h"
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <internal/rc_check.h>
  6. #include <linux/refcount.h>
  7. #include <linux/zalloc.h>
  8. #include <tools/libc_compat.h> // reallocarray
  9. #include "rwsem.h"
  10. DECLARE_RC_STRUCT(comm_str) {
  11. refcount_t refcnt;
  12. char str[];
  13. };
  14. static struct comm_strs {
  15. struct rw_semaphore lock;
  16. struct comm_str **strs;
  17. int num_strs;
  18. int capacity;
  19. } _comm_strs;
  20. static void comm_strs__remove_if_last(struct comm_str *cs);
  21. static void comm_strs__init(void)
  22. NO_THREAD_SAFETY_ANALYSIS /* Inherently single threaded due to pthread_once. */
  23. {
  24. init_rwsem(&_comm_strs.lock);
  25. _comm_strs.capacity = 16;
  26. _comm_strs.num_strs = 0;
  27. _comm_strs.strs = calloc(16, sizeof(*_comm_strs.strs));
  28. }
  29. static struct comm_strs *comm_strs__get(void)
  30. {
  31. static pthread_once_t comm_strs_type_once = PTHREAD_ONCE_INIT;
  32. pthread_once(&comm_strs_type_once, comm_strs__init);
  33. return &_comm_strs;
  34. }
  35. static refcount_t *comm_str__refcnt(struct comm_str *cs)
  36. {
  37. return &RC_CHK_ACCESS(cs)->refcnt;
  38. }
  39. static const char *comm_str__str(const struct comm_str *cs)
  40. {
  41. return &RC_CHK_ACCESS(cs)->str[0];
  42. }
  43. static struct comm_str *comm_str__get(struct comm_str *cs)
  44. {
  45. struct comm_str *result;
  46. if (RC_CHK_GET(result, cs))
  47. refcount_inc_not_zero(comm_str__refcnt(cs));
  48. return result;
  49. }
  50. static void comm_str__put(struct comm_str *cs)
  51. {
  52. if (!cs)
  53. return;
  54. if (refcount_dec_and_test(comm_str__refcnt(cs))) {
  55. RC_CHK_FREE(cs);
  56. } else {
  57. if (refcount_read(comm_str__refcnt(cs)) == 1)
  58. comm_strs__remove_if_last(cs);
  59. RC_CHK_PUT(cs);
  60. }
  61. }
  62. static struct comm_str *comm_str__new(const char *str)
  63. {
  64. struct comm_str *result = NULL;
  65. RC_STRUCT(comm_str) *cs;
  66. cs = malloc(sizeof(*cs) + strlen(str) + 1);
  67. if (ADD_RC_CHK(result, cs)) {
  68. refcount_set(comm_str__refcnt(result), 1);
  69. strcpy(&cs->str[0], str);
  70. }
  71. return result;
  72. }
  73. static int comm_str__search(const void *_key, const void *_member)
  74. {
  75. const char *key = _key;
  76. const struct comm_str *member = *(const struct comm_str * const *)_member;
  77. return strcmp(key, comm_str__str(member));
  78. }
  79. static void comm_strs__remove_if_last(struct comm_str *cs)
  80. {
  81. struct comm_strs *comm_strs = comm_strs__get();
  82. down_write(&comm_strs->lock);
  83. /*
  84. * Are there only references from the array, if so remove the array
  85. * reference under the write lock so that we don't race with findnew.
  86. */
  87. if (refcount_read(comm_str__refcnt(cs)) == 1) {
  88. struct comm_str **entry;
  89. entry = bsearch(comm_str__str(cs), comm_strs->strs, comm_strs->num_strs,
  90. sizeof(struct comm_str *), comm_str__search);
  91. comm_str__put(*entry);
  92. for (int i = entry - comm_strs->strs; i < comm_strs->num_strs - 1; i++)
  93. comm_strs->strs[i] = comm_strs->strs[i + 1];
  94. comm_strs->num_strs--;
  95. }
  96. up_write(&comm_strs->lock);
  97. }
  98. static struct comm_str *__comm_strs__find(struct comm_strs *comm_strs, const char *str)
  99. SHARED_LOCKS_REQUIRED(comm_strs->lock)
  100. {
  101. struct comm_str **result;
  102. result = bsearch(str, comm_strs->strs, comm_strs->num_strs, sizeof(struct comm_str *),
  103. comm_str__search);
  104. if (!result)
  105. return NULL;
  106. return comm_str__get(*result);
  107. }
  108. static struct comm_str *comm_strs__findnew(const char *str)
  109. {
  110. struct comm_strs *comm_strs = comm_strs__get();
  111. struct comm_str *result;
  112. if (!comm_strs)
  113. return NULL;
  114. down_read(&comm_strs->lock);
  115. result = __comm_strs__find(comm_strs, str);
  116. up_read(&comm_strs->lock);
  117. if (result)
  118. return result;
  119. down_write(&comm_strs->lock);
  120. result = __comm_strs__find(comm_strs, str);
  121. if (!result) {
  122. if (comm_strs->num_strs == comm_strs->capacity) {
  123. struct comm_str **tmp;
  124. tmp = reallocarray(comm_strs->strs,
  125. comm_strs->capacity + 16,
  126. sizeof(*comm_strs->strs));
  127. if (!tmp) {
  128. up_write(&comm_strs->lock);
  129. return NULL;
  130. }
  131. comm_strs->strs = tmp;
  132. comm_strs->capacity += 16;
  133. }
  134. result = comm_str__new(str);
  135. if (result) {
  136. int low = 0, high = comm_strs->num_strs - 1;
  137. int insert = comm_strs->num_strs; /* Default to inserting at the end. */
  138. while (low <= high) {
  139. int mid = low + (high - low) / 2;
  140. int cmp = strcmp(comm_str__str(comm_strs->strs[mid]), str);
  141. if (cmp < 0) {
  142. low = mid + 1;
  143. } else {
  144. high = mid - 1;
  145. insert = mid;
  146. }
  147. }
  148. memmove(&comm_strs->strs[insert + 1], &comm_strs->strs[insert],
  149. (comm_strs->num_strs - insert) * sizeof(struct comm_str *));
  150. comm_strs->num_strs++;
  151. comm_strs->strs[insert] = result;
  152. }
  153. }
  154. up_write(&comm_strs->lock);
  155. return comm_str__get(result);
  156. }
  157. struct comm *comm__new(const char *str, u64 timestamp, bool exec)
  158. {
  159. struct comm *comm = zalloc(sizeof(*comm));
  160. if (!comm)
  161. return NULL;
  162. comm->start = timestamp;
  163. comm->exec = exec;
  164. comm->comm_str = comm_strs__findnew(str);
  165. if (!comm->comm_str) {
  166. free(comm);
  167. return NULL;
  168. }
  169. return comm;
  170. }
  171. int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec)
  172. {
  173. struct comm_str *new, *old = comm->comm_str;
  174. new = comm_strs__findnew(str);
  175. if (!new)
  176. return -ENOMEM;
  177. comm_str__put(old);
  178. comm->comm_str = new;
  179. comm->start = timestamp;
  180. if (exec)
  181. comm->exec = true;
  182. return 0;
  183. }
  184. void comm__free(struct comm *comm)
  185. {
  186. comm_str__put(comm->comm_str);
  187. free(comm);
  188. }
  189. const char *comm__str(const struct comm *comm)
  190. {
  191. return comm_str__str(comm->comm_str);
  192. }