2
0

thread-context.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*
  2. * QEMU Thread Context
  3. *
  4. * Copyright Red Hat Inc., 2022
  5. *
  6. * Authors:
  7. * David Hildenbrand <david@redhat.com>
  8. *
  9. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10. * See the COPYING file in the top-level directory.
  11. */
  12. #include "qemu/osdep.h"
  13. #include "qemu/thread-context.h"
  14. #include "qapi/error.h"
  15. #include "qapi/qapi-builtin-visit.h"
  16. #include "qapi/visitor.h"
  17. #include "qemu/config-file.h"
  18. #include "qapi/qapi-builtin-visit.h"
  19. #include "qom/object_interfaces.h"
  20. #include "qemu/module.h"
  21. #include "qemu/bitmap.h"
  22. #ifdef CONFIG_NUMA
  23. #include <numa.h>
  24. #endif
  25. enum {
  26. TC_CMD_NONE = 0,
  27. TC_CMD_STOP,
  28. TC_CMD_NEW,
  29. };
  30. typedef struct ThreadContextCmdNew {
  31. QemuThread *thread;
  32. const char *name;
  33. void *(*start_routine)(void *);
  34. void *arg;
  35. int mode;
  36. } ThreadContextCmdNew;
  37. static void *thread_context_run(void *opaque)
  38. {
  39. ThreadContext *tc = opaque;
  40. tc->thread_id = qemu_get_thread_id();
  41. qemu_sem_post(&tc->sem);
  42. while (true) {
  43. /*
  44. * Threads inherit the CPU affinity of the creating thread. For this
  45. * reason, we create new (especially short-lived) threads from our
  46. * persistent context thread.
  47. *
  48. * Especially when QEMU is not allowed to set the affinity itself,
  49. * management tools can simply set the affinity of the context thread
  50. * after creating the context, to have new threads created via
  51. * the context inherit the CPU affinity automatically.
  52. */
  53. switch (tc->thread_cmd) {
  54. case TC_CMD_NONE:
  55. break;
  56. case TC_CMD_STOP:
  57. tc->thread_cmd = TC_CMD_NONE;
  58. qemu_sem_post(&tc->sem);
  59. return NULL;
  60. case TC_CMD_NEW: {
  61. ThreadContextCmdNew *cmd_new = tc->thread_cmd_data;
  62. qemu_thread_create(cmd_new->thread, cmd_new->name,
  63. cmd_new->start_routine, cmd_new->arg,
  64. cmd_new->mode);
  65. tc->thread_cmd = TC_CMD_NONE;
  66. tc->thread_cmd_data = NULL;
  67. qemu_sem_post(&tc->sem);
  68. break;
  69. }
  70. default:
  71. g_assert_not_reached();
  72. }
  73. qemu_sem_wait(&tc->sem_thread);
  74. }
  75. }
  76. static void thread_context_set_cpu_affinity(Object *obj, Visitor *v,
  77. const char *name, void *opaque,
  78. Error **errp)
  79. {
  80. ThreadContext *tc = THREAD_CONTEXT(obj);
  81. uint16List *l, *host_cpus = NULL;
  82. unsigned long *bitmap = NULL;
  83. int nbits = 0, ret;
  84. if (tc->init_cpu_bitmap) {
  85. error_setg(errp, "Mixing CPU and node affinity not supported");
  86. return;
  87. }
  88. if (!visit_type_uint16List(v, name, &host_cpus, errp)) {
  89. return;
  90. }
  91. if (!host_cpus) {
  92. error_setg(errp, "CPU list is empty");
  93. goto out;
  94. }
  95. for (l = host_cpus; l; l = l->next) {
  96. nbits = MAX(nbits, l->value + 1);
  97. }
  98. bitmap = bitmap_new(nbits);
  99. for (l = host_cpus; l; l = l->next) {
  100. set_bit(l->value, bitmap);
  101. }
  102. if (tc->thread_id != -1) {
  103. /*
  104. * Note: we won't be adjusting the affinity of any thread that is still
  105. * around, but only the affinity of the context thread.
  106. */
  107. ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
  108. if (ret) {
  109. error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
  110. }
  111. } else {
  112. tc->init_cpu_bitmap = bitmap;
  113. bitmap = NULL;
  114. tc->init_cpu_nbits = nbits;
  115. }
  116. out:
  117. g_free(bitmap);
  118. qapi_free_uint16List(host_cpus);
  119. }
  120. static void thread_context_get_cpu_affinity(Object *obj, Visitor *v,
  121. const char *name, void *opaque,
  122. Error **errp)
  123. {
  124. unsigned long *bitmap, nbits, value;
  125. ThreadContext *tc = THREAD_CONTEXT(obj);
  126. uint16List *host_cpus = NULL;
  127. uint16List **tail = &host_cpus;
  128. int ret;
  129. if (tc->thread_id == -1) {
  130. error_setg(errp, "Object not initialized yet");
  131. return;
  132. }
  133. ret = qemu_thread_get_affinity(&tc->thread, &bitmap, &nbits);
  134. if (ret) {
  135. error_setg(errp, "Getting CPU affinity failed: %s", strerror(ret));
  136. return;
  137. }
  138. value = find_first_bit(bitmap, nbits);
  139. while (value < nbits) {
  140. QAPI_LIST_APPEND(tail, value);
  141. value = find_next_bit(bitmap, nbits, value + 1);
  142. }
  143. g_free(bitmap);
  144. visit_type_uint16List(v, name, &host_cpus, errp);
  145. qapi_free_uint16List(host_cpus);
  146. }
  147. static void thread_context_set_node_affinity(Object *obj, Visitor *v,
  148. const char *name, void *opaque,
  149. Error **errp)
  150. {
  151. #ifdef CONFIG_NUMA
  152. const int nbits = numa_num_possible_cpus();
  153. ThreadContext *tc = THREAD_CONTEXT(obj);
  154. uint16List *l, *host_nodes = NULL;
  155. unsigned long *bitmap = NULL;
  156. struct bitmask *tmp_cpus;
  157. int ret, i;
  158. if (tc->init_cpu_bitmap) {
  159. error_setg(errp, "Mixing CPU and node affinity not supported");
  160. return;
  161. }
  162. if (!visit_type_uint16List(v, name, &host_nodes, errp)) {
  163. return;
  164. }
  165. if (!host_nodes) {
  166. error_setg(errp, "Node list is empty");
  167. goto out;
  168. }
  169. bitmap = bitmap_new(nbits);
  170. tmp_cpus = numa_allocate_cpumask();
  171. for (l = host_nodes; l; l = l->next) {
  172. numa_bitmask_clearall(tmp_cpus);
  173. ret = numa_node_to_cpus(l->value, tmp_cpus);
  174. if (ret) {
  175. /* We ignore any errors, such as impossible nodes. */
  176. continue;
  177. }
  178. for (i = 0; i < nbits; i++) {
  179. if (numa_bitmask_isbitset(tmp_cpus, i)) {
  180. set_bit(i, bitmap);
  181. }
  182. }
  183. }
  184. numa_free_cpumask(tmp_cpus);
  185. if (bitmap_empty(bitmap, nbits)) {
  186. error_setg(errp, "The nodes select no CPUs");
  187. goto out;
  188. }
  189. if (tc->thread_id != -1) {
  190. /*
  191. * Note: we won't be adjusting the affinity of any thread that is still
  192. * around for now, but only the affinity of the context thread.
  193. */
  194. ret = qemu_thread_set_affinity(&tc->thread, bitmap, nbits);
  195. if (ret) {
  196. error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
  197. }
  198. } else {
  199. tc->init_cpu_bitmap = bitmap;
  200. bitmap = NULL;
  201. tc->init_cpu_nbits = nbits;
  202. }
  203. out:
  204. g_free(bitmap);
  205. qapi_free_uint16List(host_nodes);
  206. #else
  207. error_setg(errp, "NUMA node affinity is not supported by this QEMU");
  208. #endif
  209. }
  210. static void thread_context_get_thread_id(Object *obj, Visitor *v,
  211. const char *name, void *opaque,
  212. Error **errp)
  213. {
  214. ThreadContext *tc = THREAD_CONTEXT(obj);
  215. uint64_t value = tc->thread_id;
  216. visit_type_uint64(v, name, &value, errp);
  217. }
  218. static void thread_context_instance_complete(UserCreatable *uc, Error **errp)
  219. {
  220. ThreadContext *tc = THREAD_CONTEXT(uc);
  221. char *thread_name;
  222. int ret;
  223. thread_name = g_strdup_printf("TC %s",
  224. object_get_canonical_path_component(OBJECT(uc)));
  225. qemu_thread_create(&tc->thread, thread_name, thread_context_run, tc,
  226. QEMU_THREAD_JOINABLE);
  227. g_free(thread_name);
  228. /* Wait until initialization of the thread is done. */
  229. while (tc->thread_id == -1) {
  230. qemu_sem_wait(&tc->sem);
  231. }
  232. if (tc->init_cpu_bitmap) {
  233. ret = qemu_thread_set_affinity(&tc->thread, tc->init_cpu_bitmap,
  234. tc->init_cpu_nbits);
  235. if (ret) {
  236. error_setg(errp, "Setting CPU affinity failed: %s", strerror(ret));
  237. }
  238. g_free(tc->init_cpu_bitmap);
  239. tc->init_cpu_bitmap = NULL;
  240. }
  241. }
  242. static void thread_context_class_init(ObjectClass *oc, void *data)
  243. {
  244. UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
  245. ucc->complete = thread_context_instance_complete;
  246. object_class_property_add(oc, "thread-id", "int",
  247. thread_context_get_thread_id, NULL, NULL,
  248. NULL);
  249. object_class_property_add(oc, "cpu-affinity", "int",
  250. thread_context_get_cpu_affinity,
  251. thread_context_set_cpu_affinity, NULL, NULL);
  252. object_class_property_add(oc, "node-affinity", "int", NULL,
  253. thread_context_set_node_affinity, NULL, NULL);
  254. }
  255. static void thread_context_instance_init(Object *obj)
  256. {
  257. ThreadContext *tc = THREAD_CONTEXT(obj);
  258. tc->thread_id = -1;
  259. qemu_sem_init(&tc->sem, 0);
  260. qemu_sem_init(&tc->sem_thread, 0);
  261. qemu_mutex_init(&tc->mutex);
  262. }
  263. static void thread_context_instance_finalize(Object *obj)
  264. {
  265. ThreadContext *tc = THREAD_CONTEXT(obj);
  266. if (tc->thread_id != -1) {
  267. tc->thread_cmd = TC_CMD_STOP;
  268. qemu_sem_post(&tc->sem_thread);
  269. qemu_thread_join(&tc->thread);
  270. }
  271. qemu_sem_destroy(&tc->sem);
  272. qemu_sem_destroy(&tc->sem_thread);
  273. qemu_mutex_destroy(&tc->mutex);
  274. }
  275. static const TypeInfo thread_context_info = {
  276. .name = TYPE_THREAD_CONTEXT,
  277. .parent = TYPE_OBJECT,
  278. .class_init = thread_context_class_init,
  279. .instance_size = sizeof(ThreadContext),
  280. .instance_init = thread_context_instance_init,
  281. .instance_finalize = thread_context_instance_finalize,
  282. .interfaces = (InterfaceInfo[]) {
  283. { TYPE_USER_CREATABLE },
  284. { }
  285. }
  286. };
  287. static void thread_context_register_types(void)
  288. {
  289. type_register_static(&thread_context_info);
  290. }
  291. type_init(thread_context_register_types)
  292. void thread_context_create_thread(ThreadContext *tc, QemuThread *thread,
  293. const char *name,
  294. void *(*start_routine)(void *), void *arg,
  295. int mode)
  296. {
  297. ThreadContextCmdNew data = {
  298. .thread = thread,
  299. .name = name,
  300. .start_routine = start_routine,
  301. .arg = arg,
  302. .mode = mode,
  303. };
  304. qemu_mutex_lock(&tc->mutex);
  305. tc->thread_cmd = TC_CMD_NEW;
  306. tc->thread_cmd_data = &data;
  307. qemu_sem_post(&tc->sem_thread);
  308. while (tc->thread_cmd != TC_CMD_NONE) {
  309. qemu_sem_wait(&tc->sem);
  310. }
  311. qemu_mutex_unlock(&tc->mutex);
  312. }