From: Lingfeng Yang Date: Wed, 1 Feb 2017 07:28:39 +0000 (-0800) Subject: ANDROID: goldfish_sync: upgrade to new fence sync api X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=d1c4322ea1ffee43ac3616563d65d32927f62594;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git ANDROID: goldfish_sync: upgrade to new fence sync api goldfish_sync requires the following modifications to bring it up: - Copy and integrate goldfish_sync version of sw_sync, from out of dma-buf driver. - Don't delete timelines by itself; rely on put Change-Id: Ie88d506955dbf5c8532281f122471dc7b1c0bccb Signed-off-by: Lingfeng Yang --- diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig index c579141a7bed..d293bbc22c79 100644 --- a/drivers/staging/goldfish/Kconfig +++ b/drivers/staging/goldfish/Kconfig @@ -7,6 +7,8 @@ config GOLDFISH_AUDIO config GOLDFISH_SYNC tristate "Goldfish AVD Sync Driver" depends on GOLDFISH + depends on SW_SYNC + depends on SYNC_FILE ---help--- Emulated sync fences for the Goldfish Android Virtual Device diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile index 0cf525588210..fbebfb7c781c 100644 --- a/drivers/staging/goldfish/Makefile +++ b/drivers/staging/goldfish/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o # and sync ccflags-y := -Idrivers/staging/android +obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync_timeline_fence.o obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o diff --git a/drivers/staging/goldfish/goldfish_sync.c b/drivers/staging/goldfish/goldfish_sync.c index ba8def29901e..aeccec1c51b1 100644 --- a/drivers/staging/goldfish/goldfish_sync.c +++ b/drivers/staging/goldfish/goldfish_sync.c @@ -30,10 +30,13 @@ #include #include + +#include #include +#include +#include -#include "sw_sync.h" -#include "sync.h" +#include "goldfish_sync_timeline_fence.h" #define ERR(...) printk(KERN_ERR __VA_ARGS__); @@ -45,13 +48,13 @@ /* The Goldfish sync driver is designed to provide a interface * between the underlying host's sync device and the kernel's - * sw_sync. + * fence sync framework.. * The purpose of the device/driver is to enable lightweight * creation and signaling of timelines and fences * in order to synchronize the guest with host-side graphics events. * * Each time the interrupt trips, the driver - * may perform a sw_sync operation. + * may perform a sync operation. */ /* The operations are: */ @@ -158,7 +161,7 @@ struct goldfish_sync_state { static struct goldfish_sync_state global_sync_state[1]; struct goldfish_sync_timeline_obj { - struct sw_sync_timeline *sw_sync_tl; + struct goldfish_sync_timeline *sync_tl; uint32_t current_time; /* We need to be careful about when we deallocate * this |goldfish_sync_timeline_obj| struct. @@ -166,10 +169,10 @@ struct goldfish_sync_timeline_obj { * consider the triggered host-side wait that may * still be in flight when the guest close()'s a * goldfish_sync device's sync context fd (and - * destroys the |sw_sync_tl| field above). + * destroys the |sync_tl| field above). * The host-side wait may raise IRQ * and tell the kernel to increment the timeline _after_ - * the |sw_sync_tl| has already been set to null. + * the |sync_tl| has already been set to null. * * From observations on OpenGL apps and CTS tests, this * happens at some very low probability upon context @@ -177,8 +180,8 @@ struct goldfish_sync_timeline_obj { * and it needs to be handled properly. Otherwise, * if we clean up the surrounding |goldfish_sync_timeline_obj| * too early, any |handle| field of any host->guest command - * might not even point to a null |sw_sync_tl| field, - * but to garbage memory or even a reclaimed |sw_sync_tl|. + * might not even point to a null |sync_tl| field, + * but to garbage memory or even a reclaimed |sync_tl|. * If we do not count such "pending waits" and kfree the object * immediately upon |goldfish_sync_timeline_destroy|, * we might get mysterous RCU stalls after running a long @@ -220,14 +223,14 @@ struct goldfish_sync_timeline_obj { }; /* We will call |delete_timeline_obj| when the last reference count - * of the kref is decremented. This deletes the sw_sync + * of the kref is decremented. This deletes the sync * timeline object along with the wrapper itself. */ static void delete_timeline_obj(struct kref* kref) { struct goldfish_sync_timeline_obj* obj = container_of(kref, struct goldfish_sync_timeline_obj, kref); - sync_timeline_destroy(&obj->sw_sync_tl->obj); - obj->sw_sync_tl = NULL; + goldfish_sync_timeline_put_internal(obj->sync_tl); + obj->sync_tl = NULL; kfree(obj); } @@ -245,21 +248,21 @@ goldfish_sync_timeline_create(void) { char timeline_name[256]; - struct sw_sync_timeline *res_sync_tl = NULL; + struct goldfish_sync_timeline *res_sync_tl = NULL; struct goldfish_sync_timeline_obj *res; DTRACE(); gensym(timeline_name); - res_sync_tl = sw_sync_timeline_create(timeline_name); + res_sync_tl = goldfish_sync_timeline_create_internal(timeline_name); if (!res_sync_tl) { - ERR("Failed to create sw_sync timeline."); + ERR("Failed to create goldfish_sw_sync timeline."); return NULL; } res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL); - res->sw_sync_tl = res_sync_tl; + res->sync_tl = res_sync_tl; res->current_time = 0; kref_init(&res->kref); @@ -277,19 +280,20 @@ goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj, int fd; char fence_name[256]; struct sync_pt *syncpt = NULL; - struct sync_fence *sync_obj = NULL; - struct sw_sync_timeline *tl; + struct sync_file *sync_file_obj = NULL; + struct goldfish_sync_timeline *tl; DTRACE(); if (!obj) return -1; - tl = obj->sw_sync_tl; + tl = obj->sync_tl; - syncpt = sw_sync_pt_create(tl, val); + syncpt = goldfish_sync_pt_create_internal( + tl, sizeof(struct sync_pt) + 4, val); if (!syncpt) { ERR("could not create sync point! " - "sync_timeline=0x%p val=%d", + "goldfish_sync_timeline=0x%p val=%d", tl, val); return -1; } @@ -303,24 +307,26 @@ goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj, gensym(fence_name); - sync_obj = sync_fence_create(fence_name, syncpt); - if (!sync_obj) { + sync_file_obj = sync_file_create(&syncpt->base); + if (!sync_file_obj) { ERR("could not create sync fence! " - "sync_timeline=0x%p val=%d sync_pt=0x%p", + "goldfish_sync_timeline=0x%p val=%d sync_pt=0x%p", tl, val, syncpt); goto err_cleanup_fd_pt; } - DPRINT("installing sync fence into fd %d sync_obj=0x%p", fd, sync_obj); - sync_fence_install(sync_obj, fd); + DPRINT("installing sync fence into fd %d sync_file_obj=0x%p", + fd, sync_file_obj); + fd_install(fd, sync_file_obj->file); kref_get(&obj->kref); return fd; err_cleanup_fd_pt: + fput(sync_file_obj->file); put_unused_fd(fd); err_cleanup_pt: - sync_pt_free(syncpt); + fence_put(&syncpt->base); return -1; } @@ -335,7 +341,7 @@ goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc) if (!obj) return; DPRINT("timeline_obj=0x%p", obj); - sw_sync_timeline_inc(obj->sw_sync_tl, inc); + goldfish_sync_timeline_signal_internal(obj->sync_tl, inc); DPRINT("incremented timeline. increment max_time"); obj->current_time += inc; @@ -847,7 +853,8 @@ int goldfish_sync_probe(struct platform_device *pdev) return -ENODEV; } - sync_state->reg_base = devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE); + sync_state->reg_base = + devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE); if (sync_state->reg_base == NULL) { ERR("Could not ioremap"); return -ENOMEM; @@ -880,9 +887,11 @@ int goldfish_sync_probe(struct platform_device *pdev) struct goldfish_sync_hostcmd *batch_addr_hostcmd; struct goldfish_sync_guestcmd *batch_addr_guestcmd; - batch_addr_hostcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd), + batch_addr_hostcmd = + devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd), GFP_KERNEL); - batch_addr_guestcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd), + batch_addr_guestcmd = + devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd), GFP_KERNEL); if (!setup_verify_batch_cmd_addr(sync_state, @@ -952,36 +961,3 @@ MODULE_AUTHOR("Google, Inc."); MODULE_DESCRIPTION("Android QEMU Sync Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION("1.0"); - -/* This function is only to run a basic test of sync framework. - * It creates a timeline and fence object whose signal point is at 1. - * The timeline is incremented, and we use the sync framework's - * sync_fence_wait on that fence object. If everything works out, - * we should not hang in the wait and return immediately. - * There is no way to explicitly run this test yet, but it - * can be used by inserting it at the end of goldfish_sync_probe. - */ -void test_kernel_sync(void) -{ - struct goldfish_sync_timeline_obj *test_timeline; - int test_fence_fd; - - DTRACE(); - - DPRINT("test sw_sync"); - - test_timeline = goldfish_sync_timeline_create(); - DPRINT("sw_sync_timeline_create -> 0x%p", test_timeline); - - test_fence_fd = goldfish_sync_fence_create(test_timeline, 1); - DPRINT("sync_fence_create -> %d", test_fence_fd); - - DPRINT("incrementing test timeline"); - goldfish_sync_timeline_inc(test_timeline, 1); - - DPRINT("test waiting (should NOT hang)"); - sync_fence_wait( - sync_fence_fdget(test_fence_fd), -1); - - DPRINT("test waiting (afterward)"); -} diff --git a/drivers/staging/goldfish/goldfish_sync_timeline_fence.c b/drivers/staging/goldfish/goldfish_sync_timeline_fence.c new file mode 100644 index 000000000000..e671618cf888 --- /dev/null +++ b/drivers/staging/goldfish/goldfish_sync_timeline_fence.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include + +#include "goldfish_sync_timeline_fence.h" + +/* + * Timeline-based sync for Goldfish Sync + * Based on "Sync File validation framework" + * (drivers/dma-buf/sw_sync.c) + * + * Copyright (C) 2017 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/** + * struct goldfish_sync_timeline - sync object + * @kref: reference count on fence. + * @name: name of the goldfish_sync_timeline. Useful for debugging + * @child_list_head: list of children sync_pts for this goldfish_sync_timeline + * @child_list_lock: lock protecting @child_list_head and fence.status + * @active_list_head: list of active (unsignaled/errored) sync_pts + */ +struct goldfish_sync_timeline { + struct kref kref; + char name[32]; + + /* protected by child_list_lock */ + u64 context; + int value; + + struct list_head child_list_head; + spinlock_t child_list_lock; + + struct list_head active_list_head; +}; + +static inline struct goldfish_sync_timeline *fence_parent(struct fence *fence) +{ + return container_of(fence->lock, struct goldfish_sync_timeline, + child_list_lock); +} + +static const struct fence_ops goldfish_sync_timeline_fence_ops; + +static inline struct sync_pt *goldfish_sync_fence_to_sync_pt(struct fence *fence) +{ + if (fence->ops != &goldfish_sync_timeline_fence_ops) + return NULL; + return container_of(fence, struct sync_pt, base); +} + +/** + * goldfish_sync_timeline_create_internal() - creates a sync object + * @name: sync_timeline name + * + * Creates a new sync_timeline. Returns the sync_timeline object or NULL in + * case of error. + */ +struct goldfish_sync_timeline +*goldfish_sync_timeline_create_internal(const char *name) +{ + struct goldfish_sync_timeline *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return NULL; + + kref_init(&obj->kref); + obj->context = fence_context_alloc(1); + strlcpy(obj->name, name, sizeof(obj->name)); + + INIT_LIST_HEAD(&obj->child_list_head); + INIT_LIST_HEAD(&obj->active_list_head); + spin_lock_init(&obj->child_list_lock); + + return obj; +} + +static void goldfish_sync_timeline_free_internal(struct kref *kref) +{ + struct goldfish_sync_timeline *obj = + container_of(kref, struct goldfish_sync_timeline, kref); + + kfree(obj); +} + +static void goldfish_sync_timeline_get_internal( + struct goldfish_sync_timeline *obj) +{ + kref_get(&obj->kref); +} + +void goldfish_sync_timeline_put_internal(struct goldfish_sync_timeline *obj) +{ + kref_put(&obj->kref, goldfish_sync_timeline_free_internal); +} + +/** + * goldfish_sync_timeline_signal() - + * signal a status change on a goldfish_sync_timeline + * @obj: sync_timeline to signal + * @inc: num to increment on timeline->value + * + * A sync implementation should call this any time one of it's fences + * has signaled or has an error condition. + */ +void goldfish_sync_timeline_signal_internal(struct goldfish_sync_timeline *obj, + unsigned int inc) +{ + unsigned long flags; + struct sync_pt *pt, *next; + + spin_lock_irqsave(&obj->child_list_lock, flags); + + obj->value += inc; + + list_for_each_entry_safe(pt, next, &obj->active_list_head, + active_list) { + if (fence_is_signaled_locked(&pt->base)) + list_del_init(&pt->active_list); + } + + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +/** + * goldfish_sync_pt_create_internal() - creates a sync pt + * @parent: fence's parent sync_timeline + * @size: size to allocate for this pt + * @inc: value of the fence + * + * Creates a new sync_pt as a child of @parent. @size bytes will be + * allocated allowing for implementation specific data to be kept after + * the generic sync_timeline struct. Returns the sync_pt object or + * NULL in case of error. + */ +struct sync_pt *goldfish_sync_pt_create_internal( + struct goldfish_sync_timeline *obj, int size, + unsigned int value) +{ + unsigned long flags; + struct sync_pt *pt; + + if (size < sizeof(*pt)) + return NULL; + + pt = kzalloc(size, GFP_KERNEL); + if (!pt) + return NULL; + + spin_lock_irqsave(&obj->child_list_lock, flags); + goldfish_sync_timeline_get_internal(obj); + fence_init(&pt->base, &goldfish_sync_timeline_fence_ops, &obj->child_list_lock, + obj->context, value); + list_add_tail(&pt->child_list, &obj->child_list_head); + INIT_LIST_HEAD(&pt->active_list); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + return pt; +} + +static const char *goldfish_sync_timeline_fence_get_driver_name( + struct fence *fence) +{ + return "sw_sync"; +} + +static const char *goldfish_sync_timeline_fence_get_timeline_name( + struct fence *fence) +{ + struct goldfish_sync_timeline *parent = fence_parent(fence); + + return parent->name; +} + +static void goldfish_sync_timeline_fence_release(struct fence *fence) +{ + struct sync_pt *pt = goldfish_sync_fence_to_sync_pt(fence); + struct goldfish_sync_timeline *parent = fence_parent(fence); + unsigned long flags; + + spin_lock_irqsave(fence->lock, flags); + list_del(&pt->child_list); + if (!list_empty(&pt->active_list)) + list_del(&pt->active_list); + spin_unlock_irqrestore(fence->lock, flags); + + goldfish_sync_timeline_put_internal(parent); + fence_free(fence); +} + +static bool goldfish_sync_timeline_fence_signaled(struct fence *fence) +{ + struct goldfish_sync_timeline *parent = fence_parent(fence); + + return (fence->seqno > parent->value) ? false : true; +} + +static bool goldfish_sync_timeline_fence_enable_signaling(struct fence *fence) +{ + struct sync_pt *pt = goldfish_sync_fence_to_sync_pt(fence); + struct goldfish_sync_timeline *parent = fence_parent(fence); + + if (goldfish_sync_timeline_fence_signaled(fence)) + return false; + + list_add_tail(&pt->active_list, &parent->active_list_head); + return true; +} + +static void goldfish_sync_timeline_fence_disable_signaling(struct fence *fence) +{ + struct sync_pt *pt = container_of(fence, struct sync_pt, base); + + list_del_init(&pt->active_list); +} + +static void goldfish_sync_timeline_fence_value_str(struct fence *fence, + char *str, int size) +{ + snprintf(str, size, "%d", fence->seqno); +} + +static void goldfish_sync_timeline_fence_timeline_value_str( + struct fence *fence, + char *str, int size) +{ + struct goldfish_sync_timeline *parent = fence_parent(fence); + + snprintf(str, size, "%d", parent->value); +} + +static const struct fence_ops goldfish_sync_timeline_fence_ops = { + .get_driver_name = goldfish_sync_timeline_fence_get_driver_name, + .get_timeline_name = goldfish_sync_timeline_fence_get_timeline_name, + .enable_signaling = goldfish_sync_timeline_fence_enable_signaling, + .disable_signaling = goldfish_sync_timeline_fence_disable_signaling, + .signaled = goldfish_sync_timeline_fence_signaled, + .wait = fence_default_wait, + .release = goldfish_sync_timeline_fence_release, + .fence_value_str = goldfish_sync_timeline_fence_value_str, + .timeline_value_str = goldfish_sync_timeline_fence_timeline_value_str, +}; diff --git a/drivers/staging/goldfish/goldfish_sync_timeline_fence.h b/drivers/staging/goldfish/goldfish_sync_timeline_fence.h new file mode 100644 index 000000000000..fc25924652c1 --- /dev/null +++ b/drivers/staging/goldfish/goldfish_sync_timeline_fence.h @@ -0,0 +1,58 @@ +#include +#include + +/** + * struct sync_pt - sync_pt object + * @base: base fence object + * @child_list: sync timeline child's list + * @active_list: sync timeline active child's list + */ +struct sync_pt { + struct fence base; + struct list_head child_list; + struct list_head active_list; +}; + +/** + * goldfish_sync_timeline_create_internal() - creates a sync object + * @name: goldfish_sync_timeline name + * + * Creates a new goldfish_sync_timeline. + * Returns the goldfish_sync_timeline object or NULL in case of error. + */ +struct goldfish_sync_timeline +*goldfish_sync_timeline_create_internal(const char *name); + +/** + * goldfish_sync_pt_create_internal() - creates a sync pt + * @parent: fence's parent goldfish_sync_timeline + * @size: size to allocate for this pt + * @inc: value of the fence + * + * Creates a new sync_pt as a child of @parent. @size bytes will be + * allocated allowing for implementation specific data to be kept after + * the generic sync_timeline struct. Returns the sync_pt object or + * NULL in case of error. + */ +struct sync_pt +*goldfish_sync_pt_create_internal(struct goldfish_sync_timeline *obj, + int size, unsigned int value); + +/** + * goldfish_sync_timeline_signal_internal() - + * signal a status change on a sync_timeline + * @obj: goldfish_sync_timeline to signal + * @inc: num to increment on timeline->value + * + * A sync implementation should call this any time one of it's fences + * has signaled or has an error condition. + */ +void goldfish_sync_timeline_signal_internal(struct goldfish_sync_timeline *obj, + unsigned int inc); + +/** + * goldfish_sync_timeline_put_internal() - dec refcount of a sync_timeline + * and clean up memory if it was the last ref. + * @obj: goldfish_sync_timeline to decref + */ +void goldfish_sync_timeline_put_internal(struct goldfish_sync_timeline *obj);