Provide a kfence_init() function for use for embedding the kfence into a parent structure. kfence_init() takes an optional function pointer argument should the caller wish to be notified when the kfence is complete. This is useful for allowing the kfences to drive other state machinery. Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> Cc: Sumit Semwal <sumit.semwal@xxxxxxxxxx> Cc: Shuah Khan <shuahkh@xxxxxxxxxxxxxxx> Cc: Tejun Heo <tj@xxxxxxxxxx> Cc: Daniel Vetter <daniel.vetter@xxxxxxxx> Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: Kees Cook <keescook@xxxxxxxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: "Paul E. McKenney" <paulmck@xxxxxxxxxxxxxxxxxx> Cc: Dan Williams <dan.j.williams@xxxxxxxxx> Cc: Andrey Ryabinin <aryabinin@xxxxxxxxxxxxx> Cc: Davidlohr Bueso <dave@xxxxxxxxxxxx> Cc: Nikolay Aleksandrov <nikolay@xxxxxxxxxxxxxxxxxxx> Cc: "David S. Miller" <davem@xxxxxxxxxxxxx> Cc: "Peter Zijlstra (Intel)" <peterz@xxxxxxxxxxxxx> Cc: Rasmus Villemoes <linux@xxxxxxxxxxxxxxxxxx> Cc: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> Cc: Dmitry Vyukov <dvyukov@xxxxxxxxxx> Cc: Alexander Potapenko <glider@xxxxxxxxxx> Cc: linux-kernel@xxxxxxxxxxxxxxx Cc: linux-media@xxxxxxxxxxxxxxx Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx Cc: linaro-mm-sig@xxxxxxxxxxxxxxxx --- include/linux/kfence.h | 5 ++++ kernel/async.c | 52 ++++++++++++++++++++++++++++++++++++---- lib/test-kfence.c | 64 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 109 insertions(+), 12 deletions(-) diff --git a/include/linux/kfence.h b/include/linux/kfence.h index 82eba8aacd02..82096bfafaa1 100644 --- a/include/linux/kfence.h +++ b/include/linux/kfence.h @@ -38,4 +38,9 @@ static inline bool kfence_complete(struct kfence *fence) } extern void kfence_put(struct kfence *fence); +typedef void (*kfence_notify_t)(struct kfence *); +#define __kfence_call __attribute__((aligned(4))) + +extern void kfence_init(struct kfence *fence, kfence_notify_t fn); + #endif /* _KFENCE_H_ */ diff --git a/kernel/async.c b/kernel/async.c index d0bcb7cc4884..db22b890711e 100644 --- a/kernel/async.c +++ b/kernel/async.c @@ -134,7 +134,17 @@ static atomic_t entry_count; * * The fence starts off pending a single signal. Once you have finished * setting up the fence, use kfence_signal() to allow it to wait upon - * its event sources. + * its event sources. To embed a kfence within another struct, use + * + * kfence_init() + * + * This can also be used to receive a callback when the kfence is completed + * (pending signal count hits 0). The callback is also called when the fence + * is released (the reference count hits 0, and the fence will be complete). + * Note that since the kfence is embedded inside another, its initial reference + * must not be dropped unless a callback is provided, or the kfence is the + * first member in the parent, and the parent was allocated by kmalloc (i.e. + * valid to be freed by kfree()). * * Use * @@ -151,7 +161,11 @@ static void kfence_free(struct kref *kref) WARN_ON(atomic_read(&fence->pending) > 0); - kfree(fence); + if (fence->flags) { + kfence_notify_t fn = (kfence_notify_t)fence->flags; + fn(fence); + } else + kfree(fence); } /** @@ -203,6 +217,11 @@ static void __kfence_signal(struct kfence *fence, if (!atomic_dec_and_test(&fence->pending)) return; + if (fence->flags) { + kfence_notify_t fn = (kfence_notify_t)fence->flags; + fn(fence); + } + atomic_dec(&fence->pending); __kfence_wake_up_all(fence, continuation); } @@ -240,7 +259,7 @@ void kfence_wait(struct kfence *fence) } EXPORT_SYMBOL_GPL(kfence_wait); -static void kfence_init(struct kfence *fence) +static void __kfence_init(struct kfence *fence) { init_waitqueue_head(&fence->wait); kref_init(&fence->kref); @@ -266,11 +285,36 @@ struct kfence *kfence_create(gfp_t gfp) if (!fence) return NULL; - kfence_init(fence); + __kfence_init(fence); return fence; } EXPORT_SYMBOL_GPL(kfence_create); +/** + * kfence_init - initialize a fence for use embedded within a struct + * @fence: this kfence + * @fn: a callback function for when the fence is complete, and when the + * fence is released + * + * This function initialises the @fence for use embedded within a parent + * structure. The optional @fn hook is called when the fence is completed + * (when all of its events sources have signaled their completion, i.e. + * pending signal count hits 0, fence_complete() however reports false as the + * fence is not considered complete until after the callback returns) and when + * the fence is subsequently released (the reference count hits 0, + * fence_complete() is then true). Note carefully that @fn will be called from + * atomic context. Also the lower 2 bits of the function pointer are used for + * storing flags, so the function must be aligned to at least 4 bytes - use + * __kfence_call as a function attribute to ensure correct alignment. + */ +void kfence_init(struct kfence *fence, kfence_notify_t fn) +{ + __kfence_init(fence); + BUG_ON((unsigned long)fn & KFENCE_CHECKED_BIT); + fence->flags = (unsigned long)fn; +} +EXPORT_SYMBOL_GPL(kfence_init); + static int kfence_wake(wait_queue_t *wq, unsigned mode, int flags, void *key) { list_del(&wq->task_list); diff --git a/lib/test-kfence.c b/lib/test-kfence.c index 1d9e4a5a2184..0604a27f68b2 100644 --- a/lib/test-kfence.c +++ b/lib/test-kfence.c @@ -9,9 +9,27 @@ #include <linux/module.h> #include <linux/slab.h> +static int __init __test_self(struct kfence *fence) +{ + if (kfence_complete(fence)) + return -EINVAL; + + kfence_signal(fence); + + if (!kfence_complete(fence)) + return -EINVAL; + + kfence_wait(fence); + if (!kfence_complete(fence)) + return -EINVAL; + + return 0; +} + static int __init test_self(void) { struct kfence *fence; + int ret; /* Test kfence signaling and completion testing */ pr_debug("%s\n", __func__); @@ -20,19 +38,43 @@ static int __init test_self(void) if (!fence) return -ENOMEM; - if (kfence_complete(fence)) - return -EINVAL; + ret = __test_self(fence); - kfence_signal(fence); + kfence_put(fence); + return ret; +} - if (!kfence_complete(fence)) - return -EINVAL; +struct test_stack { + struct kfence fence; + bool seen; +}; - kfence_wait(fence); - if (!kfence_complete(fence)) +static void __init __kfence_call fence_callback(struct kfence *fence) +{ + struct test_stack *ts = container_of(fence, typeof(*ts), fence); + ts->seen = true; +} + +static int __init test_stack(void) +{ + struct test_stack ts; + int ret; + + /* Test kfence signaling and completion testing (on stack) */ + pr_debug("%s\n", __func__); + + ts.seen = false; + kfence_init(&ts.fence, fence_callback); + + ret = __test_self(&ts.fence); + if (ret < 0) + return ret; + + if (!ts.seen) { + pr_err("fence callback not executed\n"); return -EINVAL; + } - kfence_put(fence); return 0; } @@ -413,6 +455,12 @@ static int __init test_kfence_init(void) return ret; } + ret = test_stack(); + if (ret < 0) { + pr_err("stack failed\n"); + return ret; + } + ret = test_dag(); if (ret < 0) { pr_err("DAG checker failed\n"); -- 2.8.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html