From a1cee437d6c91e534f9a6ee32664d3c00320a1d9 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratyev Date: Thu, 12 Nov 2020 18:45:27 +0300 Subject: [PATCH] Implement asynchronous free() call which can be called from any context where fast taskqueue are available. Use it in i915_sw_fence code to fix #9. --- drivers/gpu/drm/i915/i915_sw_fence.c | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index d5e460c81ff..a2bfb061ed5 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -9,6 +9,11 @@ #include #include +#ifndef __linux__ +#include /* __FreeBSD_version */ +#include +#endif + #include "i915_sw_fence.h" #include "i915_selftest.h" @@ -21,6 +26,21 @@ enum { DEBUG_FENCE_NOTIFY, }; +#if defined(__FreeBSD__) && __FreeBSD_version > 1300127 +static void linux_kfree_async(void *); +/* + * Critical section-friendly version of kfree(). + * Requires knowledge of the allocation size at build time. + */ +#define KFREE(addr) do { \ + BUILD_BUG_ON(sizeof(*(addr)) < sizeof(struct llist_node)); \ + if (curthread->td_critnest != 0) \ + linux_kfree_async(addr); \ + else \ + kfree(addr); \ +} while (0); +#endif + static void *i915_sw_fence_debug_hint(void *addr) { return (void *)(((struct i915_sw_fence *)addr)->flags & I915_SW_FENCE_MASK); @@ -384,7 +404,11 @@ static void dma_i915_sw_fence_wake(struct dma_fence *dma, i915_sw_fence_set_error_once(cb->fence, dma->error); i915_sw_fence_complete(cb->fence); +#if defined(__FreeBSD__) && __FreeBSD_version > 1300127 + KFREE(cb); +#else kfree(cb); +#endif } static void timer_i915_sw_fence_wake(struct timer_list *t) @@ -589,6 +613,27 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, #endif #if defined(__FreeBSD__) && __FreeBSD_version > 1300127 +static struct llist_head linux_kfree_async_list = LLIST_HEAD_INIT(); + +static void +linux_kfree_async_fn(struct irq_work *irqw) +{ + struct llist_node *freed; + + while((freed = llist_del_first(&linux_kfree_async_list)) != NULL) + kfree(freed); +} +static DEFINE_IRQ_WORK(linux_kfree_async_work, linux_kfree_async_fn); + +static void +linux_kfree_async(void *addr) +{ + if (addr == NULL) + return; + llist_add(addr, &linux_kfree_async_list); + irq_work_queue(&linux_kfree_async_work); +} + static void irq_work_ctx_init_fn(struct irq_work *irqw) {