funnel-workqueue.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2023 Red Hat
  4. */
  5. #include "funnel-workqueue.h"
  6. #include <linux/atomic.h>
  7. #include <linux/cache.h>
  8. #include <linux/completion.h>
  9. #include <linux/err.h>
  10. #include <linux/kthread.h>
  11. #include <linux/percpu.h>
  12. #include "funnel-queue.h"
  13. #include "logger.h"
  14. #include "memory-alloc.h"
  15. #include "numeric.h"
  16. #include "permassert.h"
  17. #include "string-utils.h"
  18. #include "completion.h"
  19. #include "status-codes.h"
  20. static DEFINE_PER_CPU(unsigned int, service_queue_rotor);
  21. /**
  22. * DOC: Work queue definition.
  23. *
  24. * There are two types of work queues: simple, with one worker thread, and round-robin, which uses
  25. * a group of the former to do the work, and assigns work to them in round-robin fashion (roughly).
  26. * Externally, both are represented via the same common sub-structure, though there's actually not
  27. * a great deal of overlap between the two types internally.
  28. */
  29. struct vdo_work_queue {
  30. /* Name of just the work queue (e.g., "cpuQ12") */
  31. char *name;
  32. bool round_robin_mode;
  33. struct vdo_thread *owner;
  34. /* Life cycle functions, etc */
  35. const struct vdo_work_queue_type *type;
  36. };
  37. struct simple_work_queue {
  38. struct vdo_work_queue common;
  39. struct funnel_queue *priority_lists[VDO_WORK_Q_MAX_PRIORITY + 1];
  40. void *private;
  41. /*
  42. * The fields above are unchanged after setup but often read, and are good candidates for
  43. * caching -- and if the max priority is 2, just fit in one x86-64 cache line if aligned.
  44. * The fields below are often modified as we sleep and wake, so we want a separate cache
  45. * line for performance.
  46. */
  47. /* Any (0 or 1) worker threads waiting for new work to do */
  48. wait_queue_head_t waiting_worker_threads ____cacheline_aligned;
  49. /* Hack to reduce wakeup calls if the worker thread is running */
  50. atomic_t idle;
  51. /* These are infrequently used so in terms of performance we don't care where they land. */
  52. struct task_struct *thread;
  53. /* Notify creator once worker has initialized */
  54. struct completion *started;
  55. };
  56. struct round_robin_work_queue {
  57. struct vdo_work_queue common;
  58. struct simple_work_queue **service_queues;
  59. unsigned int num_service_queues;
  60. };
  61. static inline struct simple_work_queue *as_simple_work_queue(struct vdo_work_queue *queue)
  62. {
  63. return ((queue == NULL) ?
  64. NULL : container_of(queue, struct simple_work_queue, common));
  65. }
  66. static inline struct round_robin_work_queue *as_round_robin_work_queue(struct vdo_work_queue *queue)
  67. {
  68. return ((queue == NULL) ?
  69. NULL :
  70. container_of(queue, struct round_robin_work_queue, common));
  71. }
  72. /* Processing normal completions. */
  73. /*
  74. * Dequeue and return the next waiting completion, if any.
  75. *
  76. * We scan the funnel queues from highest priority to lowest, once; there is therefore a race
  77. * condition where a high-priority completion can be enqueued followed by a lower-priority one, and
  78. * we'll grab the latter (but we'll catch the high-priority item on the next call). If strict
  79. * enforcement of priorities becomes necessary, this function will need fixing.
  80. */
  81. static struct vdo_completion *poll_for_completion(struct simple_work_queue *queue)
  82. {
  83. int i;
  84. for (i = queue->common.type->max_priority; i >= 0; i--) {
  85. struct funnel_queue_entry *link = vdo_funnel_queue_poll(queue->priority_lists[i]);
  86. if (link != NULL)
  87. return container_of(link, struct vdo_completion, work_queue_entry_link);
  88. }
  89. return NULL;
  90. }
  91. static void enqueue_work_queue_completion(struct simple_work_queue *queue,
  92. struct vdo_completion *completion)
  93. {
  94. VDO_ASSERT_LOG_ONLY(completion->my_queue == NULL,
  95. "completion %px (fn %px) to enqueue (%px) is not already queued (%px)",
  96. completion, completion->callback, queue, completion->my_queue);
  97. if (completion->priority == VDO_WORK_Q_DEFAULT_PRIORITY)
  98. completion->priority = queue->common.type->default_priority;
  99. if (VDO_ASSERT(completion->priority <= queue->common.type->max_priority,
  100. "priority is in range for queue") != VDO_SUCCESS)
  101. completion->priority = 0;
  102. completion->my_queue = &queue->common;
  103. /* Funnel queue handles the synchronization for the put. */
  104. vdo_funnel_queue_put(queue->priority_lists[completion->priority],
  105. &completion->work_queue_entry_link);
  106. /*
  107. * Due to how funnel queue synchronization is handled (just atomic operations), the
  108. * simplest safe implementation here would be to wake-up any waiting threads after
  109. * enqueueing each item. Even if the funnel queue is not empty at the time of adding an
  110. * item to the queue, the consumer thread may not see this since it is not guaranteed to
  111. * have the same view of the queue as a producer thread.
  112. *
  113. * However, the above is wasteful so instead we attempt to minimize the number of thread
  114. * wakeups. Using an idle flag, and careful ordering using memory barriers, we should be
  115. * able to determine when the worker thread might be asleep or going to sleep. We use
  116. * cmpxchg to try to take ownership (vs other producer threads) of the responsibility for
  117. * waking the worker thread, so multiple wakeups aren't tried at once.
  118. *
  119. * This was tuned for some x86 boxes that were handy; it's untested whether doing the read
  120. * first is any better or worse for other platforms, even other x86 configurations.
  121. */
  122. smp_mb();
  123. if ((atomic_read(&queue->idle) != 1) || (atomic_cmpxchg(&queue->idle, 1, 0) != 1))
  124. return;
  125. /* There's a maximum of one thread in this list. */
  126. wake_up(&queue->waiting_worker_threads);
  127. }
  128. static void run_start_hook(struct simple_work_queue *queue)
  129. {
  130. if (queue->common.type->start != NULL)
  131. queue->common.type->start(queue->private);
  132. }
  133. static void run_finish_hook(struct simple_work_queue *queue)
  134. {
  135. if (queue->common.type->finish != NULL)
  136. queue->common.type->finish(queue->private);
  137. }
  138. /*
  139. * Wait for the next completion to process, or until kthread_should_stop indicates that it's time
  140. * for us to shut down.
  141. *
  142. * If kthread_should_stop says it's time to stop but we have pending completions return a
  143. * completion.
  144. *
  145. * Also update statistics relating to scheduler interactions.
  146. */
  147. static struct vdo_completion *wait_for_next_completion(struct simple_work_queue *queue)
  148. {
  149. struct vdo_completion *completion;
  150. DEFINE_WAIT(wait);
  151. while (true) {
  152. prepare_to_wait(&queue->waiting_worker_threads, &wait,
  153. TASK_INTERRUPTIBLE);
  154. /*
  155. * Don't set the idle flag until a wakeup will not be lost.
  156. *
  157. * Force synchronization between setting the idle flag and checking the funnel
  158. * queue; the producer side will do them in the reverse order. (There's still a
  159. * race condition we've chosen to allow, because we've got a timeout below that
  160. * unwedges us if we hit it, but this may narrow the window a little.)
  161. */
  162. atomic_set(&queue->idle, 1);
  163. smp_mb(); /* store-load barrier between "idle" and funnel queue */
  164. completion = poll_for_completion(queue);
  165. if (completion != NULL)
  166. break;
  167. /*
  168. * We need to check for thread-stop after setting TASK_INTERRUPTIBLE state up
  169. * above. Otherwise, schedule() will put the thread to sleep and might miss a
  170. * wakeup from kthread_stop() call in vdo_finish_work_queue().
  171. */
  172. if (kthread_should_stop())
  173. break;
  174. schedule();
  175. /*
  176. * Most of the time when we wake, it should be because there's work to do. If it
  177. * was a spurious wakeup, continue looping.
  178. */
  179. completion = poll_for_completion(queue);
  180. if (completion != NULL)
  181. break;
  182. }
  183. finish_wait(&queue->waiting_worker_threads, &wait);
  184. atomic_set(&queue->idle, 0);
  185. return completion;
  186. }
  187. static void process_completion(struct simple_work_queue *queue,
  188. struct vdo_completion *completion)
  189. {
  190. if (VDO_ASSERT(completion->my_queue == &queue->common,
  191. "completion %px from queue %px marked as being in this queue (%px)",
  192. completion, queue, completion->my_queue) == VDO_SUCCESS)
  193. completion->my_queue = NULL;
  194. vdo_run_completion(completion);
  195. }
  196. static void service_work_queue(struct simple_work_queue *queue)
  197. {
  198. run_start_hook(queue);
  199. while (true) {
  200. struct vdo_completion *completion = poll_for_completion(queue);
  201. if (completion == NULL)
  202. completion = wait_for_next_completion(queue);
  203. if (completion == NULL) {
  204. /* No completions but kthread_should_stop() was triggered. */
  205. break;
  206. }
  207. process_completion(queue, completion);
  208. /*
  209. * Be friendly to a CPU that has other work to do, if the kernel has told us to.
  210. * This speeds up some performance tests; that "other work" might include other VDO
  211. * threads.
  212. */
  213. if (need_resched())
  214. cond_resched();
  215. }
  216. run_finish_hook(queue);
  217. }
  218. static int work_queue_runner(void *ptr)
  219. {
  220. struct simple_work_queue *queue = ptr;
  221. complete(queue->started);
  222. service_work_queue(queue);
  223. return 0;
  224. }
  225. /* Creation & teardown */
  226. static void free_simple_work_queue(struct simple_work_queue *queue)
  227. {
  228. unsigned int i;
  229. for (i = 0; i <= VDO_WORK_Q_MAX_PRIORITY; i++)
  230. vdo_free_funnel_queue(queue->priority_lists[i]);
  231. vdo_free(queue->common.name);
  232. vdo_free(queue);
  233. }
  234. static void free_round_robin_work_queue(struct round_robin_work_queue *queue)
  235. {
  236. struct simple_work_queue **queue_table = queue->service_queues;
  237. unsigned int count = queue->num_service_queues;
  238. unsigned int i;
  239. queue->service_queues = NULL;
  240. for (i = 0; i < count; i++)
  241. free_simple_work_queue(queue_table[i]);
  242. vdo_free(queue_table);
  243. vdo_free(queue->common.name);
  244. vdo_free(queue);
  245. }
  246. void vdo_free_work_queue(struct vdo_work_queue *queue)
  247. {
  248. if (queue == NULL)
  249. return;
  250. vdo_finish_work_queue(queue);
  251. if (queue->round_robin_mode)
  252. free_round_robin_work_queue(as_round_robin_work_queue(queue));
  253. else
  254. free_simple_work_queue(as_simple_work_queue(queue));
  255. }
  256. static int make_simple_work_queue(const char *thread_name_prefix, const char *name,
  257. struct vdo_thread *owner, void *private,
  258. const struct vdo_work_queue_type *type,
  259. struct simple_work_queue **queue_ptr)
  260. {
  261. DECLARE_COMPLETION_ONSTACK(started);
  262. struct simple_work_queue *queue;
  263. int i;
  264. struct task_struct *thread = NULL;
  265. int result;
  266. VDO_ASSERT_LOG_ONLY((type->max_priority <= VDO_WORK_Q_MAX_PRIORITY),
  267. "queue priority count %u within limit %u", type->max_priority,
  268. VDO_WORK_Q_MAX_PRIORITY);
  269. result = vdo_allocate(1, struct simple_work_queue, "simple work queue", &queue);
  270. if (result != VDO_SUCCESS)
  271. return result;
  272. queue->private = private;
  273. queue->started = &started;
  274. queue->common.type = type;
  275. queue->common.owner = owner;
  276. init_waitqueue_head(&queue->waiting_worker_threads);
  277. result = vdo_duplicate_string(name, "queue name", &queue->common.name);
  278. if (result != VDO_SUCCESS) {
  279. vdo_free(queue);
  280. return -ENOMEM;
  281. }
  282. for (i = 0; i <= type->max_priority; i++) {
  283. result = vdo_make_funnel_queue(&queue->priority_lists[i]);
  284. if (result != VDO_SUCCESS) {
  285. free_simple_work_queue(queue);
  286. return result;
  287. }
  288. }
  289. thread = kthread_run(work_queue_runner, queue, "%s:%s", thread_name_prefix,
  290. queue->common.name);
  291. if (IS_ERR(thread)) {
  292. free_simple_work_queue(queue);
  293. return (int) PTR_ERR(thread);
  294. }
  295. queue->thread = thread;
  296. /*
  297. * If we don't wait to ensure the thread is running VDO code, a quick kthread_stop (due to
  298. * errors elsewhere) could cause it to never get as far as running VDO, skipping the
  299. * cleanup code.
  300. *
  301. * Eventually we should just make that path safe too, and then we won't need this
  302. * synchronization.
  303. */
  304. wait_for_completion(&started);
  305. *queue_ptr = queue;
  306. return VDO_SUCCESS;
  307. }
  308. /**
  309. * vdo_make_work_queue() - Create a work queue; if multiple threads are requested, completions will
  310. * be distributed to them in round-robin fashion.
  311. *
  312. * Each queue is associated with a struct vdo_thread which has a single vdo thread id. Regardless
  313. * of the actual number of queues and threads allocated here, code outside of the queue
  314. * implementation will treat this as a single zone.
  315. */
  316. int vdo_make_work_queue(const char *thread_name_prefix, const char *name,
  317. struct vdo_thread *owner, const struct vdo_work_queue_type *type,
  318. unsigned int thread_count, void *thread_privates[],
  319. struct vdo_work_queue **queue_ptr)
  320. {
  321. struct round_robin_work_queue *queue;
  322. int result;
  323. char thread_name[TASK_COMM_LEN];
  324. unsigned int i;
  325. if (thread_count == 1) {
  326. struct simple_work_queue *simple_queue;
  327. void *context = ((thread_privates != NULL) ? thread_privates[0] : NULL);
  328. result = make_simple_work_queue(thread_name_prefix, name, owner, context,
  329. type, &simple_queue);
  330. if (result == VDO_SUCCESS)
  331. *queue_ptr = &simple_queue->common;
  332. return result;
  333. }
  334. result = vdo_allocate(1, struct round_robin_work_queue, "round-robin work queue",
  335. &queue);
  336. if (result != VDO_SUCCESS)
  337. return result;
  338. result = vdo_allocate(thread_count, struct simple_work_queue *,
  339. "subordinate work queues", &queue->service_queues);
  340. if (result != VDO_SUCCESS) {
  341. vdo_free(queue);
  342. return result;
  343. }
  344. queue->num_service_queues = thread_count;
  345. queue->common.round_robin_mode = true;
  346. queue->common.owner = owner;
  347. result = vdo_duplicate_string(name, "queue name", &queue->common.name);
  348. if (result != VDO_SUCCESS) {
  349. vdo_free(queue->service_queues);
  350. vdo_free(queue);
  351. return -ENOMEM;
  352. }
  353. *queue_ptr = &queue->common;
  354. for (i = 0; i < thread_count; i++) {
  355. void *context = ((thread_privates != NULL) ? thread_privates[i] : NULL);
  356. snprintf(thread_name, sizeof(thread_name), "%s%u", name, i);
  357. result = make_simple_work_queue(thread_name_prefix, thread_name, owner,
  358. context, type, &queue->service_queues[i]);
  359. if (result != VDO_SUCCESS) {
  360. queue->num_service_queues = i;
  361. /* Destroy previously created subordinates. */
  362. vdo_free_work_queue(vdo_forget(*queue_ptr));
  363. return result;
  364. }
  365. }
  366. return VDO_SUCCESS;
  367. }
  368. static void finish_simple_work_queue(struct simple_work_queue *queue)
  369. {
  370. if (queue->thread == NULL)
  371. return;
  372. /* Tells the worker thread to shut down and waits for it to exit. */
  373. kthread_stop(queue->thread);
  374. queue->thread = NULL;
  375. }
  376. static void finish_round_robin_work_queue(struct round_robin_work_queue *queue)
  377. {
  378. struct simple_work_queue **queue_table = queue->service_queues;
  379. unsigned int count = queue->num_service_queues;
  380. unsigned int i;
  381. for (i = 0; i < count; i++)
  382. finish_simple_work_queue(queue_table[i]);
  383. }
  384. /* No enqueueing of completions should be done once this function is called. */
  385. void vdo_finish_work_queue(struct vdo_work_queue *queue)
  386. {
  387. if (queue == NULL)
  388. return;
  389. if (queue->round_robin_mode)
  390. finish_round_robin_work_queue(as_round_robin_work_queue(queue));
  391. else
  392. finish_simple_work_queue(as_simple_work_queue(queue));
  393. }
  394. /* Debugging dumps */
  395. static void dump_simple_work_queue(struct simple_work_queue *queue)
  396. {
  397. const char *thread_status = "no threads";
  398. char task_state_report = '-';
  399. if (queue->thread != NULL) {
  400. task_state_report = task_state_to_char(queue->thread);
  401. thread_status = atomic_read(&queue->idle) ? "idle" : "running";
  402. }
  403. vdo_log_info("workQ %px (%s) %s (%c)", &queue->common, queue->common.name,
  404. thread_status, task_state_report);
  405. /* ->waiting_worker_threads wait queue status? anyone waiting? */
  406. }
  407. /*
  408. * Write to the buffer some info about the completion, for logging. Since the common use case is
  409. * dumping info about a lot of completions to syslog all at once, the format favors brevity over
  410. * readability.
  411. */
  412. void vdo_dump_work_queue(struct vdo_work_queue *queue)
  413. {
  414. if (queue->round_robin_mode) {
  415. struct round_robin_work_queue *round_robin = as_round_robin_work_queue(queue);
  416. unsigned int i;
  417. for (i = 0; i < round_robin->num_service_queues; i++)
  418. dump_simple_work_queue(round_robin->service_queues[i]);
  419. } else {
  420. dump_simple_work_queue(as_simple_work_queue(queue));
  421. }
  422. }
  423. static void get_function_name(void *pointer, char *buffer, size_t buffer_length)
  424. {
  425. if (pointer == NULL) {
  426. /*
  427. * Format "%ps" logs a null pointer as "(null)" with a bunch of leading spaces. We
  428. * sometimes use this when logging lots of data; don't be so verbose.
  429. */
  430. strscpy(buffer, "-", buffer_length);
  431. } else {
  432. /*
  433. * Use a pragma to defeat gcc's format checking, which doesn't understand that
  434. * "%ps" actually does support a precision spec in Linux kernel code.
  435. */
  436. char *space;
  437. #pragma GCC diagnostic push
  438. #pragma GCC diagnostic ignored "-Wformat"
  439. snprintf(buffer, buffer_length, "%.*ps", buffer_length - 1, pointer);
  440. #pragma GCC diagnostic pop
  441. space = strchr(buffer, ' ');
  442. if (space != NULL)
  443. *space = '\0';
  444. }
  445. }
  446. void vdo_dump_completion_to_buffer(struct vdo_completion *completion, char *buffer,
  447. size_t length)
  448. {
  449. size_t current_length =
  450. scnprintf(buffer, length, "%.*s/", TASK_COMM_LEN,
  451. (completion->my_queue == NULL ? "-" : completion->my_queue->name));
  452. if (current_length < length - 1) {
  453. get_function_name((void *) completion->callback, buffer + current_length,
  454. length - current_length);
  455. }
  456. }
  457. /* Completion submission */
  458. /*
  459. * If the completion has a timeout that has already passed, the timeout handler function may be
  460. * invoked by this function.
  461. */
  462. void vdo_enqueue_work_queue(struct vdo_work_queue *queue,
  463. struct vdo_completion *completion)
  464. {
  465. /*
  466. * Convert the provided generic vdo_work_queue to the simple_work_queue to actually queue
  467. * on.
  468. */
  469. struct simple_work_queue *simple_queue = NULL;
  470. if (!queue->round_robin_mode) {
  471. simple_queue = as_simple_work_queue(queue);
  472. } else {
  473. struct round_robin_work_queue *round_robin = as_round_robin_work_queue(queue);
  474. /*
  475. * It shouldn't be a big deal if the same rotor gets used for multiple work queues.
  476. * Any patterns that might develop are likely to be disrupted by random ordering of
  477. * multiple completions and migration between cores, unless the load is so light as
  478. * to be regular in ordering of tasks and the threads are confined to individual
  479. * cores; with a load that light we won't care.
  480. */
  481. unsigned int rotor = this_cpu_inc_return(service_queue_rotor);
  482. unsigned int index = rotor % round_robin->num_service_queues;
  483. simple_queue = round_robin->service_queues[index];
  484. }
  485. enqueue_work_queue_completion(simple_queue, completion);
  486. }
  487. /* Misc */
  488. /*
  489. * Return the work queue pointer recorded at initialization time in the work-queue stack handle
  490. * initialized on the stack of the current thread, if any.
  491. */
  492. static struct simple_work_queue *get_current_thread_work_queue(void)
  493. {
  494. /*
  495. * In interrupt context, if a vdo thread is what got interrupted, the calls below will find
  496. * the queue for the thread which was interrupted. However, the interrupted thread may have
  497. * been processing a completion, in which case starting to process another would violate
  498. * our concurrency assumptions.
  499. */
  500. if (in_interrupt())
  501. return NULL;
  502. if (kthread_func(current) != work_queue_runner)
  503. /* Not a VDO work queue thread. */
  504. return NULL;
  505. return kthread_data(current);
  506. }
  507. struct vdo_work_queue *vdo_get_current_work_queue(void)
  508. {
  509. struct simple_work_queue *queue = get_current_thread_work_queue();
  510. return (queue == NULL) ? NULL : &queue->common;
  511. }
  512. struct vdo_thread *vdo_get_work_queue_owner(struct vdo_work_queue *queue)
  513. {
  514. return queue->owner;
  515. }
  516. /**
  517. * vdo_get_work_queue_private_data() - Returns the private data for the current thread's work
  518. * queue, or NULL if none or if the current thread is not a
  519. * work queue thread.
  520. */
  521. void *vdo_get_work_queue_private_data(void)
  522. {
  523. struct simple_work_queue *queue = get_current_thread_work_queue();
  524. return (queue != NULL) ? queue->private : NULL;
  525. }
  526. bool vdo_work_queue_type_is(struct vdo_work_queue *queue,
  527. const struct vdo_work_queue_type *type)
  528. {
  529. return (queue->type == type);
  530. }