mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-04 01:49:36 +00:00
![Chris Wilson](/assets/img/avatar_default.png)
The current code is subject to a race where we may try to acquire a
reference on a stale fence:
[13703.335118] WARNING: CPU: 1 PID: 14975 at ./include/linux/kref.h:46 i915_gem_object_wait+0x1a3/0x1c0
[13703.335184] Modules linked in:
[13703.335202] CPU: 1 PID: 14975 Comm: gem_concurrent_ Not tainted 4.9.0-rc4+ #26
[13703.335216] Hardware name: / , BIOS PYBSWCEL.86A.0027.2015.0507.1758 05/07/2015
[13703.335233] ffffc90002f5bcc8 ffffffff812807de 0000000000000000 0000000000000000
[13703.335257] ffffc90002f5bd08 ffffffff81073811 0000002e80000000 ffff88026bf7c780
[13703.335279] 7fffffffffffffff 0000000000000001 ffff88027045a550 ffff88026bf7c780
[13703.335301] Call Trace:
[13703.335316] [<ffffffff812807de>] dump_stack+0x4d/0x6f
[13703.335331] [<ffffffff81073811>] __warn+0xc1/0xe0
[13703.335343] [<ffffffff810738e8>] warn_slowpath_null+0x18/0x20
[13703.335355] [<ffffffff813ac443>] i915_gem_object_wait+0x1a3/0x1c0
[13703.335367] [<ffffffff813ae8ec>] i915_gem_set_domain_ioctl+0xcc/0x330
[13703.335386] [<ffffffff813534ab>] drm_ioctl+0x1cb/0x410
[13703.335400] [<ffffffff813ae820>] ? i915_gem_obj_prepare_shmem_write+0x1d0/0x1d0
[13703.335416] [<ffffffff8135359b>] ? drm_ioctl+0x2bb/0x410
[13703.335429] [<ffffffff8117d32f>] do_vfs_ioctl+0x8f/0x5c0
[13703.335442] [<ffffffff8117d89c>] SyS_ioctl+0x3c/0x70
[13703.335456] [<ffffffff815a07a4>] entry_SYSCALL_64_fastpath+0x17/0x98
[13703.335558] ---[ end trace fd24176416ba6981 ]---
[13703.382778] general protection fault: 0000 [#1] SMP
[13703.382802] Modules linked in:
[13703.382816] CPU: 1 PID: 14967 Comm: gem_concurrent_ Tainted: G W 4.9.0-rc4+ #26
[13703.382828] Hardware name: / , BIOS PYBSWCEL.86A.0027.2015.0507.1758 05/07/2015
[13703.382841] task: ffff880275458000 task.stack: ffffc90002f18000
[13703.382849] RIP: 0010:[<ffffffff813b3534>] [<ffffffff813b3534>] i915_gem_request_retire+0x2b4/0x320
[13703.382870] RSP: 0018:ffffc90002f1bbc8 EFLAGS: 00010293
[13703.382878] RAX: dead000000000200 RBX: ffff88026bf7dce8 RCX: dead000000000100
[13703.382887] RDX: dead000000000100 RSI: ffff88026bf7c930 RDI: ffff88026bf7dd00
[13703.382897] RBP: ffffc90002f1bbf8 R08: 00000000ffffffff R09: ffff88026b89a000
[13703.382905] R10: 0000000000000001 R11: ffff88026bbe8fe0 R12: ffff88026bf7c000
[13703.382913] R13: ffff880275af8000 R14: ffff88026bf7c180 R15: dead000000000200
[13703.382922] FS: 00007f89e787d740(0000) GS:ffff88027fd00000(0000) knlGS:0000000000000000
[13703.382934] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[13703.382942] CR2: 00007f9053d2e000 CR3: 000000026d414000 CR4: 00000000001006e0
[13703.382951] Stack:
[13703.382958] ffff880275413000 ffffc90002f1bde8 ffff880275af8000 ffff880274e8a600
[13703.382976] ffff880276a06000 ffffc90002f1bde8 ffffc90002f1bc38 ffffffff813b48c5
[13703.382995] ffffc90002f1bc00 ffffc90002f1bde8 ffff88026972a440 0000000000000000
[13703.383021] Call Trace:
[13703.383032] [<ffffffff813b48c5>] i915_gem_request_alloc+0xa5/0x350
[13703.383043] [<ffffffff813a17c3>] i915_gem_do_execbuffer.isra.41+0x7b3/0x18b0
[13703.383055] [<ffffffff813b144c>] ? i915_gem_object_get_sg+0x25c/0x2b0
[13703.383065] [<ffffffff813b1d4d>] ? i915_gem_object_get_page+0x1d/0x50
[13703.383076] [<ffffffff813b28cc>] ? i915_gem_pwrite_ioctl+0x66c/0x6d0
[13703.383086] [<ffffffff813a2c25>] i915_gem_execbuffer2+0x95/0x1e0
[13703.383096] [<ffffffff813534ab>] drm_ioctl+0x1cb/0x410
[13703.383105] [<ffffffff813a2b90>] ? i915_gem_execbuffer+0x2d0/0x2d0
[13703.383117] [<ffffffff810c3df0>] ? hrtimer_start_range_ns+0x1a0/0x310
[13703.383128] [<ffffffff8117d32f>] do_vfs_ioctl+0x8f/0x5c0
[13703.383140] [<ffffffff810c60e8>] ? SyS_timer_settime+0x118/0x1a0
[13703.383150] [<ffffffff8117d89c>] SyS_ioctl+0x3c/0x70
[13703.383162] [<ffffffff815a07a4>] entry_SYSCALL_64_fastpath+0x17/0x98
[13703.383172] Code: 49 39 c6 48 8d 70 e8 48 8d 5f e8 75 16 eb 47 48 8d 43 18 48 8b 53 18 48 89 de 49 39 c6 48 8d 5a e8 74 33 48 8b 56 08 48 8b 46 10 <48> 89 42 08 48 89 10 f6 46 38 01 48 89 4e 08 4c 89 7e 10 74 cf
[13703.383557] RIP [<ffffffff813b3534>] i915_gem_request_retire+0x2b4/0x320
[13703.383570] RSP <ffffc90002f1bbc8>
[13703.383586] ---[ end trace fd24176416ba6982 ]---
This is fixed by using the kref_get_unless_zero() as a full memory
barrier to validate the fence is still the current exclusive fence before
returning it back to the caller. (Note the fix only requires using
dma_fence_get_rcu() and correct handling, but we may as well use the
helper rather than inline equivalent code.)
Note: Issue can only be hit with the i915 driver.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Sumit Semwal <sumit.semwal@linaro.org
Fixes: d07f0e59b2
("drm/i915: Move GEM activity tracking into a common struct reservation_object")
Reviewed-by: Christian König <christian.koenig@amd.com>.
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20161114115540.31155-1-chris@chris-wilson.co.uk
210 lines
6.3 KiB
C
210 lines
6.3 KiB
C
/*
|
|
* Header file for reservations for dma-buf and ttm
|
|
*
|
|
* Copyright(C) 2011 Linaro Limited. All rights reserved.
|
|
* Copyright (C) 2012-2013 Canonical Ltd
|
|
* Copyright (C) 2012 Texas Instruments
|
|
*
|
|
* Authors:
|
|
* Rob Clark <robdclark@gmail.com>
|
|
* Maarten Lankhorst <maarten.lankhorst@canonical.com>
|
|
* Thomas Hellstrom <thellstrom-at-vmware-dot-com>
|
|
*
|
|
* Based on bo.c which bears the following copyright notice,
|
|
* but is dual licensed:
|
|
*
|
|
* Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
* of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#ifndef _LINUX_RESERVATION_H
|
|
#define _LINUX_RESERVATION_H
|
|
|
|
#include <linux/ww_mutex.h>
|
|
#include <linux/dma-fence.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/seqlock.h>
|
|
#include <linux/rcupdate.h>
|
|
|
|
extern struct ww_class reservation_ww_class;
|
|
extern struct lock_class_key reservation_seqcount_class;
|
|
extern const char reservation_seqcount_string[];
|
|
|
|
/**
|
|
* struct reservation_object_list - a list of shared fences
|
|
* @rcu: for internal use
|
|
* @shared_count: table of shared fences
|
|
* @shared_max: for growing shared fence table
|
|
* @shared: shared fence table
|
|
*/
|
|
struct reservation_object_list {
|
|
struct rcu_head rcu;
|
|
u32 shared_count, shared_max;
|
|
struct dma_fence __rcu *shared[];
|
|
};
|
|
|
|
/**
|
|
* struct reservation_object - a reservation object manages fences for a buffer
|
|
* @lock: update side lock
|
|
* @seq: sequence count for managing RCU read-side synchronization
|
|
* @fence_excl: the exclusive fence, if there is one currently
|
|
* @fence: list of current shared fences
|
|
* @staged: staged copy of shared fences for RCU updates
|
|
*/
|
|
struct reservation_object {
|
|
struct ww_mutex lock;
|
|
seqcount_t seq;
|
|
|
|
struct dma_fence __rcu *fence_excl;
|
|
struct reservation_object_list __rcu *fence;
|
|
struct reservation_object_list *staged;
|
|
};
|
|
|
|
#define reservation_object_held(obj) lockdep_is_held(&(obj)->lock.base)
|
|
#define reservation_object_assert_held(obj) \
|
|
lockdep_assert_held(&(obj)->lock.base)
|
|
|
|
/**
|
|
* reservation_object_init - initialize a reservation object
|
|
* @obj: the reservation object
|
|
*/
|
|
static inline void
|
|
reservation_object_init(struct reservation_object *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);
|
|
obj->staged = NULL;
|
|
}
|
|
|
|
/**
|
|
* reservation_object_fini - destroys a reservation object
|
|
* @obj: the reservation object
|
|
*/
|
|
static inline void
|
|
reservation_object_fini(struct reservation_object *obj)
|
|
{
|
|
int i;
|
|
struct reservation_object_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);
|
|
if (fobj) {
|
|
for (i = 0; i < fobj->shared_count; ++i)
|
|
dma_fence_put(rcu_dereference_protected(fobj->shared[i], 1));
|
|
|
|
kfree(fobj);
|
|
}
|
|
kfree(obj->staged);
|
|
|
|
ww_mutex_destroy(&obj->lock);
|
|
}
|
|
|
|
/**
|
|
* reservation_object_get_list - get the reservation object's
|
|
* shared fence list, with update-side lock held
|
|
* @obj: the reservation object
|
|
*
|
|
* Returns the shared fence list. Does NOT take references to
|
|
* the fence. The obj->lock must be held.
|
|
*/
|
|
static inline struct reservation_object_list *
|
|
reservation_object_get_list(struct reservation_object *obj)
|
|
{
|
|
return rcu_dereference_protected(obj->fence,
|
|
reservation_object_held(obj));
|
|
}
|
|
|
|
/**
|
|
* reservation_object_get_excl - get the reservation object's
|
|
* exclusive fence, with update-side lock held
|
|
* @obj: the reservation object
|
|
*
|
|
* Returns the exclusive fence (if any). Does NOT take a
|
|
* reference. The obj->lock must be held.
|
|
*
|
|
* RETURNS
|
|
* The exclusive fence or NULL
|
|
*/
|
|
static inline struct dma_fence *
|
|
reservation_object_get_excl(struct reservation_object *obj)
|
|
{
|
|
return rcu_dereference_protected(obj->fence_excl,
|
|
reservation_object_held(obj));
|
|
}
|
|
|
|
/**
|
|
* reservation_object_get_excl_rcu - get the reservation object's
|
|
* exclusive fence, without lock held.
|
|
* @obj: the reservation object
|
|
*
|
|
* If there is an exclusive fence, this atomically increments it's
|
|
* reference count and returns it.
|
|
*
|
|
* RETURNS
|
|
* The exclusive fence or NULL if none
|
|
*/
|
|
static inline struct dma_fence *
|
|
reservation_object_get_excl_rcu(struct reservation_object *obj)
|
|
{
|
|
struct dma_fence *fence;
|
|
|
|
if (!rcu_access_pointer(obj->fence_excl))
|
|
return NULL;
|
|
|
|
rcu_read_lock();
|
|
fence = dma_fence_get_rcu_safe(&obj->fence_excl);
|
|
rcu_read_unlock();
|
|
|
|
return fence;
|
|
}
|
|
|
|
int reservation_object_reserve_shared(struct reservation_object *obj);
|
|
void reservation_object_add_shared_fence(struct reservation_object *obj,
|
|
struct dma_fence *fence);
|
|
|
|
void reservation_object_add_excl_fence(struct reservation_object *obj,
|
|
struct dma_fence *fence);
|
|
|
|
int reservation_object_get_fences_rcu(struct reservation_object *obj,
|
|
struct dma_fence **pfence_excl,
|
|
unsigned *pshared_count,
|
|
struct dma_fence ***pshared);
|
|
|
|
long reservation_object_wait_timeout_rcu(struct reservation_object *obj,
|
|
bool wait_all, bool intr,
|
|
unsigned long timeout);
|
|
|
|
bool reservation_object_test_signaled_rcu(struct reservation_object *obj,
|
|
bool test_all);
|
|
|
|
#endif /* _LINUX_RESERVATION_H */
|