// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2023 Red Hat */ #include "funnel-requestqueue.h" #include <linux/atomic.h> #include <linux/compiler.h> #include <linux/wait.h> #include "funnel-queue.h" #include "logger.h" #include "memory-alloc.h" #include "thread-utils.h" /* * This queue will attempt to handle requests in reasonably sized batches instead of reacting * immediately to each new request. The wait time between batches is dynamically adjusted up or * down to try to balance responsiveness against wasted thread run time. * * If the wait time becomes long enough, the queue will become dormant and must be explicitly * awoken when a new request is enqueued. The enqueue operation updates "newest" in the funnel * queue via xchg (which is a memory barrier), and later checks "dormant" to decide whether to do a * wakeup of the worker thread. * * When deciding to go to sleep, the worker thread sets "dormant" and then examines "newest" to * decide if the funnel queue is idle. In dormant mode, the last examination of "newest" before * going to sleep is done inside the wait_event_interruptible() macro, after a point where one or * more memory barriers have been issued. (Preparing to sleep uses spin locks.) Even if the funnel * queue's "next" field update isn't visible yet to make the entry accessible, its existence will * kick the worker thread out of dormant mode and back into timer-based mode. * * Unbatched requests are used to communicate between different zone threads and will also cause * the queue to awaken immediately. */ enum { … }; struct uds_request_queue { … }; static inline struct uds_request *poll_queues(struct uds_request_queue *queue) { … } static inline bool are_queues_idle(struct uds_request_queue *queue) { … } /* * Determine if there is a next request to process, and return it if there is. Also return flags * indicating whether the worker thread can sleep (for the use of wait_event() macros) and whether * the thread did sleep before returning a new request. */ static inline bool dequeue_request(struct uds_request_queue *queue, struct uds_request **request_ptr, bool *waited_ptr) { … } static void wait_for_request(struct uds_request_queue *queue, bool dormant, unsigned long timeout, struct uds_request **request, bool *waited) { … } static void request_queue_worker(void *arg) { … } int uds_make_request_queue(const char *queue_name, uds_request_queue_processor_fn processor, struct uds_request_queue **queue_ptr) { … } static inline void wake_up_worker(struct uds_request_queue *queue) { … } void uds_request_queue_enqueue(struct uds_request_queue *queue, struct uds_request *request) { … } void uds_request_queue_finish(struct uds_request_queue *queue) { … }