aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h4
-rw-r--r--drivers/gpu/drm/i915/i915_timeline.c124
-rw-r--r--drivers/gpu/drm/i915/i915_timeline.h1
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_random.c33
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_random.h3
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_timeline.c143
6 files changed, 280 insertions, 28 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 8a181b455197..6a051381f535 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1978,6 +1978,10 @@ struct drm_i915_private {
1978 struct i915_gt_timelines { 1978 struct i915_gt_timelines {
1979 struct mutex mutex; /* protects list, tainted by GPU */ 1979 struct mutex mutex; /* protects list, tainted by GPU */
1980 struct list_head list; 1980 struct list_head list;
1981
1982 /* Pack multiple timelines' seqnos into the same page */
1983 spinlock_t hwsp_lock;
1984 struct list_head hwsp_free_list;
1981 } timelines; 1985 } timelines;
1982 1986
1983 struct list_head active_rings; 1987 struct list_head active_rings;
diff --git a/drivers/gpu/drm/i915/i915_timeline.c b/drivers/gpu/drm/i915/i915_timeline.c
index 8d5792311a8f..add8fc33cf6e 100644
--- a/drivers/gpu/drm/i915/i915_timeline.c
+++ b/drivers/gpu/drm/i915/i915_timeline.c
@@ -9,6 +9,18 @@
9#include "i915_timeline.h" 9#include "i915_timeline.h"
10#include "i915_syncmap.h" 10#include "i915_syncmap.h"
11 11
12struct i915_timeline_hwsp {
13 struct i915_vma *vma;
14 struct list_head free_link;
15 u64 free_bitmap;
16};
17
18static inline struct i915_timeline_hwsp *
19i915_timeline_hwsp(const struct i915_timeline *tl)
20{
21 return tl->hwsp_ggtt->private;
22}
23
12static struct i915_vma *__hwsp_alloc(struct drm_i915_private *i915) 24static struct i915_vma *__hwsp_alloc(struct drm_i915_private *i915)
13{ 25{
14 struct drm_i915_gem_object *obj; 26 struct drm_i915_gem_object *obj;
@@ -27,28 +39,89 @@ static struct i915_vma *__hwsp_alloc(struct drm_i915_private *i915)
27 return vma; 39 return vma;
28} 40}
29 41
30static int hwsp_alloc(struct i915_timeline *timeline) 42static struct i915_vma *
43hwsp_alloc(struct i915_timeline *timeline, unsigned int *cacheline)
31{ 44{
32 struct i915_vma *vma; 45 struct drm_i915_private *i915 = timeline->i915;
46 struct i915_gt_timelines *gt = &i915->gt.timelines;
47 struct i915_timeline_hwsp *hwsp;
33 48
34 vma = __hwsp_alloc(timeline->i915); 49 BUILD_BUG_ON(BITS_PER_TYPE(u64) * CACHELINE_BYTES > PAGE_SIZE);
35 if (IS_ERR(vma))
36 return PTR_ERR(vma);
37 50
38 timeline->hwsp_ggtt = vma; 51 spin_lock(&gt->hwsp_lock);
39 timeline->hwsp_offset = 0;
40 52
41 return 0; 53 /* hwsp_free_list only contains HWSP that have available cachelines */
54 hwsp = list_first_entry_or_null(&gt->hwsp_free_list,
55 typeof(*hwsp), free_link);
56 if (!hwsp) {
57 struct i915_vma *vma;
58
59 spin_unlock(&gt->hwsp_lock);
60
61 hwsp = kmalloc(sizeof(*hwsp), GFP_KERNEL);
62 if (!hwsp)
63 return ERR_PTR(-ENOMEM);
64
65 vma = __hwsp_alloc(i915);
66 if (IS_ERR(vma)) {
67 kfree(hwsp);
68 return vma;
69 }
70
71 vma->private = hwsp;
72 hwsp->vma = vma;
73 hwsp->free_bitmap = ~0ull;
74
75 spin_lock(&gt->hwsp_lock);
76 list_add(&hwsp->free_link, &gt->hwsp_free_list);
77 }
78
79 GEM_BUG_ON(!hwsp->free_bitmap);
80 *cacheline = __ffs64(hwsp->free_bitmap);
81 hwsp->free_bitmap &= ~BIT_ULL(*cacheline);
82 if (!hwsp->free_bitmap)
83 list_del(&hwsp->free_link);
84
85 spin_unlock(&gt->hwsp_lock);
86
87 GEM_BUG_ON(hwsp->vma->private != hwsp);
88 return hwsp->vma;
89}
90
91static void hwsp_free(struct i915_timeline *timeline)
92{
93 struct i915_gt_timelines *gt = &timeline->i915->gt.timelines;
94 struct i915_timeline_hwsp *hwsp;
95
96 hwsp = i915_timeline_hwsp(timeline);
97 if (!hwsp) /* leave global HWSP alone! */
98 return;
99
100 spin_lock(&gt->hwsp_lock);
101
102 /* As a cacheline becomes available, publish the HWSP on the freelist */
103 if (!hwsp->free_bitmap)
104 list_add_tail(&hwsp->free_link, &gt->hwsp_free_list);
105
106 hwsp->free_bitmap |= BIT_ULL(timeline->hwsp_offset / CACHELINE_BYTES);
107
108 /* And if no one is left using it, give the page back to the system */
109 if (hwsp->free_bitmap == ~0ull) {
110 i915_vma_put(hwsp->vma);
111 list_del(&hwsp->free_link);
112 kfree(hwsp);
113 }
114
115 spin_unlock(&gt->hwsp_lock);
42} 116}
43 117
44int i915_timeline_init(struct drm_i915_private *i915, 118int i915_timeline_init(struct drm_i915_private *i915,
45 struct i915_timeline *timeline, 119 struct i915_timeline *timeline,
46 const char *name, 120 const char *name,
47 struct i915_vma *global_hwsp) 121 struct i915_vma *hwsp)
48{ 122{
49 struct i915_gt_timelines *gt = &i915->gt.timelines; 123 struct i915_gt_timelines *gt = &i915->gt.timelines;
50 void *vaddr; 124 void *vaddr;
51 int err;
52 125
53 /* 126 /*
54 * Ideally we want a set of engines on a single leaf as we expect 127 * Ideally we want a set of engines on a single leaf as we expect
@@ -64,18 +137,22 @@ int i915_timeline_init(struct drm_i915_private *i915,
64 timeline->name = name; 137 timeline->name = name;
65 timeline->pin_count = 0; 138 timeline->pin_count = 0;
66 139
67 if (global_hwsp) { 140 timeline->hwsp_offset = I915_GEM_HWS_SEQNO_ADDR;
68 timeline->hwsp_ggtt = i915_vma_get(global_hwsp); 141 if (!hwsp) {
69 timeline->hwsp_offset = I915_GEM_HWS_SEQNO_ADDR; 142 unsigned int cacheline;
70 } else { 143
71 err = hwsp_alloc(timeline); 144 hwsp = hwsp_alloc(timeline, &cacheline);
72 if (err) 145 if (IS_ERR(hwsp))
73 return err; 146 return PTR_ERR(hwsp);
147
148 timeline->hwsp_offset = cacheline * CACHELINE_BYTES;
74 } 149 }
150 timeline->hwsp_ggtt = i915_vma_get(hwsp);
75 151
76 vaddr = i915_gem_object_pin_map(timeline->hwsp_ggtt->obj, I915_MAP_WB); 152 vaddr = i915_gem_object_pin_map(hwsp->obj, I915_MAP_WB);
77 if (IS_ERR(vaddr)) { 153 if (IS_ERR(vaddr)) {
78 i915_vma_put(timeline->hwsp_ggtt); 154 hwsp_free(timeline);
155 i915_vma_put(hwsp);
79 return PTR_ERR(vaddr); 156 return PTR_ERR(vaddr);
80 } 157 }
81 158
@@ -105,6 +182,9 @@ void i915_timelines_init(struct drm_i915_private *i915)
105 mutex_init(&gt->mutex); 182 mutex_init(&gt->mutex);
106 INIT_LIST_HEAD(&gt->list); 183 INIT_LIST_HEAD(&gt->list);
107 184
185 spin_lock_init(&gt->hwsp_lock);
186 INIT_LIST_HEAD(&gt->hwsp_free_list);
187
108 /* via i915_gem_wait_for_idle() */ 188 /* via i915_gem_wait_for_idle() */
109 i915_gem_shrinker_taints_mutex(i915, &gt->mutex); 189 i915_gem_shrinker_taints_mutex(i915, &gt->mutex);
110} 190}
@@ -144,12 +224,13 @@ void i915_timeline_fini(struct i915_timeline *timeline)
144 GEM_BUG_ON(timeline->pin_count); 224 GEM_BUG_ON(timeline->pin_count);
145 GEM_BUG_ON(!list_empty(&timeline->requests)); 225 GEM_BUG_ON(!list_empty(&timeline->requests));
146 226
147 i915_syncmap_free(&timeline->sync);
148
149 mutex_lock(&gt->mutex); 227 mutex_lock(&gt->mutex);
150 list_del(&timeline->link); 228 list_del(&timeline->link);
151 mutex_unlock(&gt->mutex); 229 mutex_unlock(&gt->mutex);
152 230
231 i915_syncmap_free(&timeline->sync);
232 hwsp_free(timeline);
233
153 i915_gem_object_unpin_map(timeline->hwsp_ggtt->obj); 234 i915_gem_object_unpin_map(timeline->hwsp_ggtt->obj);
154 i915_vma_put(timeline->hwsp_ggtt); 235 i915_vma_put(timeline->hwsp_ggtt);
155} 236}
@@ -226,6 +307,7 @@ void i915_timelines_fini(struct drm_i915_private *i915)
226 struct i915_gt_timelines *gt = &i915->gt.timelines; 307 struct i915_gt_timelines *gt = &i915->gt.timelines;
227 308
228 GEM_BUG_ON(!list_empty(&gt->list)); 309 GEM_BUG_ON(!list_empty(&gt->list));
310 GEM_BUG_ON(!list_empty(&gt->hwsp_free_list));
229 311
230 mutex_destroy(&gt->mutex); 312 mutex_destroy(&gt->mutex);
231} 313}
diff --git a/drivers/gpu/drm/i915/i915_timeline.h b/drivers/gpu/drm/i915/i915_timeline.h
index 0c3739d53d79..ab736e2e5707 100644
--- a/drivers/gpu/drm/i915/i915_timeline.h
+++ b/drivers/gpu/drm/i915/i915_timeline.h
@@ -33,6 +33,7 @@
33#include "i915_utils.h" 33#include "i915_utils.h"
34 34
35struct i915_vma; 35struct i915_vma;
36struct i915_timeline_hwsp;
36 37
37struct i915_timeline { 38struct i915_timeline {
38 u64 fence_context; 39 u64 fence_context;
diff --git a/drivers/gpu/drm/i915/selftests/i915_random.c b/drivers/gpu/drm/i915/selftests/i915_random.c
index 1f415ce47018..716a3f19f030 100644
--- a/drivers/gpu/drm/i915/selftests/i915_random.c
+++ b/drivers/gpu/drm/i915/selftests/i915_random.c
@@ -41,18 +41,37 @@ u64 i915_prandom_u64_state(struct rnd_state *rnd)
41 return x; 41 return x;
42} 42}
43 43
44void i915_random_reorder(unsigned int *order, unsigned int count, 44void i915_prandom_shuffle(void *arr, size_t elsz, size_t count,
45 struct rnd_state *state) 45 struct rnd_state *state)
46{ 46{
47 unsigned int i, j; 47 char stack[128];
48
49 if (WARN_ON(elsz > sizeof(stack) || count > U32_MAX))
50 return;
51
52 if (!elsz || !count)
53 return;
54
55 /* Fisher-Yates shuffle courtesy of Knuth */
56 while (--count) {
57 size_t swp;
58
59 swp = i915_prandom_u32_max_state(count + 1, state);
60 if (swp == count)
61 continue;
48 62
49 for (i = 0; i < count; i++) { 63 memcpy(stack, arr + count * elsz, elsz);
50 BUILD_BUG_ON(sizeof(unsigned int) > sizeof(u32)); 64 memcpy(arr + count * elsz, arr + swp * elsz, elsz);
51 j = i915_prandom_u32_max_state(count, state); 65 memcpy(arr + swp * elsz, stack, elsz);
52 swap(order[i], order[j]);
53 } 66 }
54} 67}
55 68
69void i915_random_reorder(unsigned int *order, unsigned int count,
70 struct rnd_state *state)
71{
72 i915_prandom_shuffle(order, sizeof(*order), count, state);
73}
74
56unsigned int *i915_random_order(unsigned int count, struct rnd_state *state) 75unsigned int *i915_random_order(unsigned int count, struct rnd_state *state)
57{ 76{
58 unsigned int *order, i; 77 unsigned int *order, i;
diff --git a/drivers/gpu/drm/i915/selftests/i915_random.h b/drivers/gpu/drm/i915/selftests/i915_random.h
index 7dffedc501ca..8e1ff9c105b6 100644
--- a/drivers/gpu/drm/i915/selftests/i915_random.h
+++ b/drivers/gpu/drm/i915/selftests/i915_random.h
@@ -54,4 +54,7 @@ void i915_random_reorder(unsigned int *order,
54 unsigned int count, 54 unsigned int count,
55 struct rnd_state *state); 55 struct rnd_state *state);
56 56
57void i915_prandom_shuffle(void *arr, size_t elsz, size_t count,
58 struct rnd_state *state);
59
57#endif /* !__I915_SELFTESTS_RANDOM_H__ */ 60#endif /* !__I915_SELFTESTS_RANDOM_H__ */
diff --git a/drivers/gpu/drm/i915/selftests/i915_timeline.c b/drivers/gpu/drm/i915/selftests/i915_timeline.c
index 1585b614510d..c34340f074cf 100644
--- a/drivers/gpu/drm/i915/selftests/i915_timeline.c
+++ b/drivers/gpu/drm/i915/selftests/i915_timeline.c
@@ -4,6 +4,8 @@
4 * Copyright © 2017-2018 Intel Corporation 4 * Copyright © 2017-2018 Intel Corporation
5 */ 5 */
6 6
7#include <linux/prime_numbers.h>
8
7#include "../i915_selftest.h" 9#include "../i915_selftest.h"
8#include "i915_random.h" 10#include "i915_random.h"
9 11
@@ -11,6 +13,146 @@
11#include "mock_gem_device.h" 13#include "mock_gem_device.h"
12#include "mock_timeline.h" 14#include "mock_timeline.h"
13 15
16static struct page *hwsp_page(struct i915_timeline *tl)
17{
18 struct drm_i915_gem_object *obj = tl->hwsp_ggtt->obj;
19
20 GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj));
21 return sg_page(obj->mm.pages->sgl);
22}
23
24static unsigned long hwsp_cacheline(struct i915_timeline *tl)
25{
26 unsigned long address = (unsigned long)page_address(hwsp_page(tl));
27
28 return (address + tl->hwsp_offset) / CACHELINE_BYTES;
29}
30
31#define CACHELINES_PER_PAGE (PAGE_SIZE / CACHELINE_BYTES)
32
33struct mock_hwsp_freelist {
34 struct drm_i915_private *i915;
35 struct radix_tree_root cachelines;
36 struct i915_timeline **history;
37 unsigned long count, max;
38 struct rnd_state prng;
39};
40
41enum {
42 SHUFFLE = BIT(0),
43};
44
45static void __mock_hwsp_record(struct mock_hwsp_freelist *state,
46 unsigned int idx,
47 struct i915_timeline *tl)
48{
49 tl = xchg(&state->history[idx], tl);
50 if (tl) {
51 radix_tree_delete(&state->cachelines, hwsp_cacheline(tl));
52 i915_timeline_put(tl);
53 }
54}
55
56static int __mock_hwsp_timeline(struct mock_hwsp_freelist *state,
57 unsigned int count,
58 unsigned int flags)
59{
60 struct i915_timeline *tl;
61 unsigned int idx;
62
63 while (count--) {
64 unsigned long cacheline;
65 int err;
66
67 tl = i915_timeline_create(state->i915, "mock", NULL);
68 if (IS_ERR(tl))
69 return PTR_ERR(tl);
70
71 cacheline = hwsp_cacheline(tl);
72 err = radix_tree_insert(&state->cachelines, cacheline, tl);
73 if (err) {
74 if (err == -EEXIST) {
75 pr_err("HWSP cacheline %lu already used; duplicate allocation!\n",
76 cacheline);
77 }
78 i915_timeline_put(tl);
79 return err;
80 }
81
82 idx = state->count++ % state->max;
83 __mock_hwsp_record(state, idx, tl);
84 }
85
86 if (flags & SHUFFLE)
87 i915_prandom_shuffle(state->history,
88 sizeof(*state->history),
89 min(state->count, state->max),
90 &state->prng);
91
92 count = i915_prandom_u32_max_state(min(state->count, state->max),
93 &state->prng);
94 while (count--) {
95 idx = --state->count % state->max;
96 __mock_hwsp_record(state, idx, NULL);
97 }
98
99 return 0;
100}
101
102static int mock_hwsp_freelist(void *arg)
103{
104 struct mock_hwsp_freelist state;
105 const struct {
106 const char *name;
107 unsigned int flags;
108 } phases[] = {
109 { "linear", 0 },
110 { "shuffled", SHUFFLE },
111 { },
112 }, *p;
113 unsigned int na;
114 int err = 0;
115
116 INIT_RADIX_TREE(&state.cachelines, GFP_KERNEL);
117 state.prng = I915_RND_STATE_INITIALIZER(i915_selftest.random_seed);
118
119 state.i915 = mock_gem_device();
120 if (!state.i915)
121 return -ENOMEM;
122
123 /*
124 * Create a bunch of timelines and check that their HWSP do not overlap.
125 * Free some, and try again.
126 */
127
128 state.max = PAGE_SIZE / sizeof(*state.history);
129 state.count = 0;
130 state.history = kcalloc(state.max, sizeof(*state.history), GFP_KERNEL);
131 if (!state.history) {
132 err = -ENOMEM;
133 goto err_put;
134 }
135
136 mutex_lock(&state.i915->drm.struct_mutex);
137 for (p = phases; p->name; p++) {
138 pr_debug("%s(%s)\n", __func__, p->name);
139 for_each_prime_number_from(na, 1, 2 * CACHELINES_PER_PAGE) {
140 err = __mock_hwsp_timeline(&state, na, p->flags);
141 if (err)
142 goto out;
143 }
144 }
145
146out:
147 for (na = 0; na < state.max; na++)
148 __mock_hwsp_record(&state, na, NULL);
149 mutex_unlock(&state.i915->drm.struct_mutex);
150 kfree(state.history);
151err_put:
152 drm_dev_put(&state.i915->drm);
153 return err;
154}
155
14struct __igt_sync { 156struct __igt_sync {
15 const char *name; 157 const char *name;
16 u32 seqno; 158 u32 seqno;
@@ -260,6 +402,7 @@ static int bench_sync(void *arg)
260int i915_timeline_mock_selftests(void) 402int i915_timeline_mock_selftests(void)
261{ 403{
262 static const struct i915_subtest tests[] = { 404 static const struct i915_subtest tests[] = {
405 SUBTEST(mock_hwsp_freelist),
263 SUBTEST(igt_sync), 406 SUBTEST(igt_sync),
264 SUBTEST(bench_sync), 407 SUBTEST(bench_sync),
265 }; 408 };