Merge 5.4-rc1-prereleae into android-mainline

To make the 5.4-rc1 merge easier, merge at a prerelease point in time
before the final release happens.

Signed-off-by: Greg Kroah-Hartman <[email protected]>
Change-Id: I29b683c837ed1a3324644dbf9bf863f30740cd0b
This commit is contained in:
Greg Kroah-Hartman 2019-09-23 14:14:08 +02:00
commit 896be8f44d
3550 changed files with 333627 additions and 71650 deletions

View file

@ -39,4 +39,9 @@ config UDMABUF
A driver to let userspace turn memfd regions into dma-bufs.
Qemu can use this to create host dmabufs for guest framebuffers.
config DMABUF_SELFTESTS
tristate "Selftests for the dma-buf interfaces"
default n
depends on DMA_SHARED_BUFFER
endmenu

View file

@ -1,6 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
reservation.o seqno-fence.o
dma-resv.o seqno-fence.o
obj-$(CONFIG_SYNC_FILE) += sync_file.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
obj-$(CONFIG_UDMABUF) += udmabuf.o
dmabuf_selftests-y := \
selftest.o \
st-dma-fence.o
obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o

View file

@ -21,7 +21,7 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/reservation.h>
#include <linux/dma-resv.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/pseudo_fs.h>
@ -104,8 +104,8 @@ static int dma_buf_release(struct inode *inode, struct file *file)
list_del(&dmabuf->list_node);
mutex_unlock(&db_list.lock);
if (dmabuf->resv == (struct reservation_object *)&dmabuf[1])
reservation_object_fini(dmabuf->resv);
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
dma_resv_fini(dmabuf->resv);
module_put(dmabuf->owner);
kfree(dmabuf);
@ -165,7 +165,7 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
* To support cross-device and cross-driver synchronization of buffer access
* implicit fences (represented internally in the kernel with &struct fence) can
* be attached to a &dma_buf. The glue for that and a few related things are
* provided in the &reservation_object structure.
* provided in the &dma_resv structure.
*
* Userspace can query the state of these implicitly tracked fences using poll()
* and related system calls:
@ -195,8 +195,8 @@ static void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
static __poll_t dma_buf_poll(struct file *file, poll_table *poll)
{
struct dma_buf *dmabuf;
struct reservation_object *resv;
struct reservation_object_list *fobj;
struct dma_resv *resv;
struct dma_resv_list *fobj;
struct dma_fence *fence_excl;
__poll_t events;
unsigned shared_count, seq;
@ -506,13 +506,13 @@ err_alloc_file:
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
{
struct dma_buf *dmabuf;
struct reservation_object *resv = exp_info->resv;
struct dma_resv *resv = exp_info->resv;
struct file *file;
size_t alloc_size = sizeof(struct dma_buf);
int ret;
if (!exp_info->resv)
alloc_size += sizeof(struct reservation_object);
alloc_size += sizeof(struct dma_resv);
else
/* prevent &dma_buf[1] == dma_buf->resv */
alloc_size += 1;
@ -544,8 +544,8 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
if (!resv) {
resv = (struct reservation_object *)&dmabuf[1];
reservation_object_init(resv);
resv = (struct dma_resv *)&dmabuf[1];
dma_resv_init(resv);
}
dmabuf->resv = resv;
@ -909,11 +909,11 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
{
bool write = (direction == DMA_BIDIRECTIONAL ||
direction == DMA_TO_DEVICE);
struct reservation_object *resv = dmabuf->resv;
struct dma_resv *resv = dmabuf->resv;
long ret;
/* Wait on any implicit rendering fences */
ret = reservation_object_wait_timeout_rcu(resv, write, true,
ret = dma_resv_wait_timeout_rcu(resv, write, true,
MAX_SCHEDULE_TIMEOUT);
if (ret < 0)
return ret;
@ -1208,8 +1208,8 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
int ret;
struct dma_buf *buf_obj;
struct dma_buf_attachment *attach_obj;
struct reservation_object *robj;
struct reservation_object_list *fobj;
struct dma_resv *robj;
struct dma_resv_list *fobj;
struct dma_fence *fence;
unsigned seq;
int count = 0, attach_count, shared_count, i;

View file

@ -13,6 +13,8 @@
#include <linux/slab.h>
#include <linux/dma-fence-array.h>
#define PENDING_ERROR 1
static const char *dma_fence_array_get_driver_name(struct dma_fence *fence)
{
return "dma_fence_array";
@ -23,10 +25,29 @@ static const char *dma_fence_array_get_timeline_name(struct dma_fence *fence)
return "unbound";
}
static void dma_fence_array_set_pending_error(struct dma_fence_array *array,
int error)
{
/*
* Propagate the first error reported by any of our fences, but only
* before we ourselves are signaled.
*/
if (error)
cmpxchg(&array->base.error, PENDING_ERROR, error);
}
static void dma_fence_array_clear_pending_error(struct dma_fence_array *array)
{
/* Clear the error flag if not actually set. */
cmpxchg(&array->base.error, PENDING_ERROR, 0);
}
static void irq_dma_fence_array_work(struct irq_work *wrk)
{
struct dma_fence_array *array = container_of(wrk, typeof(*array), work);
dma_fence_array_clear_pending_error(array);
dma_fence_signal(&array->base);
dma_fence_put(&array->base);
}
@ -38,6 +59,8 @@ static void dma_fence_array_cb_func(struct dma_fence *f,
container_of(cb, struct dma_fence_array_cb, cb);
struct dma_fence_array *array = array_cb->array;
dma_fence_array_set_pending_error(array, f->error);
if (atomic_dec_and_test(&array->num_pending))
irq_work_queue(&array->work);
else
@ -63,9 +86,14 @@ static bool dma_fence_array_enable_signaling(struct dma_fence *fence)
dma_fence_get(&array->base);
if (dma_fence_add_callback(array->fences[i], &cb[i].cb,
dma_fence_array_cb_func)) {
int error = array->fences[i]->error;
dma_fence_array_set_pending_error(array, error);
dma_fence_put(&array->base);
if (atomic_dec_and_test(&array->num_pending))
if (atomic_dec_and_test(&array->num_pending)) {
dma_fence_array_clear_pending_error(array);
return false;
}
}
}
@ -142,6 +170,8 @@ struct dma_fence_array *dma_fence_array_create(int num_fences,
atomic_set(&array->num_pending, signal_on_any ? 1 : num_fences);
array->fences = fences;
array->base.error = PENDING_ERROR;
return array;
}
EXPORT_SYMBOL(dma_fence_array_create);

View file

@ -178,8 +178,30 @@ static bool dma_fence_chain_signaled(struct dma_fence *fence)
static void dma_fence_chain_release(struct dma_fence *fence)
{
struct dma_fence_chain *chain = to_dma_fence_chain(fence);
struct dma_fence *prev;
/* Manually unlink the chain as much as possible to avoid recursion
* and potential stack overflow.
*/
while ((prev = rcu_dereference_protected(chain->prev, true))) {
struct dma_fence_chain *prev_chain;
if (kref_read(&prev->refcount) > 1)
break;
prev_chain = to_dma_fence_chain(prev);
if (!prev_chain)
break;
/* No need for atomic operations since we hold the last
* reference to prev_chain.
*/
chain->prev = prev_chain->prev;
RCU_INIT_POINTER(prev_chain->prev, NULL);
dma_fence_put(prev);
}
dma_fence_put(prev);
dma_fence_put(rcu_dereference_protected(chain->prev, true));
dma_fence_put(chain->fence);
dma_fence_free(fence);
}

View file

@ -60,7 +60,7 @@ static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(1);
*
* - Then there's also implicit fencing, where the synchronization points are
* implicitly passed around as part of shared &dma_buf instances. Such
* implicit fences are stored in &struct reservation_object through the
* implicit fences are stored in &struct dma_resv through the
* &dma_buf.resv pointer.
*/
@ -129,31 +129,27 @@ EXPORT_SYMBOL(dma_fence_context_alloc);
int dma_fence_signal_locked(struct dma_fence *fence)
{
struct dma_fence_cb *cur, *tmp;
int ret = 0;
struct list_head cb_list;
lockdep_assert_held(fence->lock);
if (WARN_ON(!fence))
if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
&fence->flags)))
return -EINVAL;
if (test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) {
ret = -EINVAL;
/* Stash the cb_list before replacing it with the timestamp */
list_replace(&fence->cb_list, &cb_list);
/*
* we might have raced with the unlocked dma_fence_signal,
* still run through all callbacks
*/
} else {
fence->timestamp = ktime_get();
set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
trace_dma_fence_signaled(fence);
}
fence->timestamp = ktime_get();
set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
trace_dma_fence_signaled(fence);
list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) {
list_del_init(&cur->node);
list_for_each_entry_safe(cur, tmp, &cb_list, node) {
INIT_LIST_HEAD(&cur->node);
cur->func(fence, cur);
}
return ret;
return 0;
}
EXPORT_SYMBOL(dma_fence_signal_locked);
@ -173,28 +169,16 @@ EXPORT_SYMBOL(dma_fence_signal_locked);
int dma_fence_signal(struct dma_fence *fence)
{
unsigned long flags;
int ret;
if (!fence)
return -EINVAL;
if (test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
return -EINVAL;
spin_lock_irqsave(fence->lock, flags);
ret = dma_fence_signal_locked(fence);
spin_unlock_irqrestore(fence->lock, flags);
fence->timestamp = ktime_get();
set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
trace_dma_fence_signaled(fence);
if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags)) {
struct dma_fence_cb *cur, *tmp;
spin_lock_irqsave(fence->lock, flags);
list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) {
list_del_init(&cur->node);
cur->func(fence, cur);
}
spin_unlock_irqrestore(fence->lock, flags);
}
return 0;
return ret;
}
EXPORT_SYMBOL(dma_fence_signal);
@ -248,7 +232,8 @@ void dma_fence_release(struct kref *kref)
trace_dma_fence_destroy(fence);
if (WARN(!list_empty(&fence->cb_list),
if (WARN(!list_empty(&fence->cb_list) &&
!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags),
"Fence %s:%s:%llx:%llx released with pending signals!\n",
fence->ops->get_driver_name(fence),
fence->ops->get_timeline_name(fence),

View file

@ -32,7 +32,7 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
#include <linux/reservation.h>
#include <linux/dma-resv.h>
#include <linux/export.h>
/**
@ -56,26 +56,103 @@ const char reservation_seqcount_string[] = "reservation_seqcount";
EXPORT_SYMBOL(reservation_seqcount_string);
/**
* reservation_object_reserve_shared - Reserve space to add shared fences to
* a reservation_object.
* dma_resv_list_alloc - allocate fence list
* @shared_max: number of fences we need space for
*
* Allocate a new dma_resv_list and make sure to correctly initialize
* shared_max.
*/
static struct dma_resv_list *dma_resv_list_alloc(unsigned int shared_max)
{
struct dma_resv_list *list;
list = kmalloc(offsetof(typeof(*list), shared[shared_max]), GFP_KERNEL);
if (!list)
return NULL;
list->shared_max = (ksize(list) - offsetof(typeof(*list), shared)) /
sizeof(*list->shared);
return list;
}
/**
* dma_resv_list_free - free fence list
* @list: list to free
*
* Free a dma_resv_list and make sure to drop all references.
*/
static void dma_resv_list_free(struct dma_resv_list *list)
{
unsigned int i;
if (!list)
return;
for (i = 0; i < list->shared_count; ++i)
dma_fence_put(rcu_dereference_protected(list->shared[i], true));
kfree_rcu(list, rcu);
}
/**
* dma_resv_init - initialize a reservation object
* @obj: the reservation object
*/
void dma_resv_init(struct dma_resv *obj)
{
ww_mutex_init(&obj->lock, &reservation_ww_class);
__seqcount_init(&obj->seq, reservation_seqcount_string,
&reservation_seqcount_class);
RCU_INIT_POINTER(obj->fence, NULL);
RCU_INIT_POINTER(obj->fence_excl, NULL);
}
EXPORT_SYMBOL(dma_resv_init);
/**
* dma_resv_fini - destroys a reservation object
* @obj: the reservation object
*/
void dma_resv_fini(struct dma_resv *obj)
{
struct dma_resv_list *fobj;
struct dma_fence *excl;
/*
* This object should be dead and all references must have
* been released to it, so no need to be protected with rcu.
*/
excl = rcu_dereference_protected(obj->fence_excl, 1);
if (excl)
dma_fence_put(excl);
fobj = rcu_dereference_protected(obj->fence, 1);
dma_resv_list_free(fobj);
ww_mutex_destroy(&obj->lock);
}
EXPORT_SYMBOL(dma_resv_fini);
/**
* dma_resv_reserve_shared - Reserve space to add shared fences to
* a dma_resv.
* @obj: reservation object
* @num_fences: number of fences we want to add
*
* Should be called before reservation_object_add_shared_fence(). Must
* Should be called before dma_resv_add_shared_fence(). Must
* be called with obj->lock held.
*
* RETURNS
* Zero for success, or -errno
*/
int reservation_object_reserve_shared(struct reservation_object *obj,
unsigned int num_fences)
int dma_resv_reserve_shared(struct dma_resv *obj, unsigned int num_fences)
{
struct reservation_object_list *old, *new;
struct dma_resv_list *old, *new;
unsigned int i, j, k, max;
reservation_object_assert_held(obj);
dma_resv_assert_held(obj);
old = reservation_object_get_list(obj);
old = dma_resv_get_list(obj);
if (old && old->shared_max) {
if ((old->shared_count + num_fences) <= old->shared_max)
@ -87,7 +164,7 @@ int reservation_object_reserve_shared(struct reservation_object *obj,
max = 4;
}
new = kmalloc(offsetof(typeof(*new), shared[max]), GFP_KERNEL);
new = dma_resv_list_alloc(max);
if (!new)
return -ENOMEM;
@ -101,79 +178,76 @@ int reservation_object_reserve_shared(struct reservation_object *obj,
struct dma_fence *fence;
fence = rcu_dereference_protected(old->shared[i],
reservation_object_held(obj));
dma_resv_held(obj));
if (dma_fence_is_signaled(fence))
RCU_INIT_POINTER(new->shared[--k], fence);
else
RCU_INIT_POINTER(new->shared[j++], fence);
}
new->shared_count = j;
new->shared_max = max;
preempt_disable();
write_seqcount_begin(&obj->seq);
/*
* RCU_INIT_POINTER can be used here,
* seqcount provides the necessary barriers
* We are not changing the effective set of fences here so can
* merely update the pointer to the new array; both existing
* readers and new readers will see exactly the same set of
* active (unsignaled) shared fences. Individual fences and the
* old array are protected by RCU and so will not vanish under
* the gaze of the rcu_read_lock() readers.
*/
RCU_INIT_POINTER(obj->fence, new);
write_seqcount_end(&obj->seq);
preempt_enable();
rcu_assign_pointer(obj->fence, new);
if (!old)
return 0;
/* Drop the references to the signaled fences */
for (i = k; i < new->shared_max; ++i) {
for (i = k; i < max; ++i) {
struct dma_fence *fence;
fence = rcu_dereference_protected(new->shared[i],
reservation_object_held(obj));
dma_resv_held(obj));
dma_fence_put(fence);
}
kfree_rcu(old, rcu);
return 0;
}
EXPORT_SYMBOL(reservation_object_reserve_shared);
EXPORT_SYMBOL(dma_resv_reserve_shared);
/**
* reservation_object_add_shared_fence - Add a fence to a shared slot
* dma_resv_add_shared_fence - Add a fence to a shared slot
* @obj: the reservation object
* @fence: the shared fence to add
*
* Add a fence to a shared slot, obj->lock must be held, and
* reservation_object_reserve_shared() has been called.
* dma_resv_reserve_shared() has been called.
*/
void reservation_object_add_shared_fence(struct reservation_object *obj,
struct dma_fence *fence)
void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence)
{
struct reservation_object_list *fobj;
struct dma_resv_list *fobj;
struct dma_fence *old;
unsigned int i, count;
dma_fence_get(fence);
reservation_object_assert_held(obj);
dma_resv_assert_held(obj);
fobj = reservation_object_get_list(obj);
fobj = dma_resv_get_list(obj);
count = fobj->shared_count;
preempt_disable();
write_seqcount_begin(&obj->seq);
for (i = 0; i < count; ++i) {
struct dma_fence *old_fence;
old_fence = rcu_dereference_protected(fobj->shared[i],
reservation_object_held(obj));
if (old_fence->context == fence->context ||
dma_fence_is_signaled(old_fence)) {
dma_fence_put(old_fence);
old = rcu_dereference_protected(fobj->shared[i],
dma_resv_held(obj));
if (old->context == fence->context ||
dma_fence_is_signaled(old))
goto replace;
}
}
BUG_ON(fobj->shared_count >= fobj->shared_max);
old = NULL;
count++;
replace:
@ -183,26 +257,26 @@ replace:
write_seqcount_end(&obj->seq);
preempt_enable();
dma_fence_put(old);
}
EXPORT_SYMBOL(reservation_object_add_shared_fence);
EXPORT_SYMBOL(dma_resv_add_shared_fence);
/**
* reservation_object_add_excl_fence - Add an exclusive fence.
* dma_resv_add_excl_fence - Add an exclusive fence.
* @obj: the reservation object
* @fence: the shared fence to add
*
* Add a fence to the exclusive slot. The obj->lock must be held.
*/
void reservation_object_add_excl_fence(struct reservation_object *obj,
struct dma_fence *fence)
void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence)
{
struct dma_fence *old_fence = reservation_object_get_excl(obj);
struct reservation_object_list *old;
struct dma_fence *old_fence = dma_resv_get_excl(obj);
struct dma_resv_list *old;
u32 i = 0;
reservation_object_assert_held(obj);
dma_resv_assert_held(obj);
old = reservation_object_get_list(obj);
old = dma_resv_get_list(obj);
if (old)
i = old->shared_count;
@ -221,28 +295,26 @@ void reservation_object_add_excl_fence(struct reservation_object *obj,
/* inplace update, no shared fences */
while (i--)
dma_fence_put(rcu_dereference_protected(old->shared[i],
reservation_object_held(obj)));
dma_resv_held(obj)));
dma_fence_put(old_fence);
}
EXPORT_SYMBOL(reservation_object_add_excl_fence);
EXPORT_SYMBOL(dma_resv_add_excl_fence);
/**
* reservation_object_copy_fences - Copy all fences from src to dst.
* dma_resv_copy_fences - Copy all fences from src to dst.
* @dst: the destination reservation object
* @src: the source reservation object
*
* Copy all fences from src to dst. dst-lock must be held.
*/
int reservation_object_copy_fences(struct reservation_object *dst,
struct reservation_object *src)
int dma_resv_copy_fences(struct dma_resv *dst, struct dma_resv *src)
{
struct reservation_object_list *src_list, *dst_list;
struct dma_resv_list *src_list, *dst_list;
struct dma_fence *old, *new;
size_t size;
unsigned i;
reservation_object_assert_held(dst);
dma_resv_assert_held(dst);
rcu_read_lock();
src_list = rcu_dereference(src->fence);
@ -251,10 +323,9 @@ retry:
if (src_list) {
unsigned shared_count = src_list->shared_count;
size = offsetof(typeof(*src_list), shared[shared_count]);
rcu_read_unlock();
dst_list = kmalloc(size, GFP_KERNEL);
dst_list = dma_resv_list_alloc(shared_count);
if (!dst_list)
return -ENOMEM;
@ -266,7 +337,6 @@ retry:
}
dst_list->shared_count = 0;
dst_list->shared_max = shared_count;
for (i = 0; i < src_list->shared_count; ++i) {
struct dma_fence *fence;
@ -276,7 +346,7 @@ retry:
continue;
if (!dma_fence_get_rcu(fence)) {
kfree(dst_list);
dma_resv_list_free(dst_list);
src_list = rcu_dereference(src->fence);
goto retry;
}
@ -295,8 +365,8 @@ retry:
new = dma_fence_get_rcu_safe(&src->fence_excl);
rcu_read_unlock();
src_list = reservation_object_get_list(dst);
old = reservation_object_get_excl(dst);
src_list = dma_resv_get_list(dst);
old = dma_resv_get_excl(dst);
preempt_disable();
write_seqcount_begin(&dst->seq);
@ -306,16 +376,15 @@ retry:
write_seqcount_end(&dst->seq);
preempt_enable();
if (src_list)
kfree_rcu(src_list, rcu);
dma_resv_list_free(src_list);
dma_fence_put(old);
return 0;
}
EXPORT_SYMBOL(reservation_object_copy_fences);
EXPORT_SYMBOL(dma_resv_copy_fences);
/**
* reservation_object_get_fences_rcu - Get an object's shared and exclusive
* dma_resv_get_fences_rcu - Get an object's shared and exclusive
* fences without update side lock held
* @obj: the reservation object
* @pfence_excl: the returned exclusive fence (or NULL)
@ -327,10 +396,10 @@ EXPORT_SYMBOL(reservation_object_copy_fences);
* exclusive fence is not specified the fence is put into the array of the
* shared fences as well. Returns either zero or -ENOMEM.
*/
int reservation_object_get_fences_rcu(struct reservation_object *obj,
struct dma_fence **pfence_excl,
unsigned *pshared_count,
struct dma_fence ***pshared)
int dma_resv_get_fences_rcu(struct dma_resv *obj,
struct dma_fence **pfence_excl,
unsigned *pshared_count,
struct dma_fence ***pshared)
{
struct dma_fence **shared = NULL;
struct dma_fence *fence_excl;
@ -338,7 +407,7 @@ int reservation_object_get_fences_rcu(struct reservation_object *obj,
int ret = 1;
do {
struct reservation_object_list *fobj;
struct dma_resv_list *fobj;
unsigned int i, seq;
size_t sz = 0;
@ -385,13 +454,6 @@ int reservation_object_get_fences_rcu(struct reservation_object *obj,
if (!dma_fence_get_rcu(shared[i]))
break;
}
if (!pfence_excl && fence_excl) {
shared[i] = fence_excl;
fence_excl = NULL;
++i;
++shared_count;
}
}
if (i != shared_count || read_seqcount_retry(&obj->seq, seq)) {
@ -406,6 +468,11 @@ unlock:
rcu_read_unlock();
} while (ret);
if (pfence_excl)
*pfence_excl = fence_excl;
else if (fence_excl)
shared[++shared_count] = fence_excl;
if (!shared_count) {
kfree(shared);
shared = NULL;
@ -413,15 +480,12 @@ unlock:
*pshared_count = shared_count;
*pshared = shared;
if (pfence_excl)
*pfence_excl = fence_excl;
return ret;
}
EXPORT_SYMBOL_GPL(reservation_object_get_fences_rcu);
EXPORT_SYMBOL_GPL(dma_resv_get_fences_rcu);
/**
* reservation_object_wait_timeout_rcu - Wait on reservation's objects
* dma_resv_wait_timeout_rcu - Wait on reservation's objects
* shared and/or exclusive fences.
* @obj: the reservation object
* @wait_all: if true, wait on all fences, else wait on just exclusive fence
@ -432,9 +496,9 @@ EXPORT_SYMBOL_GPL(reservation_object_get_fences_rcu);
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or
* greater than zer on success.
*/
long reservation_object_wait_timeout_rcu(struct reservation_object *obj,
bool wait_all, bool intr,
unsigned long timeout)
long dma_resv_wait_timeout_rcu(struct dma_resv *obj,
bool wait_all, bool intr,
unsigned long timeout)
{
struct dma_fence *fence;
unsigned seq, shared_count;
@ -462,8 +526,7 @@ retry:
}
if (wait_all) {
struct reservation_object_list *fobj =
rcu_dereference(obj->fence);
struct dma_resv_list *fobj = rcu_dereference(obj->fence);
if (fobj)
shared_count = fobj->shared_count;
@ -506,11 +569,10 @@ unlock_retry:
rcu_read_unlock();
goto retry;
}
EXPORT_SYMBOL_GPL(reservation_object_wait_timeout_rcu);
EXPORT_SYMBOL_GPL(dma_resv_wait_timeout_rcu);
static inline int
reservation_object_test_signaled_single(struct dma_fence *passed_fence)
static inline int dma_resv_test_signaled_single(struct dma_fence *passed_fence)
{
struct dma_fence *fence, *lfence = passed_fence;
int ret = 1;
@ -527,7 +589,7 @@ reservation_object_test_signaled_single(struct dma_fence *passed_fence)
}
/**
* reservation_object_test_signaled_rcu - Test if a reservation object's
* dma_resv_test_signaled_rcu - Test if a reservation object's
* fences have been signaled.
* @obj: the reservation object
* @test_all: if true, test all fences, otherwise only test the exclusive
@ -536,8 +598,7 @@ reservation_object_test_signaled_single(struct dma_fence *passed_fence)
* RETURNS
* true if all fences signaled, else false
*/
bool reservation_object_test_signaled_rcu(struct reservation_object *obj,
bool test_all)
bool dma_resv_test_signaled_rcu(struct dma_resv *obj, bool test_all)
{
unsigned seq, shared_count;
int ret;
@ -551,8 +612,7 @@ retry:
if (test_all) {
unsigned i;
struct reservation_object_list *fobj =
rcu_dereference(obj->fence);
struct dma_resv_list *fobj = rcu_dereference(obj->fence);
if (fobj)
shared_count = fobj->shared_count;
@ -560,7 +620,7 @@ retry:
for (i = 0; i < shared_count; ++i) {
struct dma_fence *fence = rcu_dereference(fobj->shared[i]);
ret = reservation_object_test_signaled_single(fence);
ret = dma_resv_test_signaled_single(fence);
if (ret < 0)
goto retry;
else if (!ret)
@ -575,8 +635,7 @@ retry:
struct dma_fence *fence_excl = rcu_dereference(obj->fence_excl);
if (fence_excl) {
ret = reservation_object_test_signaled_single(
fence_excl);
ret = dma_resv_test_signaled_single(fence_excl);
if (ret < 0)
goto retry;
@ -588,4 +647,4 @@ retry:
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(reservation_object_test_signaled_rcu);
EXPORT_SYMBOL_GPL(dma_resv_test_signaled_rcu);

167
drivers/dma-buf/selftest.c Normal file
View file

@ -0,0 +1,167 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2019 Intel Corporation
*/
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include "selftest.h"
enum {
#define selftest(n, func) __idx_##n,
#include "selftests.h"
#undef selftest
};
#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f },
static struct selftest {
bool enabled;
const char *name;
int (*func)(void);
} selftests[] = {
#include "selftests.h"
};
#undef selftest
/* Embed the line number into the parameter name so that we can order tests */
#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n))
#define selftest_0(n, func, id) \
module_param_named(id, selftests[__idx_##n].enabled, bool, 0400);
#define selftest(n, func) selftest_0(n, func, param(n))
#include "selftests.h"
#undef selftest
int __sanitycheck__(void)
{
pr_debug("Hello World!\n");
return 0;
}
static char *__st_filter;
static bool apply_subtest_filter(const char *caller, const char *name)
{
char *filter, *sep, *tok;
bool result = true;
filter = kstrdup(__st_filter, GFP_KERNEL);
for (sep = filter; (tok = strsep(&sep, ","));) {
bool allow = true;
char *sl;
if (*tok == '!') {
allow = false;
tok++;
}
if (*tok == '\0')
continue;
sl = strchr(tok, '/');
if (sl) {
*sl++ = '\0';
if (strcmp(tok, caller)) {
if (allow)
result = false;
continue;
}
tok = sl;
}
if (strcmp(tok, name)) {
if (allow)
result = false;
continue;
}
result = allow;
break;
}
kfree(filter);
return result;
}
int
__subtests(const char *caller, const struct subtest *st, int count, void *data)
{
int err;
for (; count--; st++) {
cond_resched();
if (signal_pending(current))
return -EINTR;
if (!apply_subtest_filter(caller, st->name))
continue;
pr_info("dma-buf: Running %s/%s\n", caller, st->name);
err = st->func(data);
if (err && err != -EINTR) {
pr_err("dma-buf/%s: %s failed with error %d\n",
caller, st->name, err);
return err;
}
}
return 0;
}
static void set_default_test_all(struct selftest *st, unsigned long count)
{
unsigned long i;
for (i = 0; i < count; i++)
if (st[i].enabled)
return;
for (i = 0; i < count; i++)
st[i].enabled = true;
}
static int run_selftests(struct selftest *st, unsigned long count)
{
int err = 0;
set_default_test_all(st, count);
/* Tests are listed in natural order in selftests.h */
for (; count--; st++) {
if (!st->enabled)
continue;
pr_info("dma-buf: Running %s\n", st->name);
err = st->func();
if (err)
break;
}
if (WARN(err > 0 || err == -ENOTTY,
"%s returned %d, conflicting with selftest's magic values!\n",
st->name, err))
err = -1;
return err;
}
static int __init st_init(void)
{
return run_selftests(selftests, ARRAY_SIZE(selftests));
}
static void __exit st_exit(void)
{
}
module_param_named(st_filter, __st_filter, charp, 0400);
module_init(st_init);
module_exit(st_exit);
MODULE_DESCRIPTION("Self-test harness for dma-buf");
MODULE_LICENSE("GPL and additional rights");

View file

@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2019 Intel Corporation
*/
#ifndef __SELFTEST_H__
#define __SELFTEST_H__
#include <linux/compiler.h>
#define selftest(name, func) int func(void);
#include "selftests.h"
#undef selftest
struct subtest {
int (*func)(void *data);
const char *name;
};
int __subtests(const char *caller,
const struct subtest *st,
int count,
void *data);
#define subtests(T, data) \
__subtests(__func__, T, ARRAY_SIZE(T), data)
#define SUBTEST(x) { x, #x }
#endif /* __SELFTEST_H__ */

View file

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT */
/* List each unit test as selftest(name, function)
*
* The name is used as both an enum and expanded as subtest__name to create
* a module parameter. It must be unique and legal for a C identifier.
*
* The function should be of type int function(void). It may be conditionally
* compiled using #if IS_ENABLED(DRM_I915_SELFTEST).
*
* Tests are executed in order by igt/dmabuf_selftest
*/
selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
selftest(dma_fence, dma_fence)

View file

@ -0,0 +1,574 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2019 Intel Corporation
*/
#include <linux/delay.h>
#include <linux/dma-fence.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "selftest.h"
static struct kmem_cache *slab_fences;
static struct mock_fence {
struct dma_fence base;
struct spinlock lock;
} *to_mock_fence(struct dma_fence *f) {
return container_of(f, struct mock_fence, base);
}
static const char *mock_name(struct dma_fence *f)
{
return "mock";
}
static void mock_fence_release(struct dma_fence *f)
{
kmem_cache_free(slab_fences, to_mock_fence(f));
}
struct wait_cb {
struct dma_fence_cb cb;
struct task_struct *task;
};
static void mock_wakeup(struct dma_fence *f, struct dma_fence_cb *cb)
{
wake_up_process(container_of(cb, struct wait_cb, cb)->task);
}
static long mock_wait(struct dma_fence *f, bool intr, long timeout)
{
const int state = intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
struct wait_cb cb = { .task = current };
if (dma_fence_add_callback(f, &cb.cb, mock_wakeup))
return timeout;
while (timeout) {
set_current_state(state);
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags))
break;
if (signal_pending_state(state, current))
break;
timeout = schedule_timeout(timeout);
}
__set_current_state(TASK_RUNNING);
if (!dma_fence_remove_callback(f, &cb.cb))
return timeout;
if (signal_pending_state(state, current))
return -ERESTARTSYS;
return -ETIME;
}
static const struct dma_fence_ops mock_ops = {
.get_driver_name = mock_name,
.get_timeline_name = mock_name,
.wait = mock_wait,
.release = mock_fence_release,
};
static struct dma_fence *mock_fence(void)
{
struct mock_fence *f;
f = kmem_cache_alloc(slab_fences, GFP_KERNEL);
if (!f)
return NULL;
spin_lock_init(&f->lock);
dma_fence_init(&f->base, &mock_ops, &f->lock, 0, 0);
return &f->base;
}
static int sanitycheck(void *arg)
{
struct dma_fence *f;
f = mock_fence();
if (!f)
return -ENOMEM;
dma_fence_signal(f);
dma_fence_put(f);
return 0;
}
static int test_signaling(void *arg)
{
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
if (dma_fence_is_signaled(f)) {
pr_err("Fence unexpectedly signaled on creation\n");
goto err_free;
}
if (dma_fence_signal(f)) {
pr_err("Fence reported being already signaled\n");
goto err_free;
}
if (!dma_fence_is_signaled(f)) {
pr_err("Fence not reporting signaled\n");
goto err_free;
}
if (!dma_fence_signal(f)) {
pr_err("Fence reported not being already signaled\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
struct simple_cb {
struct dma_fence_cb cb;
bool seen;
};
static void simple_callback(struct dma_fence *f, struct dma_fence_cb *cb)
{
smp_store_mb(container_of(cb, struct simple_cb, cb)->seen, true);
}
static int test_add_callback(void *arg)
{
struct simple_cb cb = {};
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
pr_err("Failed to add callback, fence already signaled!\n");
goto err_free;
}
dma_fence_signal(f);
if (!cb.seen) {
pr_err("Callback failed!\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
static int test_late_add_callback(void *arg)
{
struct simple_cb cb = {};
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
dma_fence_signal(f);
if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) {
pr_err("Added callback, but fence was already signaled!\n");
goto err_free;
}
dma_fence_signal(f);
if (cb.seen) {
pr_err("Callback called after failed attachment !\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
static int test_rm_callback(void *arg)
{
struct simple_cb cb = {};
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
pr_err("Failed to add callback, fence already signaled!\n");
goto err_free;
}
if (!dma_fence_remove_callback(f, &cb.cb)) {
pr_err("Failed to remove callback!\n");
goto err_free;
}
dma_fence_signal(f);
if (cb.seen) {
pr_err("Callback still signaled after removal!\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
static int test_late_rm_callback(void *arg)
{
struct simple_cb cb = {};
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
pr_err("Failed to add callback, fence already signaled!\n");
goto err_free;
}
dma_fence_signal(f);
if (!cb.seen) {
pr_err("Callback failed!\n");
goto err_free;
}
if (dma_fence_remove_callback(f, &cb.cb)) {
pr_err("Callback removal succeed after being executed!\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
static int test_status(void *arg)
{
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
if (dma_fence_get_status(f)) {
pr_err("Fence unexpectedly has signaled status on creation\n");
goto err_free;
}
dma_fence_signal(f);
if (!dma_fence_get_status(f)) {
pr_err("Fence not reporting signaled status\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
static int test_error(void *arg)
{
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
dma_fence_set_error(f, -EIO);
if (dma_fence_get_status(f)) {
pr_err("Fence unexpectedly has error status before signal\n");
goto err_free;
}
dma_fence_signal(f);
if (dma_fence_get_status(f) != -EIO) {
pr_err("Fence not reporting error status, got %d\n",
dma_fence_get_status(f));
goto err_free;
}
err = 0;
err_free:
dma_fence_put(f);
return err;
}
static int test_wait(void *arg)
{
struct dma_fence *f;
int err = -EINVAL;
f = mock_fence();
if (!f)
return -ENOMEM;
if (dma_fence_wait_timeout(f, false, 0) != -ETIME) {
pr_err("Wait reported complete before being signaled\n");
goto err_free;
}
dma_fence_signal(f);
if (dma_fence_wait_timeout(f, false, 0) != 0) {
pr_err("Wait reported incomplete after being signaled\n");
goto err_free;
}
err = 0;
err_free:
dma_fence_signal(f);
dma_fence_put(f);
return err;
}
struct wait_timer {
struct timer_list timer;
struct dma_fence *f;
};
static void wait_timer(struct timer_list *timer)
{
struct wait_timer *wt = from_timer(wt, timer, timer);
dma_fence_signal(wt->f);
}
static int test_wait_timeout(void *arg)
{
struct wait_timer wt;
int err = -EINVAL;
timer_setup_on_stack(&wt.timer, wait_timer, 0);
wt.f = mock_fence();
if (!wt.f)
return -ENOMEM;
if (dma_fence_wait_timeout(wt.f, false, 1) != -ETIME) {
pr_err("Wait reported complete before being signaled\n");
goto err_free;
}
mod_timer(&wt.timer, jiffies + 1);
if (dma_fence_wait_timeout(wt.f, false, 2) == -ETIME) {
if (timer_pending(&wt.timer)) {
pr_notice("Timer did not fire within the jiffie!\n");
err = 0; /* not our fault! */
} else {
pr_err("Wait reported incomplete after timeout\n");
}
goto err_free;
}
err = 0;
err_free:
del_timer_sync(&wt.timer);
destroy_timer_on_stack(&wt.timer);
dma_fence_signal(wt.f);
dma_fence_put(wt.f);
return err;
}
static int test_stub(void *arg)
{
struct dma_fence *f[64];
int err = -EINVAL;
int i;
for (i = 0; i < ARRAY_SIZE(f); i++) {
f[i] = dma_fence_get_stub();
if (!dma_fence_is_signaled(f[i])) {
pr_err("Obtained unsignaled stub fence!\n");
goto err;
}
}
err = 0;
err:
while (i--)
dma_fence_put(f[i]);
return err;
}
/* Now off to the races! */
struct race_thread {
struct dma_fence __rcu **fences;
struct task_struct *task;
bool before;
int id;
};
static void __wait_for_callbacks(struct dma_fence *f)
{
spin_lock_irq(f->lock);
spin_unlock_irq(f->lock);
}
static int thread_signal_callback(void *arg)
{
const struct race_thread *t = arg;
unsigned long pass = 0;
unsigned long miss = 0;
int err = 0;
while (!err && !kthread_should_stop()) {
struct dma_fence *f1, *f2;
struct simple_cb cb;
f1 = mock_fence();
if (!f1) {
err = -ENOMEM;
break;
}
rcu_assign_pointer(t->fences[t->id], f1);
smp_wmb();
rcu_read_lock();
do {
f2 = dma_fence_get_rcu_safe(&t->fences[!t->id]);
} while (!f2 && !kthread_should_stop());
rcu_read_unlock();
if (t->before)
dma_fence_signal(f1);
smp_store_mb(cb.seen, false);
if (!f2 || dma_fence_add_callback(f2, &cb.cb, simple_callback))
miss++, cb.seen = true;
if (!t->before)
dma_fence_signal(f1);
if (!cb.seen) {
dma_fence_wait(f2, false);
__wait_for_callbacks(f2);
}
if (!READ_ONCE(cb.seen)) {
pr_err("Callback not seen on thread %d, pass %lu (%lu misses), signaling %s add_callback; fence signaled? %s\n",
t->id, pass, miss,
t->before ? "before" : "after",
dma_fence_is_signaled(f2) ? "yes" : "no");
err = -EINVAL;
}
dma_fence_put(f2);
rcu_assign_pointer(t->fences[t->id], NULL);
smp_wmb();
dma_fence_put(f1);
pass++;
}
pr_info("%s[%d] completed %lu passes, %lu misses\n",
__func__, t->id, pass, miss);
return err;
}
static int race_signal_callback(void *arg)
{
struct dma_fence __rcu *f[2] = {};
int ret = 0;
int pass;
for (pass = 0; !ret && pass <= 1; pass++) {
struct race_thread t[2];
int i;
for (i = 0; i < ARRAY_SIZE(t); i++) {
t[i].fences = f;
t[i].id = i;
t[i].before = pass;
t[i].task = kthread_run(thread_signal_callback, &t[i],
"dma-fence:%d", i);
get_task_struct(t[i].task);
}
msleep(50);
for (i = 0; i < ARRAY_SIZE(t); i++) {
int err;
err = kthread_stop(t[i].task);
if (err && !ret)
ret = err;
put_task_struct(t[i].task);
}
}
return ret;
}
int dma_fence(void)
{
static const struct subtest tests[] = {
SUBTEST(sanitycheck),
SUBTEST(test_signaling),
SUBTEST(test_add_callback),
SUBTEST(test_late_add_callback),
SUBTEST(test_rm_callback),
SUBTEST(test_late_rm_callback),
SUBTEST(test_status),
SUBTEST(test_error),
SUBTEST(test_wait),
SUBTEST(test_wait_timeout),
SUBTEST(test_stub),
SUBTEST(race_signal_callback),
};
int ret;
pr_info("sizeof(dma_fence)=%zu\n", sizeof(struct dma_fence));
slab_fences = KMEM_CACHE(mock_fence,
SLAB_TYPESAFE_BY_RCU |
SLAB_HWCACHE_ALIGN);
if (!slab_fences)
return -ENOMEM;
ret = subtests(tests, NULL);
kmem_cache_destroy(slab_fences);
return ret;
}

View file

@ -132,17 +132,14 @@ static void timeline_fence_release(struct dma_fence *fence)
{
struct sync_pt *pt = dma_fence_to_sync_pt(fence);
struct sync_timeline *parent = dma_fence_parent(fence);
unsigned long flags;
spin_lock_irqsave(fence->lock, flags);
if (!list_empty(&pt->link)) {
unsigned long flags;
spin_lock_irqsave(fence->lock, flags);
if (!list_empty(&pt->link)) {
list_del(&pt->link);
rb_erase(&pt->node, &parent->pt_tree);
}
spin_unlock_irqrestore(fence->lock, flags);
list_del(&pt->link);
rb_erase(&pt->node, &parent->pt_tree);
}
spin_unlock_irqrestore(fence->lock, flags);
sync_timeline_put(parent);
dma_fence_free(fence);
@ -265,7 +262,8 @@ static struct sync_pt *sync_pt_create(struct sync_timeline *obj,
p = &parent->rb_left;
} else {
if (dma_fence_get_rcu(&other->base)) {
dma_fence_put(&pt->base);
sync_timeline_put(obj);
kfree(pt);
pt = other;
goto unlock;
}

View file

@ -419,7 +419,7 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
* info->num_fences.
*/
if (!info.num_fences) {
info.status = dma_fence_is_signaled(sync_file->fence);
info.status = dma_fence_get_status(sync_file->fence);
goto no_fences;
} else {
info.status = 1;