linux-stable/drivers/gpu/drm/i915/i915_active_types.h
Chris Wilson b1e3177bd1 drm/i915: Coordinate i915_active with its own mutex
Forgo the struct_mutex serialisation for i915_active, and interpose its
own mutex handling for active/retire.

This is a multi-layered sleight-of-hand. First, we had to ensure that no
active/retire callbacks accidentally inverted the mutex ordering rules,
nor assumed that they were themselves serialised by struct_mutex. More
challenging though, is the rule over updating elements of the active
rbtree. Instead of the whole i915_active now being serialised by
struct_mutex, allocations/rotations of the tree are serialised by the
i915_active.mutex and individual nodes are serialised by the caller
using the i915_timeline.mutex (we need to use nested spinlocks to
interact with the dma_fence callback lists).

The pain point here is that instead of a single mutex around execbuf, we
now have to take a mutex for active tracker (one for each vma, context,
etc) and a couple of spinlocks for each fence update. The improvement in
fine grained locking allowing for multiple concurrent clients
(eventually!) should be worth it in typical loads.

v2: Add some comments that barely elucidate anything :(

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191004134015.13204-6-chris@chris-wilson.co.uk
2019-10-04 15:39:12 +01:00

68 lines
1.6 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2019 Intel Corporation
*/
#ifndef _I915_ACTIVE_TYPES_H_
#define _I915_ACTIVE_TYPES_H_
#include <linux/atomic.h>
#include <linux/dma-fence.h>
#include <linux/llist.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
#include "i915_utils.h"
struct i915_active_fence {
struct dma_fence __rcu *fence;
struct dma_fence_cb cb;
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
/*
* Incorporeal!
*
* Updates to the i915_active_request must be serialised under a lock
* to ensure that the timeline is ordered. Normally, this is the
* timeline->mutex, but another mutex may be used so long as it is
* done so consistently.
*
* For lockdep tracking of the above, we store the lock we intend
* to always use for updates of this i915_active_request during
* construction and assert that is held on every update.
*/
struct mutex *lock;
#endif
};
struct active_node;
#define I915_ACTIVE_MAY_SLEEP BIT(0)
#define __i915_active_call __aligned(4)
#define i915_active_may_sleep(fn) ptr_pack_bits(&(fn), I915_ACTIVE_MAY_SLEEP, 2)
struct i915_active {
atomic_t count;
struct mutex mutex;
struct active_node *cache;
struct rb_root tree;
/* Preallocated "exclusive" node */
struct i915_active_fence excl;
unsigned long flags;
#define I915_ACTIVE_RETIRE_SLEEPS BIT(0)
int (*active)(struct i915_active *ref);
void (*retire)(struct i915_active *ref);
struct work_struct work;
struct llist_head preallocated_barriers;
};
#endif /* _I915_ACTIVE_TYPES_H_ */