From 2246de53c9726d9000438c28c35f906e94cf5758 Mon Sep 17 00:00:00 2001 From: Show Liu Date: Wed, 25 Dec 2013 14:46:34 +0800 Subject: Added ARM mali KDS driver support --- drivers/base/Kconfig | 12 + drivers/base/Makefile | 1 + drivers/base/kds/Kbuild | 17 ++ drivers/base/kds/Kconfig | 19 ++ drivers/base/kds/Makefile | 36 +++ drivers/base/kds/Module.symvers | 8 + drivers/base/kds/kds.c | 532 ++++++++++++++++++++++++++++++++++++++++ drivers/base/kds/sconscript | 72 ++++++ include/linux/kds.h | 171 +++++++++++++ 9 files changed, 868 insertions(+) create mode 100755 drivers/base/kds/Kbuild create mode 100755 drivers/base/kds/Kconfig create mode 100755 drivers/base/kds/Makefile create mode 100644 drivers/base/kds/Module.symvers create mode 100755 drivers/base/kds/kds.c create mode 100755 drivers/base/kds/sconscript create mode 100755 include/linux/kds.h diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 07abd9d76f7..c09a1ddba29 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -292,4 +292,16 @@ config CMA_AREAS endif +config DMA_SHARED_BUFFER_USES_KDS + bool "Add KDS resource within every dma_buf allocation" + depends on DMA_SHARED_BUFFER && KDS + default n + help + This option adds a KDS resource within every dma_buf allocation. + +config KDS + bool "Kernel dependency system" + help + This option enables the generic kernel dependency system + endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 4e22ce3ed73..435077321aa 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -11,6 +11,7 @@ obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o +obj-$(CONFIG_KDS) += kds/ obj-$(CONFIG_ISA) += isa.o obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o diff --git a/drivers/base/kds/Kbuild b/drivers/base/kds/Kbuild new file mode 100755 index 00000000000..74db4816472 --- /dev/null +++ b/drivers/base/kds/Kbuild @@ -0,0 +1,17 @@ +# +# (C) COPYRIGHT 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +obj-$(CONFIG_KDS) += kds.o +obj-$(CONFIG_KDS_TEST) += kds_test.o diff --git a/drivers/base/kds/Kconfig b/drivers/base/kds/Kconfig new file mode 100755 index 00000000000..3efdb5f464f --- /dev/null +++ b/drivers/base/kds/Kconfig @@ -0,0 +1,19 @@ +# +# (C) COPYRIGHT 2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +config KDS + tristate "Kernel dependency system" + help + This option enables the generic kernel dependency system diff --git a/drivers/base/kds/Makefile b/drivers/base/kds/Makefile new file mode 100755 index 00000000000..03d2626df23 --- /dev/null +++ b/drivers/base/kds/Makefile @@ -0,0 +1,36 @@ +# +# (C) COPYRIGHT 2011-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +# linux build system bootstrap for out-of-tree module + +# default to building for the host +ARCH ?= $(shell uname -m) +CONFIG_KDS_TEST ?= n + +ifeq ($(KDIR),) +$(error Must specify KDIR to point to the kernel to target)) +endif + +all: kds + +kds: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" CONFIG_KDS=m + +kds_test: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" CONFIG_KDS_TEST=m + +clean: + $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean + diff --git a/drivers/base/kds/Module.symvers b/drivers/base/kds/Module.symvers new file mode 100644 index 00000000000..7eba467f4fa --- /dev/null +++ b/drivers/base/kds/Module.symvers @@ -0,0 +1,8 @@ +0x00000000 kds_waitall /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_resource_set_release_sync /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_resource_term /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_async_waitall /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_callback_init /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_callback_term /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_resource_init /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL +0x00000000 kds_resource_set_release /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL diff --git a/drivers/base/kds/kds.c b/drivers/base/kds/kds.c new file mode 100755 index 00000000000..aada2da646a --- /dev/null +++ b/drivers/base/kds/kds.c @@ -0,0 +1,532 @@ +/* + * + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define KDS_LINK_TRIGGERED (1u << 0) +#define KDS_LINK_EXCLUSIVE (1u << 1) + +#define KDS_INVALID (void *)-2 +#define KDS_RESOURCE (void *)-1 + +struct kds_resource_set +{ + unsigned long num_resources; + unsigned long pending; + struct kds_callback *cb; + void *callback_parameter; + void *callback_extra_parameter; + struct list_head callback_link; + struct work_struct callback_work; + + /* This is only initted when kds_waitall() is called. */ + wait_queue_head_t wake; + + struct kds_link resources[0]; +}; + +static DEFINE_SPINLOCK(kds_lock); + +int kds_callback_init(struct kds_callback *cb, int direct, kds_callback_fn user_cb) +{ + int ret = 0; + + cb->direct = direct; + cb->user_cb = user_cb; + + if (!direct) + { + cb->wq = alloc_workqueue("kds", WQ_UNBOUND | WQ_HIGHPRI, WQ_UNBOUND_MAX_ACTIVE); + if (!cb->wq) + ret = -ENOMEM; + } + else + { + cb->wq = NULL; + } + + return ret; +} +EXPORT_SYMBOL(kds_callback_init); + +void kds_callback_term(struct kds_callback *cb) +{ + if (!cb->direct) + { + BUG_ON(!cb->wq); + destroy_workqueue(cb->wq); + } + else + { + BUG_ON(cb->wq); + } +} + +EXPORT_SYMBOL(kds_callback_term); + +static void kds_do_user_callback(struct kds_resource_set *rset) +{ + rset->cb->user_cb(rset->callback_parameter, rset->callback_extra_parameter); +} + +static void kds_queued_callback(struct work_struct *work) +{ + struct kds_resource_set *rset; + rset = container_of(work, struct kds_resource_set, callback_work); + + kds_do_user_callback(rset); +} + +static void kds_callback_perform(struct kds_resource_set *rset) +{ + if (rset->cb->direct) + kds_do_user_callback(rset); + else + { + int result; + result = queue_work(rset->cb->wq, &rset->callback_work); + /* if we got a 0 return it means we've triggered the same rset twice! */ + WARN_ON(!result); + } +} + +void kds_resource_init(struct kds_resource * const res) +{ + BUG_ON(!res); + INIT_LIST_HEAD(&res->waiters.link); + res->waiters.parent = KDS_RESOURCE; +} +EXPORT_SYMBOL(kds_resource_init); + +int kds_resource_term(struct kds_resource *res) +{ + unsigned long lflags; + BUG_ON(!res); + spin_lock_irqsave(&kds_lock, lflags); + if (!list_empty(&res->waiters.link)) + { + spin_unlock_irqrestore(&kds_lock, lflags); + printk(KERN_ERR "ERROR: KDS resource is still in use\n"); + return -EBUSY; + } + res->waiters.parent = KDS_INVALID; + spin_unlock_irqrestore(&kds_lock, lflags); + return 0; +} +EXPORT_SYMBOL(kds_resource_term); + +int kds_async_waitall( + struct kds_resource_set ** const pprset, + struct kds_callback *cb, + void *callback_parameter, + void *callback_extra_parameter, + int number_resources, + unsigned long *exclusive_access_bitmap, + struct kds_resource **resource_list) +{ + struct kds_resource_set *rset = NULL; + unsigned long lflags; + int i; + int triggered; + int err = -EFAULT; + + BUG_ON(!pprset); + BUG_ON(!resource_list); + BUG_ON(!cb); + + WARN_ONCE(number_resources > 10, "Waiting on a high numbers of resources may increase latency, see documentation."); + + rset = kmalloc(sizeof(*rset) + number_resources * sizeof(struct kds_link), GFP_KERNEL); + if (!rset) + { + return -ENOMEM; + } + + rset->num_resources = number_resources; + rset->pending = number_resources; + rset->cb = cb; + rset->callback_parameter = callback_parameter; + rset->callback_extra_parameter = callback_extra_parameter; + INIT_LIST_HEAD(&rset->callback_link); + INIT_WORK(&rset->callback_work, kds_queued_callback); + + for (i = 0; i < number_resources; i++) + { + INIT_LIST_HEAD(&rset->resources[i].link); + rset->resources[i].parent = rset; + } + + spin_lock_irqsave(&kds_lock, lflags); + + for (i = 0; i < number_resources; i++) + { + unsigned long link_state = 0; + + if (test_bit(i, exclusive_access_bitmap)) + { + link_state |= KDS_LINK_EXCLUSIVE; + } + + /* no-one else waiting? */ + if (list_empty(&resource_list[i]->waiters.link)) + { + link_state |= KDS_LINK_TRIGGERED; + rset->pending--; + } + /* Adding a non-exclusive and the current tail is a triggered non-exclusive? */ + else if (((link_state & KDS_LINK_EXCLUSIVE) == 0) && + (((list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->state & (KDS_LINK_EXCLUSIVE | KDS_LINK_TRIGGERED)) == KDS_LINK_TRIGGERED))) + { + link_state |= KDS_LINK_TRIGGERED; + rset->pending--; + } + rset->resources[i].state = link_state; + + /* avoid double wait (hang) */ + if (!list_empty(&resource_list[i]->waiters.link)) + { + /* adding same rset again? */ + if (list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->parent == rset) + { + goto roll_back; + } + } + list_add_tail(&rset->resources[i].link, &resource_list[i]->waiters.link); + } + + triggered = (rset->pending == 0); + + /* set the pointer before the callback is called so it sees it */ + *pprset = rset; + + spin_unlock_irqrestore(&kds_lock, lflags); + + if (triggered) + { + /* all resources obtained, trigger callback */ + kds_callback_perform(rset); + } + + return 0; + +roll_back: + /* roll back */ + while (i-- > 0) + { + list_del(&rset->resources[i].link); + } + err = -EINVAL; + + spin_unlock_irqrestore(&kds_lock, lflags); + kfree(rset); + return err; +} +EXPORT_SYMBOL(kds_async_waitall); + +static void wake_up_sync_call(void *callback_parameter, void *callback_extra_parameter) +{ + wait_queue_head_t *wait = (wait_queue_head_t *)callback_parameter; + wake_up(wait); +} + +static struct kds_callback sync_cb = +{ + wake_up_sync_call, + 1, + NULL, +}; + +struct kds_resource_set *kds_waitall( + int number_resources, + unsigned long *exclusive_access_bitmap, + struct kds_resource **resource_list, + unsigned long jiffies_timeout) +{ + struct kds_resource_set *rset; + unsigned long lflags; + int i; + int triggered = 0; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + + rset = kmalloc(sizeof(*rset) + number_resources * sizeof(struct kds_link), GFP_KERNEL); + if (!rset) + return rset; + + rset->num_resources = number_resources; + rset->pending = number_resources; + init_waitqueue_head(&rset->wake); + INIT_LIST_HEAD(&rset->callback_link); + INIT_WORK(&rset->callback_work, kds_queued_callback); + + spin_lock_irqsave(&kds_lock, lflags); + + for (i = 0; i < number_resources; i++) + { + unsigned long link_state = 0; + + if (test_bit(i, exclusive_access_bitmap)) + { + link_state |= KDS_LINK_EXCLUSIVE; + } + + if (list_empty(&resource_list[i]->waiters.link)) + { + link_state |= KDS_LINK_TRIGGERED; + rset->pending--; + } + /* Adding a non-exclusive and the current tail is a triggered non-exclusive? */ + else if (((link_state & KDS_LINK_EXCLUSIVE) == 0) && + (((list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->state & (KDS_LINK_EXCLUSIVE | KDS_LINK_TRIGGERED)) == KDS_LINK_TRIGGERED))) + { + link_state |= KDS_LINK_TRIGGERED; + rset->pending--; + } + + INIT_LIST_HEAD(&rset->resources[i].link); + rset->resources[i].parent = rset; + rset->resources[i].state = link_state; + + /* avoid double wait (hang) */ + if (!list_empty(&resource_list[i]->waiters.link)) + { + /* adding same rset again? */ + if (list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->parent == rset) + { + goto roll_back; + } + } + + list_add_tail(&rset->resources[i].link, &resource_list[i]->waiters.link); + } + + if (rset->pending == 0) + triggered = 1; + else + { + rset->cb = &sync_cb; + rset->callback_parameter = &rset->wake; + rset->callback_extra_parameter = NULL; + } + + spin_unlock_irqrestore(&kds_lock, lflags); + + if (!triggered) + { + long wait_res = 0; + long timeout = (jiffies_timeout == KDS_WAIT_BLOCKING) ? + MAX_SCHEDULE_TIMEOUT : jiffies_timeout; + + if (timeout) + { + wait_res = wait_event_interruptible_timeout(rset->wake, + rset->pending == 0, timeout); + } + + if ((wait_res == -ERESTARTSYS) || (wait_res == 0)) + { + /* use \a kds_resource_set_release to roll back */ + kds_resource_set_release(&rset); + return ERR_PTR(wait_res); + } + } + return rset; + +roll_back: + /* roll back */ + while (i-- > 0) + { + list_del(&rset->resources[i].link); + } + + spin_unlock_irqrestore(&kds_lock, lflags); + kfree(rset); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL(kds_waitall); + +static void __kds_resource_set_release_common(struct kds_resource_set *rset) +{ + struct list_head triggered = LIST_HEAD_INIT(triggered); + struct kds_resource_set *it; + unsigned long lflags; + int i; + + spin_lock_irqsave(&kds_lock, lflags); + + for (i = 0; i < rset->num_resources; i++) + { + struct kds_resource *resource; + struct kds_link *it = NULL; + + /* fetch the previous entry on the linked list */ + it = list_entry(rset->resources[i].link.prev, struct kds_link, link); + /* unlink ourself */ + list_del(&rset->resources[i].link); + + /* any waiters? */ + if (list_empty(&it->link)) + continue; + + /* were we the head of the list? (head if prev is a resource) */ + if (it->parent != KDS_RESOURCE) + { + if ((it->state & KDS_LINK_TRIGGERED) && !(it->state & KDS_LINK_EXCLUSIVE) ) + { + /* + * previous was triggered and not exclusive, so we + * trigger non-exclusive until end-of-list or first + * exclusive + */ + + struct kds_link *it_waiting = it; + + list_for_each_entry(it, &it_waiting->link, link) + { + /* exclusive found, stop triggering */ + if (it->state & KDS_LINK_EXCLUSIVE) + break; + + it->state |= KDS_LINK_TRIGGERED; + /* a parent to update? */ + if ( it->parent != KDS_RESOURCE ) + { + if (0 == --it->parent->pending) + { + /* new owner now triggered, track for callback later */ + list_add(&it->parent->callback_link, &triggered); + } + } + } + } + continue; + } + + /* we were the head, find the kds_resource */ + resource = container_of(it, struct kds_resource, waiters); + + /* we know there is someone waiting from the any-waiters test above */ + + /* find the head of the waiting list */ + it = list_first_entry(&resource->waiters.link, struct kds_link, link); + + /* new exclusive owner? */ + if (it->state & KDS_LINK_EXCLUSIVE) + { + /* link now triggered */ + it->state |= KDS_LINK_TRIGGERED; + /* a parent to update? */ + if (0 == --it->parent->pending) + { + /* new owner now triggered, track for callback later */ + list_add(&it->parent->callback_link, &triggered); + } + } + /* exclusive releasing ? */ + else if (rset->resources[i].state & KDS_LINK_EXCLUSIVE) + { + /* trigger non-exclusive until end-of-list or first exclusive */ + list_for_each_entry(it, &resource->waiters.link, link) + { + /* exclusive found, stop triggering */ + if (it->state & KDS_LINK_EXCLUSIVE) + break; + + it->state |= KDS_LINK_TRIGGERED; + /* a parent to update? */ + if (0 == --it->parent->pending) + { + /* new owner now triggered, track for callback later */ + list_add(&it->parent->callback_link, &triggered); + } + } + } + } + + spin_unlock_irqrestore(&kds_lock, lflags); + + while (!list_empty(&triggered)) + { + it = list_first_entry(&triggered, struct kds_resource_set, callback_link); + list_del(&it->callback_link); + kds_callback_perform(it); + } +} + +void kds_resource_set_release(struct kds_resource_set **pprset) +{ + struct kds_resource_set *rset; + + rset = cmpxchg(pprset,*pprset,NULL); + + if (!rset) + { + /* caught a race between a cancelation + * and a completion, nothing to do */ + return; + } + + __kds_resource_set_release_common(rset); + + /* + * Caller is responsible for guaranteeing that callback work is not + * pending (i.e. its running or completed) prior to calling release. + */ + BUG_ON(work_pending(&rset->callback_work)); + + /* free the resource set */ + kfree(rset); +} +EXPORT_SYMBOL(kds_resource_set_release); + +void kds_resource_set_release_sync(struct kds_resource_set **pprset) +{ + struct kds_resource_set *rset; + + rset = cmpxchg(pprset,*pprset,NULL); + if (!rset) + { + /* caught a race between a cancelation + * and a completion, nothing to do */ + return; + } + + __kds_resource_set_release_common(rset); + + /* + * In the case of a kds async wait cancellation ensure the deferred + * call back does not get scheduled if a trigger fired at the same time + * to release the wait. + */ + cancel_work_sync(&rset->callback_work); + + /* free the resource set */ + kfree(rset); +} +EXPORT_SYMBOL(kds_resource_set_release_sync); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION("1.0"); diff --git a/drivers/base/kds/sconscript b/drivers/base/kds/sconscript new file mode 100755 index 00000000000..23f8eca4cdc --- /dev/null +++ b/drivers/base/kds/sconscript @@ -0,0 +1,72 @@ +# +# (C) COPYRIGHT 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the +# GNU General Public License version 2 as published by the Free Software +# Foundation, and any use by you of this program is subject to the terms +# of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained +# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# + + +import os +import re +Import('env') + +# If KDS is built into the kernel already we skip building the module ourselves +linux_config_file = os.path.normpath(os.environ['KDIR']) + '/.config' +search_term = '^[\ ]*CONFIG_KDS[\ ]*=[\ ]*y' +build_kds = 1 +for line in open(linux_config_file, 'r'): + if re.search(search_term, line): + # KDS in kernel. Do not build KDS kernel module but + # still allow for building kds_test module. + build_kds = 0 + +if env['v'] != '1': + env['MAKECOMSTR'] = '[MAKE] ${SOURCE.dir}' + +src = [Glob('#kernel/drivers/base/kds/*.c'), Glob('#kernel/include/linux/*.h'), Glob('#kernel/drivers/base/kds/K*')] + +env.Append( CPPPATH = '#kernel/include' ) + +arch = env['arch'] +# translate into something Linux understands +if arch == 'x86_32': + arch = 'x86' +elif 'arm' in arch: + arch = 'arm' + +if Glob('tests/sconscript'): + SConscript( 'tests/sconscript' ) + +if env.GetOption('clean') : + # Clean KDS module + if build_kds: + env.Execute(Action("make ARCH=%s clean" % arch, '[CLEAN] kds')) + cmd = env.Command('$STATIC_LIB_PATH/kds.ko', src, []) + env.ProgTarget('kds', cmd) + + # Clean KDS test module + if (int(env['unit']) == 1): + cmdTest = env.Command('$STATIC_LIB_PATH/kds_test.ko', src, []) + env.ProgTarget('kds', cmdTest) +else: + # Build KDS module + if build_kds: + makeAction=Action("cd ${SOURCE.dir} && make kds ARCH=%s && cp kds.ko $STATIC_LIB_PATH/" % arch, '$MAKECOMSTR') + cmd = env.Command('$STATIC_LIB_PATH/kds.ko', src, [makeAction]) + env.ProgTarget('kds', cmd) + + # Build KDS test module + if int(env['unit']) == 1: + makeActionTest=Action("cd ${SOURCE.dir} && make kds_test ARCH=%s && cp kds_test.ko $STATIC_LIB_PATH/" %arch, '$MAKECOMSTR') + cmdTest = env.Command('$STATIC_LIB_PATH/kds_test.ko', src, [makeActionTest]) + env.ProgTarget('kds', cmdTest) + if build_kds: + Depends(cmdTest, cmd) + diff --git a/include/linux/kds.h b/include/linux/kds.h new file mode 100755 index 00000000000..73168657b3c --- /dev/null +++ b/include/linux/kds.h @@ -0,0 +1,171 @@ +/* + * + * (C) COPYRIGHT 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + + + +#ifndef _KDS_H_ +#define _KDS_H_ + +#include +#include + +#define KDS_WAIT_BLOCKING (ULONG_MAX) + +struct kds_resource_set; + +typedef void (*kds_callback_fn) (void *callback_parameter, void *callback_extra_parameter); + +struct kds_callback +{ + kds_callback_fn user_cb; /* real cb */ + int direct; /* do direct or queued call? */ + struct workqueue_struct *wq; +}; + +struct kds_link +{ + struct kds_resource_set *parent; + struct list_head link; + unsigned long state; +}; + +struct kds_resource +{ + struct kds_link waiters; +}; + +/* callback API */ + +/* Initialize a callback object. + * + * Typically created per context or per hw resource. + * + * Callbacks can be performed directly if no nested locking can + * happen in the client. + * + * Nested locking can occur when a lock is held during the kds_async_waitall or + * kds_resource_set_release call. If the callback needs to take the same lock + * nested locking will happen. + * + * If nested locking could happen non-direct callbacks can be requested. + * Callbacks will then be called asynchronous to the triggering call. + */ +int kds_callback_init(struct kds_callback *cb, int direct, kds_callback_fn user_cb); + +/* Terminate the use of a callback object. + * + * If the callback object was set up as non-direct + * any pending callbacks will be flushed first. + * Note that to avoid a deadlock the lock callbacks needs + * can't be held when a callback object is terminated. + */ +void kds_callback_term(struct kds_callback *cb); + + +/* resource object API */ + +/* initialize a resource handle for a shared resource */ +void kds_resource_init(struct kds_resource * const resource); + +/* + * Will return 0 on success. + * If the resource is being used or waited -EBUSY is returned. + * The caller should NOT try to terminate a resource that could still have clients. + * After the function returns the resource is no longer known by kds. + */ +int kds_resource_term(struct kds_resource *resource); + +/* Asynchronous wait for a set of resources. + * Callback will be called when all resources are available. + * If all the resources was available the callback will be called before kds_async_waitall returns. + * So one must not hold any locks the callback code-flow can take when calling kds_async_waitall. + * Caller considered to own/use the resources until \a kds_rset_release is called. + * exclusive_access_bitmap is a bitmap where a high bit means exclusive access while a low bit means shared access. + * Use the Linux __set_bit API, where the index of the buffer to control is used as the bit index. + * + * Standard Linux error return value. + */ +int kds_async_waitall( + struct kds_resource_set ** const pprset, + struct kds_callback *cb, + void *callback_parameter, + void *callback_extra_parameter, + int number_resources, + unsigned long *exclusive_access_bitmap, + struct kds_resource **resource_list); + +/* Synchronous wait for a set of resources. + * Function will return when one of these have happened: + * - all resources have been obtained + * - timeout lapsed while waiting + * - a signal was received while waiting + * + * To wait without a timeout, specify KDS_WAIT_BLOCKING for \a jifies_timeout, otherwise + * the timeout in jiffies. A zero timeout attempts to obtain all resources and returns + * immediately with a timeout if all resources could not be obtained. + * + * Caller considered to own/use the resources when the function returns. + * Caller must release the resources using \a kds_rset_release. + * + * Calling this function while holding already locked resources or other locking primitives is dangerous. + * One must if this is needed decide on a lock order of the resources and/or the other locking primitives + * and always take the resources/locking primitives in the specific order. + * + * Use the ERR_PTR framework to decode the return value. + * NULL = time out + * If IS_ERR then PTR_ERR gives: + * ERESTARTSYS = signal received, retry call after signal + * all other values = internal error, lock failed + * Other values = successful wait, now the owner, must call kds_resource_set_release + */ +struct kds_resource_set *kds_waitall( + int number_resources, + unsigned long *exclusive_access_bitmap, + struct kds_resource **resource_list, + unsigned long jifies_timeout); + +/* Release resources after use. + * Caller must handle that other async callbacks will trigger, + * so must avoid holding any locks a callback will take. + * + * The function takes a pointer to your poiner to handle a race + * between a cancelation and a completion. + * + * If the caller can't guarantee that a race can't occur then + * the passed in pointer must be the same in both call paths + * to allow kds to manage the potential race. + */ +void kds_resource_set_release(struct kds_resource_set **pprset); + +/* Release resources after use and wait callbacks to complete. + * Caller must handle that other async callbacks will trigger, + * so must avoid holding any locks a callback will take. + * + * The function takes a pointer to your poiner to handle a race + * between a cancelation and a completion. + * + * If the caller can't guarantee that a race can't occur then + * the passed in pointer must be the same in both call paths + * to allow kds to manage the potential race. + * + * This should be used to cancel waits which are pending on a kds + * resource. + * + * It is a bug to call this from atomic contexts and from within + * a kds callback that now owns the kds_rseource. + */ + +void kds_resource_set_release_sync(struct kds_resource_set **pprset); +#endif /* _KDS_H_ */ -- cgit v1.2.3