Skip to content

Commit bdefca2

Browse files
bbrezillonrobherring
authored andcommitted
drm/panfrost: Add the panfrost_gem_mapping concept
With the introduction of per-FD address space, the same BO can be mapped in different address space if the BO is globally visible (GEM_FLINK) and opened in different context or if the dmabuf is self-imported. The current implementation does not take case into account, and attaches the mapping directly to the panfrost_gem_object. Let's create a panfrost_gem_mapping struct and allow multiple mappings per BO. The mappings are refcounted which helps solve another problem where mappings were torn down (GEM handle closed by userspace) while GPU jobs accessing those BOs were still in-flight. Jobs now keep a reference on the mappings they use. v2 (robh): - Minor review comment clean-ups from Steven - Use list_is_singular helper - Just WARN if we add a mapping when madvise state is not WILLNEED. With that, drop the use of object_name_lock. v3 (robh): - Revert returning list iterator in panfrost_gem_mapping_get() Fixes: a5efb4c ("drm/panfrost: Restructure the GEM object creation") Fixes: 7282f76 ("drm/panfrost: Implement per FD address spaces") Cc: <stable@vger.kernel.org> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com> Signed-off-by: Rob Herring <robh@kernel.org> Acked-by: Boris Brezillon <boris.brezillon@collabora.com> Reviewed-by: Steven Price <steven.price@arm.com> Link: https://patchwork.freedesktop.org/patch/msgid/20200116021554.15090-1-robh@kernel.org
1 parent db1a079 commit bdefca2

9 files changed

Lines changed: 300 additions & 74 deletions

File tree

drivers/gpu/drm/panfrost/panfrost_drv.c

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct
7878
static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
7979
struct drm_file *file)
8080
{
81+
struct panfrost_file_priv *priv = file->driver_priv;
8182
struct panfrost_gem_object *bo;
8283
struct drm_panfrost_create_bo *args = data;
84+
struct panfrost_gem_mapping *mapping;
8385

8486
if (!args->size || args->pad ||
8587
(args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP)))
@@ -95,7 +97,14 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
9597
if (IS_ERR(bo))
9698
return PTR_ERR(bo);
9799

98-
args->offset = bo->node.start << PAGE_SHIFT;
100+
mapping = panfrost_gem_mapping_get(bo, priv);
101+
if (!mapping) {
102+
drm_gem_object_put_unlocked(&bo->base.base);
103+
return -EINVAL;
104+
}
105+
106+
args->offset = mapping->mmnode.start << PAGE_SHIFT;
107+
panfrost_gem_mapping_put(mapping);
99108

100109
return 0;
101110
}
@@ -119,6 +128,11 @@ panfrost_lookup_bos(struct drm_device *dev,
119128
struct drm_panfrost_submit *args,
120129
struct panfrost_job *job)
121130
{
131+
struct panfrost_file_priv *priv = file_priv->driver_priv;
132+
struct panfrost_gem_object *bo;
133+
unsigned int i;
134+
int ret;
135+
122136
job->bo_count = args->bo_handle_count;
123137

124138
if (!job->bo_count)
@@ -130,9 +144,32 @@ panfrost_lookup_bos(struct drm_device *dev,
130144
if (!job->implicit_fences)
131145
return -ENOMEM;
132146

133-
return drm_gem_objects_lookup(file_priv,
134-
(void __user *)(uintptr_t)args->bo_handles,
135-
job->bo_count, &job->bos);
147+
ret = drm_gem_objects_lookup(file_priv,
148+
(void __user *)(uintptr_t)args->bo_handles,
149+
job->bo_count, &job->bos);
150+
if (ret)
151+
return ret;
152+
153+
job->mappings = kvmalloc_array(job->bo_count,
154+
sizeof(struct panfrost_gem_mapping *),
155+
GFP_KERNEL | __GFP_ZERO);
156+
if (!job->mappings)
157+
return -ENOMEM;
158+
159+
for (i = 0; i < job->bo_count; i++) {
160+
struct panfrost_gem_mapping *mapping;
161+
162+
bo = to_panfrost_bo(job->bos[i]);
163+
mapping = panfrost_gem_mapping_get(bo, priv);
164+
if (!mapping) {
165+
ret = -EINVAL;
166+
break;
167+
}
168+
169+
job->mappings[i] = mapping;
170+
}
171+
172+
return ret;
136173
}
137174

138175
/**
@@ -320,7 +357,9 @@ static int panfrost_ioctl_mmap_bo(struct drm_device *dev, void *data,
320357
static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,
321358
struct drm_file *file_priv)
322359
{
360+
struct panfrost_file_priv *priv = file_priv->driver_priv;
323361
struct drm_panfrost_get_bo_offset *args = data;
362+
struct panfrost_gem_mapping *mapping;
324363
struct drm_gem_object *gem_obj;
325364
struct panfrost_gem_object *bo;
326365

@@ -331,41 +370,75 @@ static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,
331370
}
332371
bo = to_panfrost_bo(gem_obj);
333372

334-
args->offset = bo->node.start << PAGE_SHIFT;
335-
373+
mapping = panfrost_gem_mapping_get(bo, priv);
336374
drm_gem_object_put_unlocked(gem_obj);
375+
376+
if (!mapping)
377+
return -EINVAL;
378+
379+
args->offset = mapping->mmnode.start << PAGE_SHIFT;
380+
panfrost_gem_mapping_put(mapping);
337381
return 0;
338382
}
339383

340384
static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
341385
struct drm_file *file_priv)
342386
{
387+
struct panfrost_file_priv *priv = file_priv->driver_priv;
343388
struct drm_panfrost_madvise *args = data;
344389
struct panfrost_device *pfdev = dev->dev_private;
345390
struct drm_gem_object *gem_obj;
391+
struct panfrost_gem_object *bo;
392+
int ret = 0;
346393

347394
gem_obj = drm_gem_object_lookup(file_priv, args->handle);
348395
if (!gem_obj) {
349396
DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
350397
return -ENOENT;
351398
}
352399

400+
bo = to_panfrost_bo(gem_obj);
401+
353402
mutex_lock(&pfdev->shrinker_lock);
403+
mutex_lock(&bo->mappings.lock);
404+
if (args->madv == PANFROST_MADV_DONTNEED) {
405+
struct panfrost_gem_mapping *first;
406+
407+
first = list_first_entry(&bo->mappings.list,
408+
struct panfrost_gem_mapping,
409+
node);
410+
411+
/*
412+
* If we want to mark the BO purgeable, there must be only one
413+
* user: the caller FD.
414+
* We could do something smarter and mark the BO purgeable only
415+
* when all its users have marked it purgeable, but globally
416+
* visible/shared BOs are likely to never be marked purgeable
417+
* anyway, so let's not bother.
418+
*/
419+
if (!list_is_singular(&bo->mappings.list) ||
420+
WARN_ON_ONCE(first->mmu != &priv->mmu)) {
421+
ret = -EINVAL;
422+
goto out_unlock_mappings;
423+
}
424+
}
425+
354426
args->retained = drm_gem_shmem_madvise(gem_obj, args->madv);
355427

356428
if (args->retained) {
357-
struct panfrost_gem_object *bo = to_panfrost_bo(gem_obj);
358-
359429
if (args->madv == PANFROST_MADV_DONTNEED)
360430
list_add_tail(&bo->base.madv_list,
361431
&pfdev->shrinker_list);
362432
else if (args->madv == PANFROST_MADV_WILLNEED)
363433
list_del_init(&bo->base.madv_list);
364434
}
435+
436+
out_unlock_mappings:
437+
mutex_unlock(&bo->mappings.lock);
365438
mutex_unlock(&pfdev->shrinker_lock);
366439

367440
drm_gem_object_put_unlocked(gem_obj);
368-
return 0;
441+
return ret;
369442
}
370443

371444
int panfrost_unstable_ioctl_check(void)

drivers/gpu/drm/panfrost/panfrost_gem.c

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
2929
list_del_init(&bo->base.madv_list);
3030
mutex_unlock(&pfdev->shrinker_lock);
3131

32+
/*
33+
* If we still have mappings attached to the BO, there's a problem in
34+
* our refcounting.
35+
*/
36+
WARN_ON_ONCE(!list_empty(&bo->mappings.list));
37+
3238
if (bo->sgts) {
3339
int i;
3440
int n_sgt = bo->base.base.size / SZ_2M;
@@ -46,6 +52,69 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
4652
drm_gem_shmem_free_object(obj);
4753
}
4854

55+
struct panfrost_gem_mapping *
56+
panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
57+
struct panfrost_file_priv *priv)
58+
{
59+
struct panfrost_gem_mapping *iter, *mapping = NULL;
60+
61+
mutex_lock(&bo->mappings.lock);
62+
list_for_each_entry(iter, &bo->mappings.list, node) {
63+
if (iter->mmu == &priv->mmu) {
64+
kref_get(&iter->refcount);
65+
mapping = iter;
66+
break;
67+
}
68+
}
69+
mutex_unlock(&bo->mappings.lock);
70+
71+
return mapping;
72+
}
73+
74+
static void
75+
panfrost_gem_teardown_mapping(struct panfrost_gem_mapping *mapping)
76+
{
77+
struct panfrost_file_priv *priv;
78+
79+
if (mapping->active)
80+
panfrost_mmu_unmap(mapping);
81+
82+
priv = container_of(mapping->mmu, struct panfrost_file_priv, mmu);
83+
spin_lock(&priv->mm_lock);
84+
if (drm_mm_node_allocated(&mapping->mmnode))
85+
drm_mm_remove_node(&mapping->mmnode);
86+
spin_unlock(&priv->mm_lock);
87+
}
88+
89+
static void panfrost_gem_mapping_release(struct kref *kref)
90+
{
91+
struct panfrost_gem_mapping *mapping;
92+
93+
mapping = container_of(kref, struct panfrost_gem_mapping, refcount);
94+
95+
panfrost_gem_teardown_mapping(mapping);
96+
drm_gem_object_put_unlocked(&mapping->obj->base.base);
97+
kfree(mapping);
98+
}
99+
100+
void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping)
101+
{
102+
if (!mapping)
103+
return;
104+
105+
kref_put(&mapping->refcount, panfrost_gem_mapping_release);
106+
}
107+
108+
void panfrost_gem_teardown_mappings(struct panfrost_gem_object *bo)
109+
{
110+
struct panfrost_gem_mapping *mapping;
111+
112+
mutex_lock(&bo->mappings.lock);
113+
list_for_each_entry(mapping, &bo->mappings.list, node)
114+
panfrost_gem_teardown_mapping(mapping);
115+
mutex_unlock(&bo->mappings.lock);
116+
}
117+
49118
int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
50119
{
51120
int ret;
@@ -54,6 +123,16 @@ int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
54123
struct panfrost_gem_object *bo = to_panfrost_bo(obj);
55124
unsigned long color = bo->noexec ? PANFROST_BO_NOEXEC : 0;
56125
struct panfrost_file_priv *priv = file_priv->driver_priv;
126+
struct panfrost_gem_mapping *mapping;
127+
128+
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
129+
if (!mapping)
130+
return -ENOMEM;
131+
132+
INIT_LIST_HEAD(&mapping->node);
133+
kref_init(&mapping->refcount);
134+
drm_gem_object_get(obj);
135+
mapping->obj = bo;
57136

58137
/*
59138
* Executable buffers cannot cross a 16MB boundary as the program
@@ -66,37 +145,48 @@ int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
66145
else
67146
align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
68147

69-
bo->mmu = &priv->mmu;
148+
mapping->mmu = &priv->mmu;
70149
spin_lock(&priv->mm_lock);
71-
ret = drm_mm_insert_node_generic(&priv->mm, &bo->node,
150+
ret = drm_mm_insert_node_generic(&priv->mm, &mapping->mmnode,
72151
size >> PAGE_SHIFT, align, color, 0);
73152
spin_unlock(&priv->mm_lock);
74153
if (ret)
75-
return ret;
154+
goto err;
76155

77156
if (!bo->is_heap) {
78-
ret = panfrost_mmu_map(bo);
79-
if (ret) {
80-
spin_lock(&priv->mm_lock);
81-
drm_mm_remove_node(&bo->node);
82-
spin_unlock(&priv->mm_lock);
83-
}
157+
ret = panfrost_mmu_map(mapping);
158+
if (ret)
159+
goto err;
84160
}
161+
162+
mutex_lock(&bo->mappings.lock);
163+
WARN_ON(bo->base.madv != PANFROST_MADV_WILLNEED);
164+
list_add_tail(&mapping->node, &bo->mappings.list);
165+
mutex_unlock(&bo->mappings.lock);
166+
167+
err:
168+
if (ret)
169+
panfrost_gem_mapping_put(mapping);
85170
return ret;
86171
}
87172

88173
void panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv)
89174
{
90-
struct panfrost_gem_object *bo = to_panfrost_bo(obj);
91175
struct panfrost_file_priv *priv = file_priv->driver_priv;
176+
struct panfrost_gem_object *bo = to_panfrost_bo(obj);
177+
struct panfrost_gem_mapping *mapping = NULL, *iter;
92178

93-
if (bo->is_mapped)
94-
panfrost_mmu_unmap(bo);
179+
mutex_lock(&bo->mappings.lock);
180+
list_for_each_entry(iter, &bo->mappings.list, node) {
181+
if (iter->mmu == &priv->mmu) {
182+
mapping = iter;
183+
list_del(&iter->node);
184+
break;
185+
}
186+
}
187+
mutex_unlock(&bo->mappings.lock);
95188

96-
spin_lock(&priv->mm_lock);
97-
if (drm_mm_node_allocated(&bo->node))
98-
drm_mm_remove_node(&bo->node);
99-
spin_unlock(&priv->mm_lock);
189+
panfrost_gem_mapping_put(mapping);
100190
}
101191

102192
static int panfrost_gem_pin(struct drm_gem_object *obj)
@@ -136,6 +226,8 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
136226
if (!obj)
137227
return NULL;
138228

229+
INIT_LIST_HEAD(&obj->mappings.list);
230+
mutex_init(&obj->mappings.lock);
139231
obj->base.base.funcs = &panfrost_gem_funcs;
140232

141233
return &obj->base.base;

0 commit comments

Comments
 (0)