From f4cdf7ca9a1fdcca413157df19753f388a5a224e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 19 Aug 2026 10:09:22 -0700 Subject: Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull media updates from Mauro Carvalho Chehab: - v4l2-core: added ISP statistics support and per-block validation - v4l2-core: Allow unknown HDR10 white point and luminance - New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor - New codec: Milos: VPU v2.0 codec support - isp driver: gained support for Dreamchip RPPX1 ISP framework - vsp1 driver: gained support for RZ/T2H and RZ/N2H - Novalake driver: gained CVS support for new NVL hardware - dvb-core: fix feed leak on failed DMX_ADD_PID - several driver fixes, cleanups and minor improvements * tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits) media: ipu-bridge: check all DMI entries when overriding sensor rotation media: v4l2-async: avoid deleting unlinked ASC entry on link error media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement media: intel/ipu6: fix async notifier cleanup leak on parse error media: staging/ipu7: fix async notifier UAF on probe error path media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem() media: rcar-isp: Fix VSPX reference leaks media: rcar-isp: Release ISPCORE resources media: i2c: imx415: Release runtime PM reference on VBLANK error media: i2c: imx415: Return test pattern write errors media: renesas: vsp1: Declare index variables in for loop statement media: renesas: vsp1: Make reset control optional to support platforms without a reset line media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs media: nxp: imx8-isi: Add additional 32-bit RGB format support media: nxp: imx8-isi: Add 16-bit raw Bayer format support media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks media: nxp: imx8-isi: Correct color map between V4L2 and ISI ... --- drivers/md/dm-vdo/wait-queue.c | 205 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 drivers/md/dm-vdo/wait-queue.c (limited to 'drivers/md/dm-vdo/wait-queue.c') diff --git a/drivers/md/dm-vdo/wait-queue.c b/drivers/md/dm-vdo/wait-queue.c new file mode 100644 index 000000000..f81ed0cee --- /dev/null +++ b/drivers/md/dm-vdo/wait-queue.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2023 Red Hat + */ + +#include "wait-queue.h" + +#include + +#include "permassert.h" + +#include "status-codes.h" + +/** + * vdo_waitq_enqueue_waiter() - Add a waiter to the tail end of a waitq. + * @waitq: The vdo_wait_queue to which to add the waiter. + * @waiter: The waiter to add to the waitq. + * + * The waiter must not already be waiting in a waitq. + */ +void vdo_waitq_enqueue_waiter(struct vdo_wait_queue *waitq, struct vdo_waiter *waiter) +{ + BUG_ON(waiter->next_waiter != NULL); + + if (waitq->last_waiter == NULL) { + /* + * The waitq is empty, so form the initial circular list by self-linking the + * initial waiter. + */ + waiter->next_waiter = waiter; + } else { + /* Splice the new waiter in at the end of the waitq. */ + waiter->next_waiter = waitq->last_waiter->next_waiter; + waitq->last_waiter->next_waiter = waiter; + } + + /* In both cases, the waiter we added to the list becomes the last waiter. */ + waitq->last_waiter = waiter; + waitq->length += 1; +} + +/** + * vdo_waitq_transfer_all_waiters() - Transfer all waiters from one waitq to + * a second waitq, emptying the first waitq. + * @from_waitq: The waitq containing the waiters to move. + * @to_waitq: The waitq that will receive the waiters from the first waitq. + */ +void vdo_waitq_transfer_all_waiters(struct vdo_wait_queue *from_waitq, + struct vdo_wait_queue *to_waitq) +{ + /* If the source waitq is empty, there's nothing to do. */ + if (!vdo_waitq_has_waiters(from_waitq)) + return; + + if (vdo_waitq_has_waiters(to_waitq)) { + /* + * Both are non-empty. Splice the two circular lists together + * by swapping the next (head) pointers in the list tails. + */ + struct vdo_waiter *from_head = from_waitq->last_waiter->next_waiter; + struct vdo_waiter *to_head = to_waitq->last_waiter->next_waiter; + + to_waitq->last_waiter->next_waiter = from_head; + from_waitq->last_waiter->next_waiter = to_head; + } + + to_waitq->last_waiter = from_waitq->last_waiter; + to_waitq->length += from_waitq->length; + vdo_waitq_init(from_waitq); +} + +/** + * vdo_waitq_notify_all_waiters() - Notify all the entries waiting in a waitq. + * @waitq: The vdo_wait_queue containing the waiters to notify. + * @callback: The function to call to notify each waiter, or NULL to invoke the callback field + * registered in each waiter. + * @context: The context to pass to the callback function. + * + * Notifies all the entries waiting in a waitq to continue execution by invoking a callback + * function on each of them in turn. The waitq is copied and emptied before invoking any callbacks, + * and only the waiters that were in the waitq at the start of the call will be notified. + */ +void vdo_waitq_notify_all_waiters(struct vdo_wait_queue *waitq, + vdo_waiter_callback_fn callback, void *context) +{ + /* + * Copy and empty the waitq first, avoiding the possibility of an infinite + * loop if entries are returned to the waitq by the callback function. + */ + struct vdo_wait_queue waiters; + + vdo_waitq_init(&waiters); + vdo_waitq_transfer_all_waiters(waitq, &waiters); + + /* Drain the copied waitq, invoking the callback on every entry. */ + while (vdo_waitq_has_waiters(&waiters)) + vdo_waitq_notify_next_waiter(&waiters, callback, context); +} + +/** + * vdo_waitq_get_first_waiter() - Return the waiter that is at the head end of a waitq. + * @waitq: The vdo_wait_queue from which to get the first waiter. + * + * Return: The first (oldest) waiter in the waitq, or NULL if the waitq is empty. + */ +struct vdo_waiter *vdo_waitq_get_first_waiter(const struct vdo_wait_queue *waitq) +{ + struct vdo_waiter *last_waiter = waitq->last_waiter; + + if (last_waiter == NULL) { + /* There are no waiters, so we're done. */ + return NULL; + } + + /* The waitq is circular, so the last entry links to the head of the waitq. */ + return last_waiter->next_waiter; +} + +/** + * vdo_waitq_dequeue_matching_waiters() - Remove all waiters that match based on the specified + * matching method and append them to a vdo_wait_queue. + * @waitq: The vdo_wait_queue to process. + * @waiter_match: The method to determine matching. + * @match_context: Contextual info for the match method. + * @matched_waitq: A wait_waitq to store matches. + */ +void vdo_waitq_dequeue_matching_waiters(struct vdo_wait_queue *waitq, + vdo_waiter_match_fn waiter_match, + void *match_context, + struct vdo_wait_queue *matched_waitq) +{ + struct vdo_wait_queue iteration_waitq; + + vdo_waitq_init(&iteration_waitq); + vdo_waitq_transfer_all_waiters(waitq, &iteration_waitq); + + while (vdo_waitq_has_waiters(&iteration_waitq)) { + struct vdo_waiter *waiter = vdo_waitq_dequeue_waiter(&iteration_waitq); + + vdo_waitq_enqueue_waiter((waiter_match(waiter, match_context) ? + matched_waitq : waitq), waiter); + } +} + +/** + * vdo_waitq_dequeue_waiter() - Remove the first (oldest) waiter from a waitq. + * @waitq: The vdo_wait_queue from which to remove the first entry. + * + * The caller will be responsible for waking the waiter by continuing its + * execution appropriately. + * + * Return: The first (oldest) waiter in the waitq, or NULL if the waitq is empty. + */ +struct vdo_waiter *vdo_waitq_dequeue_waiter(struct vdo_wait_queue *waitq) +{ + struct vdo_waiter *first_waiter = vdo_waitq_get_first_waiter(waitq); + struct vdo_waiter *last_waiter = waitq->last_waiter; + + if (first_waiter == NULL) + return NULL; + + if (first_waiter == last_waiter) { + /* The waitq has a single entry, so empty it by nulling the tail. */ + waitq->last_waiter = NULL; + } else { + /* + * The waitq has multiple waiters, so splice the first waiter out + * of the circular waitq. + */ + last_waiter->next_waiter = first_waiter->next_waiter; + } + + /* The waiter is no longer in a waitq. */ + first_waiter->next_waiter = NULL; + waitq->length -= 1; + + return first_waiter; +} + +/** + * vdo_waitq_notify_next_waiter() - Notify the next entry waiting in a waitq. + * @waitq: The vdo_wait_queue containing the waiter to notify. + * @callback: The function to call to notify the waiter, or NULL to invoke the callback field + * registered in the waiter. + * @context: The context to pass to the callback function. + * + * Notifies the next entry waiting in a waitq to continue execution by invoking a callback function + * on it after removing it from the waitq. + * + * Return: true if there was a waiter in the waitq. + */ +bool vdo_waitq_notify_next_waiter(struct vdo_wait_queue *waitq, + vdo_waiter_callback_fn callback, void *context) +{ + struct vdo_waiter *waiter = vdo_waitq_dequeue_waiter(waitq); + + if (waiter == NULL) + return false; + + if (callback == NULL) + callback = waiter->callback; + callback(waiter, context); + + return true; +} -- cgit v1.3.1