2023-10-29 23:10:37

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 00/26] Add generic memory shrinker to VirtIO-GPU and Panfrost DRM drivers

This series:

1. Adds common drm-shmem memory shrinker
2. Enables shrinker for VirtIO-GPU driver
3. Switches Panfrost driver to the common shrinker
4. Fixes bugs and improves drm-shmem code

Mesa: https://gitlab.freedesktop.org/digetx/mesa/-/commits/virgl-madvise
IGT: https://gitlab.freedesktop.org/digetx/igt-gpu-tools/-/commits/virtio-madvise
https://gitlab.freedesktop.org/digetx/igt-gpu-tools/-/commits/panfrost-madvise

Changelog:

v18:- Added new pathes that change sgt allocation policy. Previously once
sgt was allocated, it exsited till GEM was freed. Now sgt is destroyed
once pages are unpinned and drivers have to manage the pages' pinning
refcounting by themselves using get/put() and pin/unpin() pages.
This removes pages refcounting ambiguity from the drm-shmem core.

- Dropped patch that changed drm_gem_shmem_vmap_locked() error handling,
like was requested by Boris Brezillon.

- Added new patches that make minor improvements:

- Optimize unlocked get_pages_sgt()
- Don't free refcounted GEM

- Dropped t-b from the Panfrost shrinker patch that was given for older
patch version since code changed with the new sgt allocation policy.

v17:- Dropped patches that added new drm-shmem sgt flags, fixing dma-buf UAF
in drm-prime error code path and preventing invalid page_count when GEM
is freed. Will revist them later on and then factor them out into a
seprate patchset.

- Dropped patches that replaced drm_gem_shmem_free() with
drm_gem_object_put(), they not needed anymore after changing
drm_gem_shmem_free() to not touch reservation lock.

- Addressed review comments from Boris Brezillon:

- Added new patch to clean up error unwinding in
drm_gem_shmem_vmap_locked()

- Added new __drm_gem_shmem_put_pages() to let the callers
to assert the held reservation lock themselves

- Moved replacement of shmem->pages check with refcount_read()
in drm_gem_shmem_free() to the shrinker addition patch

- Improved commit message of the vmap_use_count patch

- Added r-bs from Boris Brezillon that he gave to v16

v16:- Added more comments to the code for the new drm-shmem flags

- Added r-bs from Boris Brezillon

- Fixed typos and made impovements pointed out by Boris Brezillon

- Replaced kref with refcount_t as was suggested by Boris Brezillon

- Corrected placement of got_sgt flag in the Lima driver, also renamed
flag to got_pages_sgt

- Removed drm_gem_shmem_resv_assert_held() and made drm_gem_shmem_free()
to free pages without a new func that doesn't touch resv lock, as was
suggested by Boris Brezillon

- Added pages_pin_count to drm_gem_shmem_print_info()

v15:- Moved drm-shmem reference counters to use kref that allows to
optimize unlocked functions, like was suggested by Boris Brezillon.

- Changed drm/gem/shmem function names to use _locked postfix and
dropped the _unlocked, making the naming scheme consistent across
DRM code, like was suggested by Boris Brezillon.

- Added patch that fixes UAF in drm-shmem for drivers that import
dma-buf and then release buffer in the import error code path.

- Added patch that makes drm-shmem use new flag for SGT's get_pages()
refcounting, preventing unbalanced refcounting when GEM is freed.

- Fixed guest blob pinning in virtio-gpu driver that was missed
previously in the shrinker patch.

- Moved VC4 and virtio-gpu drivers to use drm_gem_put() in
GEM-creation error code paths, which is now required by drm-shmem
and was missed in a previous patch versions.

- Virtio-GPU now attaches shmem pages to host on first use and not
when BO is created. In older patch versions there was a potential
race condition in the BO creation code path where both
get_sgt()+object_attach() should've been made under same resv lock,
otherwise pages could be evicted before attachment is invoked.

- Virtio-GPU and drm-shmem shrinker patches are split into smaller
ones.

v14:- All the prerequisite reservation locking patches landed upstream,
previously were a part of this series in v13 and older.

https://lore.kernel.org/dri-devel/[email protected]/

- Added patches to improve locked/unlocked function names, like was
suggested by Boris Brezillon for v13.

- Made all exported drm-shmem symbols GPL, like was previously
discussed with Thomas Zimmermann on this series.

- Improved virtio-gpu shrinker patch. Now it won't detach purged BO
when userspace closes GEM. Crosvm (and not qemu) checks res_id on
CMD_CTX_DETACH_RESOURCE and prints noisy error message if ID is
invalid, which wasn't noticed before.

v13:- Updated virtio-gpu shrinker patch to use drm_gem_shmem_object_pin()
directly instead of drm_gem_pin() and dropped patch that exported
drm_gem_pin() functions, like was requested by Thomas Zimmermann in
v12.

v12:- Fixed the "no previous prototype for function" warning reported by
kernel build bot for v11.

- Fixed the missing reservation lock reported by Intel CI for VGEM
driver. Other drivers using drm-shmem were affected similarly to
VGEM. The problem was in the dma-buf attachment code path that led
to drm-shmem pinning function which assumed the held reservation lock
by drm_gem_pin(). In the past that code path was causing trouble for
i915 driver and we've changed the locking scheme for the attachment
code path in the dma-buf core to let exporters to handle the locking
themselves. After a closer investigation, I realized that my assumption
about testing of dma-buf export code path using Panfrost driver was
incorrect. Now I created additional local test to exrecise the Panfrost
export path. I also reproduced the issue reported by the Intel CI for
v10. It's all fixed now by making the drm_gem_shmem_pin() to take the
resv lock by itself.

- Patches are based on top of drm-tip, CC'd intel-gfx CI for testing.

v11:- Rebased on a recent linux-next. Added new patch as a result:

drm/shmem-helper: Export drm_gem_shmem_get_pages_sgt_locked()

It's needed by the virtio-gpu driver to swap-in/unevict shmem
object, previously get_pages_sgt() didn't use locking.

- Separated the "Add memory shrinker" patch into smaller parts to ease
the reviewing, as was requested by Thomas Zimmermann:

drm/shmem-helper: Factor out pages alloc/release from
drm_gem_shmem_get/put_pages()
drm/shmem-helper: Add pages_pin_count field
drm/shmem-helper: Switch drm_gem_shmem_vmap/vunmap to use pin/unpin
drm/shmem-helper: Factor out unpinning part from drm_gem_shmem_purge()

- Addessed the v10 review comments from Thomas Zimmermann: return errno
instead of bool, sort code alphabetically, rename function and etc
minor changes.

- Added new patch to remove the "map->is_iomem" from drm-shmem, as
was suggested by Thomas Zimmermann.

- Added acks and r-b's that were given to v10.

v10:- Was partially applied to misc-fixes/next.

https://lore.kernel.org/dri-devel/[email protected]/T/

Dmitry Osipenko (26):
drm/gem: Change locked/unlocked postfix of drm_gem_v/unmap() function
names
drm/gem: Add _locked postfix to functions that have unlocked
counterpart
drm/shmem-helper: Make all exported symbols GPL
drm/shmem-helper: Refactor locked/unlocked functions
drm/shmem-helper: Remove obsoleted is_iomem test
drm/shmem-helper: Add and use pages_pin_count
drm/shmem-helper: Use refcount_t for pages_use_count
drm/shmem-helper: Add and use lockless drm_gem_shmem_get_pages()
drm/shmem-helper: Switch drm_gem_shmem_vmap/vunmap to use pin/unpin
drm/shmem-helper: Use refcount_t for vmap_use_count
drm/shmem-helper: Prepare drm_gem_shmem_free() to shrinker addition
drm/shmem-helper: Make drm_gem_shmem_get_pages() public
drm/shmem-helper: Add drm_gem_shmem_put_pages()
drm/lima: Explicitly get and put drm-shmem pages
drm/panfrost: Explicitly get and put drm-shmem pages
drm/virtio: Explicitly get and put drm-shmem pages
drm/v3d: Explicitly get and put drm-shmem pages
drm/shmem-helper: Change sgt allocation policy
drm/shmem-helper: Add common memory shrinker
drm/shmem-helper: Export drm_gem_shmem_get_pages_sgt_locked()
drm/shmem-helper: Optimize unlocked get_pages_sgt()
drm/shmem-helper: Don't free refcounted GEM
drm/virtio: Pin display framebuffer BO
drm/virtio: Attach shmem BOs dynamically
drm/virtio: Support shmem shrinking
drm/panfrost: Switch to generic memory shrinker

drivers/gpu/drm/drm_client.c | 6 +-
drivers/gpu/drm/drm_gem.c | 26 +-
drivers/gpu/drm/drm_gem_framebuffer_helper.c | 6 +-
drivers/gpu/drm/drm_gem_shmem_helper.c | 644 +++++++++++++++---
drivers/gpu/drm/drm_internal.h | 4 +-
drivers/gpu/drm/drm_prime.c | 4 +-
drivers/gpu/drm/lima/lima_gem.c | 28 +-
drivers/gpu/drm/lima/lima_gem.h | 1 +
drivers/gpu/drm/lima/lima_sched.c | 4 +-
drivers/gpu/drm/panfrost/Makefile | 1 -
drivers/gpu/drm/panfrost/panfrost_device.h | 4 -
drivers/gpu/drm/panfrost/panfrost_drv.c | 29 +-
drivers/gpu/drm/panfrost/panfrost_dump.c | 4 +-
drivers/gpu/drm/panfrost/panfrost_gem.c | 57 +-
drivers/gpu/drm/panfrost/panfrost_gem.h | 9 -
.../gpu/drm/panfrost/panfrost_gem_shrinker.c | 122 ----
drivers/gpu/drm/panfrost/panfrost_job.c | 18 +-
drivers/gpu/drm/panfrost/panfrost_mmu.c | 26 +-
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 6 +-
drivers/gpu/drm/v3d/v3d_bo.c | 15 +-
drivers/gpu/drm/virtio/virtgpu_drv.h | 22 +-
drivers/gpu/drm/virtio/virtgpu_gem.c | 80 +++
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 57 +-
drivers/gpu/drm/virtio/virtgpu_kms.c | 8 +
drivers/gpu/drm/virtio/virtgpu_object.c | 147 +++-
drivers/gpu/drm/virtio/virtgpu_plane.c | 17 +-
drivers/gpu/drm/virtio/virtgpu_submit.c | 15 +-
drivers/gpu/drm/virtio/virtgpu_vq.c | 40 ++
include/drm/drm_device.h | 10 +-
include/drm/drm_gem.h | 6 +-
include/drm/drm_gem_shmem_helper.h | 125 +++-
include/uapi/drm/virtgpu_drm.h | 14 +
32 files changed, 1159 insertions(+), 396 deletions(-)
delete mode 100644 drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c

--
2.41.0


2023-10-29 23:10:51

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 07/26] drm/shmem-helper: Use refcount_t for pages_use_count

Use atomic refcount_t helper for pages_use_count to optimize pin/unpin
functions by skipping reservation locking while GEM's pin refcount > 1.

Reviewed-by: Boris Brezillon <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 33 +++++++++++--------------
drivers/gpu/drm/lima/lima_gem.c | 2 +-
drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
include/drm/drm_gem_shmem_helper.h | 2 +-
4 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index b9b71a1a563a..6e02643ed87e 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -155,7 +155,7 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
if (shmem->pages)
drm_gem_shmem_put_pages_locked(shmem);

- drm_WARN_ON(obj->dev, shmem->pages_use_count);
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));

dma_resv_unlock(shmem->base.resv);
@@ -173,14 +173,13 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)

dma_resv_assert_held(shmem->base.resv);

- if (shmem->pages_use_count++ > 0)
+ if (refcount_inc_not_zero(&shmem->pages_use_count))
return 0;

pages = drm_gem_get_pages(obj);
if (IS_ERR(pages)) {
drm_dbg_kms(obj->dev, "Failed to get pages (%ld)\n",
PTR_ERR(pages));
- shmem->pages_use_count = 0;
return PTR_ERR(pages);
}

@@ -196,6 +195,8 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)

shmem->pages = pages;

+ refcount_set(&shmem->pages_use_count, 1);
+
return 0;
}

@@ -211,21 +212,17 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)

dma_resv_assert_held(shmem->base.resv);

- if (drm_WARN_ON_ONCE(obj->dev, !shmem->pages_use_count))
- return;
-
- if (--shmem->pages_use_count > 0)
- return;
-
+ if (refcount_dec_and_test(&shmem->pages_use_count)) {
#ifdef CONFIG_X86
- if (shmem->map_wc)
- set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
+ if (shmem->map_wc)
+ set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
#endif

- drm_gem_put_pages(obj, shmem->pages,
- shmem->pages_mark_dirty_on_put,
- shmem->pages_mark_accessed_on_put);
- shmem->pages = NULL;
+ drm_gem_put_pages(obj, shmem->pages,
+ shmem->pages_mark_dirty_on_put,
+ shmem->pages_mark_accessed_on_put);
+ shmem->pages = NULL;
+ }
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

@@ -552,8 +549,8 @@ static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
* mmap'd, vm_open() just grabs an additional reference for the new
* mm the vma is getting copied into (ie. on fork()).
*/
- if (!drm_WARN_ON_ONCE(obj->dev, !shmem->pages_use_count))
- shmem->pages_use_count++;
+ drm_WARN_ON_ONCE(obj->dev,
+ !refcount_inc_not_zero(&shmem->pages_use_count));

dma_resv_unlock(shmem->base.resv);

@@ -641,7 +638,7 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
return;

drm_printf_indent(p, indent, "pages_pin_count=%u\n", refcount_read(&shmem->pages_pin_count));
- drm_printf_indent(p, indent, "pages_use_count=%u\n", shmem->pages_use_count);
+ drm_printf_indent(p, indent, "pages_use_count=%u\n", refcount_read(&shmem->pages_use_count));
drm_printf_indent(p, indent, "vmap_use_count=%u\n", shmem->vmap_use_count);
drm_printf_indent(p, indent, "vaddr=%p\n", shmem->vaddr);
}
diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c
index 62d4a409faa8..988e74f67465 100644
--- a/drivers/gpu/drm/lima/lima_gem.c
+++ b/drivers/gpu/drm/lima/lima_gem.c
@@ -47,7 +47,7 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm)
}

bo->base.pages = pages;
- bo->base.pages_use_count = 1;
+ refcount_set(&bo->base.pages_use_count, 1);

mapping_set_unevictable(mapping);
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 9fd4a89c52dd..770dab1942c2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -487,7 +487,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
goto err_unlock;
}
bo->base.pages = pages;
- bo->base.pages_use_count = 1;
+ refcount_set(&bo->base.pages_use_count, 1);
} else {
pages = bo->base.pages;
if (pages[page_offset]) {
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index 5088bd623518..bd3596e54abe 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -37,7 +37,7 @@ struct drm_gem_shmem_object {
* Reference count on the pages table.
* The pages are put when the count reaches zero.
*/
- unsigned int pages_use_count;
+ refcount_t pages_use_count;

/**
* @pages_pin_count:
--
2.41.0

2023-10-29 23:11:21

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 05/26] drm/shmem-helper: Remove obsoleted is_iomem test

Everything that uses the mapped buffer should be agnostic to is_iomem.
The only reason for the is_iomem test is that we're setting shmem->vaddr
to the returned map->vaddr. Now that the shmem->vaddr code is gone, remove
the obsoleted is_iomem test to clean up the code.

Suggested-by: Thomas Zimmermann <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 6 ------
1 file changed, 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 154585ddae08..2cc0601865f6 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -315,12 +315,6 @@ int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem,

if (obj->import_attach) {
ret = dma_buf_vmap(obj->import_attach->dmabuf, map);
- if (!ret) {
- if (drm_WARN_ON(obj->dev, map->is_iomem)) {
- dma_buf_vunmap(obj->import_attach->dmabuf, map);
- return -EIO;
- }
- }
} else {
pgprot_t prot = PAGE_KERNEL;

--
2.41.0

2023-10-29 23:17:54

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 16/26] drm/virtio: Explicitly get and put drm-shmem pages

We're moving away from implicit get_pages() that is done by
get_pages_sgt() to simplify the refcnt handling. Drivers will have
to pin pages while they use sgt. VirtIO-GPU doesn't support shrinker,
hence pages are pinned and sgt is valid as long as pages' use-count > 0.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/virtio/virtgpu_object.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index ee5d2a70656b..998f8b05ceb1 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -67,6 +67,7 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)

virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle);
if (virtio_gpu_is_shmem(bo)) {
+ drm_gem_shmem_put_pages(&bo->base);
drm_gem_shmem_free(&bo->base);
} else if (virtio_gpu_is_vram(bo)) {
struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo);
@@ -196,9 +197,13 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
return PTR_ERR(shmem_obj);
bo = gem_to_virtio_gpu_obj(&shmem_obj->base);

+ ret = drm_gem_shmem_get_pages(shmem_obj);
+ if (ret)
+ goto err_free_gem;
+
ret = virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle);
if (ret < 0)
- goto err_free_gem;
+ goto err_put_pages;

bo->dumb = params->dumb;

@@ -243,6 +248,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
kvfree(ents);
err_put_id:
virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle);
+err_put_pages:
+ drm_gem_shmem_put_pages(shmem_obj);
err_free_gem:
drm_gem_shmem_free(shmem_obj);
return ret;
--
2.41.0

2023-10-29 23:18:31

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 03/26] drm/shmem-helper: Make all exported symbols GPL

Make all drm-shmem exported symbols GPL to make them consistent with
the rest of drm-shmem symbols.

Reviewed-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index e435f986cd13..0d61f2b3e213 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -226,7 +226,7 @@ void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
shmem->pages_mark_accessed_on_put);
shmem->pages = NULL;
}
-EXPORT_SYMBOL(drm_gem_shmem_put_pages);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages);

static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)
{
@@ -271,7 +271,7 @@ int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem)

return ret;
}
-EXPORT_SYMBOL(drm_gem_shmem_pin);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_pin);

/**
* drm_gem_shmem_unpin - Unpin backing pages for a shmem GEM object
@@ -290,7 +290,7 @@ void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem)
drm_gem_shmem_unpin_locked(shmem);
dma_resv_unlock(shmem->base.resv);
}
-EXPORT_SYMBOL(drm_gem_shmem_unpin);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_unpin);

/*
* drm_gem_shmem_vmap - Create a virtual mapping for a shmem GEM object
@@ -360,7 +360,7 @@ int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,

return ret;
}
-EXPORT_SYMBOL(drm_gem_shmem_vmap);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vmap);

/*
* drm_gem_shmem_vunmap - Unmap a virtual mapping for a shmem GEM object
@@ -396,7 +396,7 @@ void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem,

shmem->vaddr = NULL;
}
-EXPORT_SYMBOL(drm_gem_shmem_vunmap);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vunmap);

static int
drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
@@ -435,7 +435,7 @@ int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv)

return (madv >= 0);
}
-EXPORT_SYMBOL(drm_gem_shmem_madvise);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_madvise);

void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
{
@@ -467,7 +467,7 @@ void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)

invalidate_mapping_pages(file_inode(obj->filp)->i_mapping, 0, (loff_t)-1);
}
-EXPORT_SYMBOL(drm_gem_shmem_purge);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_purge);

/**
* drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
@@ -642,7 +642,7 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
drm_printf_indent(p, indent, "vmap_use_count=%u\n", shmem->vmap_use_count);
drm_printf_indent(p, indent, "vaddr=%p\n", shmem->vaddr);
}
-EXPORT_SYMBOL(drm_gem_shmem_print_info);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_print_info);

/**
* drm_gem_shmem_get_sg_table - Provide a scatter/gather table of pinned
--
2.41.0

2023-10-29 23:19:12

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

To simplify the drm-shmem refcnt handling, we're moving away from
the implicit get_pages() that is used by get_pages_sgt(). From now on
drivers will have to pin pages while they use sgt. Panfrost's shrinker
doesn't support swapping out BOs, hence pages are pinned and sgt is valid
as long as pages' use-count > 0.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/panfrost/panfrost_gem.c | 17 +++++++++++++++++
drivers/gpu/drm/panfrost/panfrost_mmu.c | 6 ++----
2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 6b77d8cebcb2..bb9d43cf7c3c 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -47,8 +47,13 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
}
}
kvfree(bo->sgts);
+
+ drm_gem_shmem_put_pages(&bo->base);
}

+ if (!bo->is_heap && !obj->import_attach)
+ drm_gem_shmem_put_pages(&bo->base);
+
drm_gem_shmem_free(&bo->base);
}

@@ -269,6 +274,7 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
{
struct drm_gem_shmem_object *shmem;
struct panfrost_gem_object *bo;
+ int err;

/* Round up heap allocations to 2MB to keep fault handling simple */
if (flags & PANFROST_BO_HEAP)
@@ -282,7 +288,18 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
bo->is_heap = !!(flags & PANFROST_BO_HEAP);

+ if (!bo->is_heap) {
+ err = drm_gem_shmem_get_pages(shmem);
+ if (err)
+ goto err_free;
+ }
+
return bo;
+
+err_free:
+ drm_gem_shmem_free(&bo->base);
+
+ return ERR_PTR(err);
}

struct drm_gem_object *
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 770dab1942c2..ac145a98377b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -504,7 +504,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
if (IS_ERR(pages[i])) {
ret = PTR_ERR(pages[i]);
pages[i] = NULL;
- goto err_pages;
+ goto err_unlock;
}
}

@@ -512,7 +512,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL);
if (ret)
- goto err_pages;
+ goto err_unlock;

ret = dma_map_sgtable(pfdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
if (ret)
@@ -535,8 +535,6 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,

err_map:
sg_free_table(sgt);
-err_pages:
- drm_gem_shmem_put_pages_locked(&bo->base);
err_unlock:
dma_resv_unlock(obj->resv);
err_bo:
--
2.41.0

2023-10-29 23:19:36

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 21/26] drm/shmem-helper: Optimize unlocked get_pages_sgt()

SGT isn't refcounted. Once SGT pointer has been obtained, it remains the
same for both locked and unlocked get_pages_sgt(). Return cached SGT
directly without taking a potentially expensive lock.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 560ce565f376..6dd087f19ea3 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -955,6 +955,9 @@ struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem)
drm_WARN_ON(obj->dev, drm_gem_shmem_is_evictable(shmem));
drm_WARN_ON(obj->dev, drm_gem_shmem_is_purgeable(shmem));

+ if (shmem->sgt)
+ return shmem->sgt;
+
ret = dma_resv_lock_interruptible(shmem->base.resv, NULL);
if (ret)
return ERR_PTR(ret);
--
2.41.0

2023-10-29 23:19:38

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 20/26] drm/shmem-helper: Export drm_gem_shmem_get_pages_sgt_locked()

Export drm_gem_shmem_get_pages_sgt_locked() that will be used by virtio-gpu
shrinker during GEM swap-in operation done under the held reservation lock.

Reviewed-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 22 +++++++++++++++++++++-
include/drm/drm_gem_shmem_helper.h | 1 +
2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 007521bea302..560ce565f376 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -872,12 +872,31 @@ struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_get_sg_table);

-static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_object *shmem)
+/**
+ * drm_gem_shmem_get_pages_sgt_locked - Provide a scatter/gather table of pinned
+ * pages for a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This is a locked version of @drm_gem_shmem_get_sg_table that exports a
+ * scatter/gather table suitable for PRIME usage by calling the standard
+ * DMA mapping API.
+ *
+ * Drivers must hold GEM's reservation lock when using this function.
+ *
+ * Drivers who need to acquire an scatter/gather table for objects need to call
+ * drm_gem_shmem_get_pages_sgt() instead.
+ *
+ * Returns:
+ * A pointer to the scatter/gather table of pinned pages or error pointer on failure.
+ */
+struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
int ret;
struct sg_table *sgt;

+ dma_resv_assert_held(shmem->base.resv);
+
if (shmem->sgt)
return shmem->sgt;

@@ -901,6 +920,7 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_
kfree(sgt);
return ERR_PTR(ret);
}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_get_pages_sgt_locked);

/**
* drm_gem_shmem_get_pages_sgt - Pin pages, dma map them, and return a
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index 3bb70616d095..6ac77c2082ed 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -149,6 +149,7 @@ void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem);

struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem);
struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem);
+struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_object *shmem);

void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
struct drm_printer *p, unsigned int indent);
--
2.41.0

2023-10-29 23:20:04

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 23/26] drm/virtio: Pin display framebuffer BO

Prepare to addition of memory shrinker support by pinning display
framebuffer BO pages in memory while they are in use by display on host.
Shrinker is free to relocate framebuffer BO pages if it doesn't know that
pages are in use, thus pin the pages to disallow shrinker to move them.

Acked-by: Gerd Hoffmann <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/virtio/virtgpu_drv.h | 2 ++
drivers/gpu/drm/virtio/virtgpu_gem.c | 19 +++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_plane.c | 17 +++++++++++++++--
3 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 96365a772f77..56269814fb6d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -313,6 +313,8 @@ void virtio_gpu_array_put_free(struct virtio_gpu_object_array *objs);
void virtio_gpu_array_put_free_delayed(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_array *objs);
void virtio_gpu_array_put_free_work(struct work_struct *work);
+int virtio_gpu_gem_pin(struct virtio_gpu_object *bo);
+void virtio_gpu_gem_unpin(struct virtio_gpu_object *bo);

/* virtgpu_vq.c */
int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev);
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 7db48d17ee3a..625c05d625bf 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -294,3 +294,22 @@ void virtio_gpu_array_put_free_work(struct work_struct *work)
}
spin_unlock(&vgdev->obj_free_lock);
}
+
+int virtio_gpu_gem_pin(struct virtio_gpu_object *bo)
+{
+ int err;
+
+ if (virtio_gpu_is_shmem(bo)) {
+ err = drm_gem_shmem_pin(&bo->base);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+void virtio_gpu_gem_unpin(struct virtio_gpu_object *bo)
+{
+ if (virtio_gpu_is_shmem(bo))
+ drm_gem_shmem_unpin(&bo->base);
+}
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index a2e045f3a000..def57b01a826 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -238,20 +238,28 @@ static int virtio_gpu_plane_prepare_fb(struct drm_plane *plane,
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_framebuffer *vgfb;
struct virtio_gpu_object *bo;
+ int err;

if (!new_state->fb)
return 0;

vgfb = to_virtio_gpu_framebuffer(new_state->fb);
bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
- if (!bo || (plane->type == DRM_PLANE_TYPE_PRIMARY && !bo->guest_blob))
+
+ err = virtio_gpu_gem_pin(bo);
+ if (err)
+ return err;
+
+ if (plane->type == DRM_PLANE_TYPE_PRIMARY && !bo->guest_blob)
return 0;

if (bo->dumb && (plane->state->fb != new_state->fb)) {
vgfb->fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context,
0);
- if (!vgfb->fence)
+ if (!vgfb->fence) {
+ virtio_gpu_gem_unpin(bo);
return -ENOMEM;
+ }
}

return 0;
@@ -261,15 +269,20 @@ static void virtio_gpu_plane_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct virtio_gpu_framebuffer *vgfb;
+ struct virtio_gpu_object *bo;

if (!state->fb)
return;

vgfb = to_virtio_gpu_framebuffer(state->fb);
+ bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]);
+
if (vgfb->fence) {
dma_fence_put(&vgfb->fence->f);
vgfb->fence = NULL;
}
+
+ virtio_gpu_gem_unpin(bo);
}

static void virtio_gpu_cursor_plane_update(struct drm_plane *plane,
--
2.41.0

2023-10-29 23:20:42

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 22/26] drm/shmem-helper: Don't free refcounted GEM

Don't free refcounted shmem object to prevent use-after-free bug that
is worse than a memory leak.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 6dd087f19ea3..4253c367dc07 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -203,9 +203,10 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
if (obj->import_attach)
drm_prime_gem_destroy(obj, shmem->sgt);

- drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
- drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
- drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));
+ if (drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count)) ||
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count)) ||
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count)))
+ return;

drm_gem_object_release(obj);
kfree(shmem);
--
2.41.0

2023-10-29 23:21:24

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 19/26] drm/shmem-helper: Add common memory shrinker

Introduce common drm-shmem shrinker for DRM drivers.

To start using drm-shmem shrinker drivers should do the following:

1. Implement evict() callback of GEM object where driver should check
whether object is purgeable or evictable using drm-shmem helpers and
perform the shrinking action

2. Initialize drm-shmem internals using drmm_gem_shmem_init(drm_device),
which will register drm-shmem shrinker

3. Implement madvise IOCTL that will use drm_gem_shmem_madvise()

Signed-off-by: Daniel Almeida <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 386 +++++++++++++++++-
.../gpu/drm/panfrost/panfrost_gem_shrinker.c | 9 +-
include/drm/drm_device.h | 10 +-
include/drm/drm_gem_shmem_helper.h | 68 ++-
4 files changed, 450 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 1420d2166b76..007521bea302 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -20,6 +20,7 @@
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_managed.h>
#include <drm/drm_prime.h>
#include <drm/drm_print.h>

@@ -88,8 +89,6 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
if (ret)
goto err_release;

- INIT_LIST_HEAD(&shmem->madv_list);
-
if (!private) {
/*
* Our buffers are kept pinned, so allocating them
@@ -128,11 +127,49 @@ struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_create);

+static bool drm_gem_shmem_is_evictable(struct drm_gem_shmem_object *shmem)
+{
+ return (shmem->madv >= 0) && shmem->base.funcs->evict &&
+ refcount_read(&shmem->pages_use_count) &&
+ !refcount_read(&shmem->pages_pin_count) &&
+ !shmem->base.dma_buf && !shmem->base.import_attach &&
+ !shmem->evicted;
+}
+
+static void
+drm_gem_shmem_shrinker_update_lru_locked(struct drm_gem_shmem_object *shmem)
+{
+ struct drm_gem_object *obj = &shmem->base;
+ struct drm_gem_shmem *shmem_mm = obj->dev->shmem_mm;
+ struct drm_gem_shmem_shrinker *shmem_shrinker = &shmem_mm->shrinker;
+
+ dma_resv_assert_held(shmem->base.resv);
+
+ if (!shmem_shrinker || obj->import_attach)
+ return;
+
+ if (shmem->madv < 0)
+ drm_gem_lru_remove(&shmem->base);
+ else if (drm_gem_shmem_is_evictable(shmem) || drm_gem_shmem_is_purgeable(shmem))
+ drm_gem_lru_move_tail(&shmem_shrinker->lru_evictable, &shmem->base);
+ else if (shmem->evicted)
+ drm_gem_lru_move_tail(&shmem_shrinker->lru_evicted, &shmem->base);
+ else if (!shmem->pages)
+ drm_gem_lru_remove(&shmem->base);
+ else
+ drm_gem_lru_move_tail(&shmem_shrinker->lru_pinned, &shmem->base);
+}
+
static void
drm_gem_shmem_free_pages(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;

+ if (!shmem->pages) {
+ drm_WARN_ON(obj->dev, !shmem->evicted && shmem->madv >= 0);
+ return;
+ }
+
if (shmem->sgt) {
dma_unmap_sgtable(obj->dev->dev, shmem->sgt,
DMA_BIDIRECTIONAL, 0);
@@ -175,15 +212,25 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_free);

-static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
+static int
+drm_gem_shmem_acquire_pages(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
struct page **pages;

+ drm_WARN_ON(obj->dev, obj->import_attach);
+
dma_resv_assert_held(shmem->base.resv);

- if (refcount_inc_not_zero(&shmem->pages_use_count))
+ if (shmem->madv < 0) {
+ drm_WARN_ON(obj->dev, shmem->pages);
+ return -ENOMEM;
+ }
+
+ if (shmem->pages) {
+ drm_WARN_ON(obj->dev, !shmem->evicted);
return 0;
+ }

pages = drm_gem_get_pages(obj);
if (IS_ERR(pages)) {
@@ -204,8 +251,29 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)

shmem->pages = pages;

+ return 0;
+}
+
+static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
+{
+ int err;
+
+ dma_resv_assert_held(shmem->base.resv);
+
+ if (shmem->madv < 0)
+ return -ENOMEM;
+
+ if (refcount_inc_not_zero(&shmem->pages_use_count))
+ return 0;
+
+ err = drm_gem_shmem_acquire_pages(shmem);
+ if (err)
+ return err;
+
refcount_set(&shmem->pages_use_count, 1);

+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
+
return 0;
}

@@ -222,6 +290,8 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)

if (refcount_dec_and_test(&shmem->pages_use_count))
drm_gem_shmem_free_pages(shmem);
+
+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

@@ -238,6 +308,20 @@ void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
if (refcount_dec_not_one(&shmem->pages_use_count))
return;

+ /*
+ * Destroying the object is a special case because acquiring
+ * the obj lock can cause a locking order inversion between
+ * reservation_ww_class_mutex and fs_reclaim.
+ *
+ * This deadlock is not actually possible, because no one should
+ * be already holding the lock when GEM is released. Unfortunately
+ * lockdep is not aware of this detail. So when the refcount drops
+ * to zero, we pretend it is already locked.
+ */
+ if (!kref_read(&shmem->base.refcount) &&
+ refcount_dec_and_test(&shmem->pages_use_count))
+ return drm_gem_shmem_free_pages(shmem);
+
dma_resv_lock(shmem->base.resv, NULL);
drm_gem_shmem_put_pages_locked(shmem);
dma_resv_unlock(shmem->base.resv);
@@ -250,6 +334,11 @@ EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages);
*
* This function Increases the use count and allocates the backing pages if
* use-count equals to zero.
+ *
+ * Note that this function doesn't pin pages in memory. If your driver
+ * uses drm-shmem shrinker, then it's free to relocate pages to swap.
+ * Getting pages only guarantees that pages are allocated, and not that
+ * pages reside in memory. In order to pin pages use drm_gem_shmem_pin().
*/
int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
{
@@ -275,6 +364,10 @@ static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)
if (refcount_inc_not_zero(&shmem->pages_pin_count))
return 0;

+ ret = drm_gem_shmem_swapin_locked(shmem);
+ if (ret)
+ return ret;
+
ret = drm_gem_shmem_get_pages_locked(shmem);
if (!ret)
refcount_set(&shmem->pages_pin_count, 1);
@@ -473,29 +566,50 @@ int drm_gem_shmem_madvise_locked(struct drm_gem_shmem_object *shmem, int madv)

madv = shmem->madv;

+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
+
return (madv >= 0);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_madvise_locked);

-void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem)
+int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv)
+{
+ struct drm_gem_object *obj = &shmem->base;
+ int ret;
+
+ ret = dma_resv_lock_interruptible(obj->resv, NULL);
+ if (ret)
+ return ret;
+
+ ret = drm_gem_shmem_madvise_locked(shmem, madv);
+ dma_resv_unlock(obj->resv);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_madvise);
+
+static void
+drm_gem_shmem_shrinker_put_pages_locked(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
struct drm_device *dev = obj->dev;

dma_resv_assert_held(shmem->base.resv);

- drm_WARN_ON(obj->dev, !drm_gem_shmem_is_purgeable(shmem));
+ if (shmem->evicted)
+ return;

- dma_unmap_sgtable(dev->dev, shmem->sgt, DMA_BIDIRECTIONAL, 0);
- sg_free_table(shmem->sgt);
- kfree(shmem->sgt);
- shmem->sgt = NULL;
+ drm_gem_shmem_free_pages(shmem);
+ drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
+}

- drm_gem_shmem_put_pages_locked(shmem);
+void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem)
+{
+ struct drm_gem_object *obj = &shmem->base;

- shmem->madv = -1;
+ drm_WARN_ON(obj->dev, !drm_gem_shmem_is_purgeable(shmem));

- drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
+ drm_gem_shmem_shrinker_put_pages_locked(shmem);
drm_gem_free_mmap_offset(obj);

/* Our goal here is to return as much of the memory as
@@ -506,9 +620,45 @@ void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem)
shmem_truncate_range(file_inode(obj->filp), 0, (loff_t)-1);

invalidate_mapping_pages(file_inode(obj->filp)->i_mapping, 0, (loff_t)-1);
+
+ shmem->madv = -1;
+ shmem->evicted = false;
+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_purge_locked);

+/**
+ * drm_gem_shmem_swapin_locked() - Moves shmem GEM back to memory and enables
+ * hardware access to the memory.
+ * @shmem: shmem GEM object
+ *
+ * This function moves shmem GEM back to memory if it was previously evicted
+ * by the memory shrinker. The GEM is ready to use on success.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_swapin_locked(struct drm_gem_shmem_object *shmem)
+{
+ int err;
+
+ dma_resv_assert_held(shmem->base.resv);
+
+ if (!shmem->evicted)
+ return 0;
+
+ err = drm_gem_shmem_acquire_pages(shmem);
+ if (err)
+ return err;
+
+ shmem->evicted = false;
+
+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_swapin_locked);
+
/**
* drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
* @file: DRM file structure to create the dumb buffer for
@@ -555,22 +705,32 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
vm_fault_t ret;
struct page *page;
pgoff_t page_offset;
+ int err;

/* We don't use vmf->pgoff since that has the fake offset */
page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;

dma_resv_lock(shmem->base.resv, NULL);

- if (page_offset >= num_pages ||
- drm_WARN_ON_ONCE(obj->dev, !shmem->pages) ||
- shmem->madv < 0) {
+ err = drm_gem_shmem_swapin_locked(shmem);
+ if (err) {
+ ret = VM_FAULT_OOM;
+ goto unlock;
+ }
+
+ if (page_offset >= num_pages || !shmem->pages) {
ret = VM_FAULT_SIGBUS;
} else {
+ /*
+ * shmem->pages is guaranteed to be valid while reservation
+ * lock is held and drm_gem_shmem_swapin_locked() succeeds.
+ */
page = shmem->pages[page_offset];

ret = vmf_insert_pfn(vma, vmf->address, page_to_pfn(page));
}

+unlock:
dma_resv_unlock(shmem->base.resv);

return ret;
@@ -593,6 +753,7 @@ static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
drm_WARN_ON_ONCE(obj->dev,
!refcount_inc_not_zero(&shmem->pages_use_count));

+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
dma_resv_unlock(shmem->base.resv);

drm_gem_vm_open(vma);
@@ -678,7 +839,9 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
drm_printf_indent(p, indent, "pages_pin_count=%u\n", refcount_read(&shmem->pages_pin_count));
drm_printf_indent(p, indent, "pages_use_count=%u\n", refcount_read(&shmem->pages_use_count));
drm_printf_indent(p, indent, "vmap_use_count=%u\n", refcount_read(&shmem->vmap_use_count));
+ drm_printf_indent(p, indent, "evicted=%d\n", shmem->evicted);
drm_printf_indent(p, indent, "vaddr=%p\n", shmem->vaddr);
+ drm_printf_indent(p, indent, "madv=%d\n", shmem->madv);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_print_info);

@@ -765,8 +928,12 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_
*/
struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem)
{
- int ret;
+ struct drm_gem_object *obj = &shmem->base;
struct sg_table *sgt;
+ int ret;
+
+ drm_WARN_ON(obj->dev, drm_gem_shmem_is_evictable(shmem));
+ drm_WARN_ON(obj->dev, drm_gem_shmem_is_purgeable(shmem));

ret = dma_resv_lock_interruptible(shmem->base.resv, NULL);
if (ret)
@@ -813,6 +980,191 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_sg_table);

+static struct drm_gem_shmem_shrinker *
+to_drm_gem_shmem_shrinker(struct shrinker *shrinker)
+{
+ return container_of(shrinker, struct drm_gem_shmem_shrinker, base);
+}
+
+static unsigned long
+drm_gem_shmem_shrinker_count_objects(struct shrinker *shrinker,
+ struct shrink_control *sc)
+{
+ struct drm_gem_shmem_shrinker *shmem_shrinker =
+ to_drm_gem_shmem_shrinker(shrinker);
+ unsigned long count = shmem_shrinker->lru_evictable.count;
+
+ if (count >= SHRINK_EMPTY)
+ return SHRINK_EMPTY - 1;
+
+ return count ?: SHRINK_EMPTY;
+}
+
+void drm_gem_shmem_evict_locked(struct drm_gem_shmem_object *shmem)
+{
+ struct drm_gem_object *obj = &shmem->base;
+
+ drm_WARN_ON(obj->dev, !drm_gem_shmem_is_evictable(shmem));
+ drm_WARN_ON(obj->dev, shmem->evicted);
+
+ drm_gem_shmem_shrinker_put_pages_locked(shmem);
+
+ shmem->evicted = true;
+ drm_gem_shmem_shrinker_update_lru_locked(shmem);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_evict_locked);
+
+static bool drm_gem_shmem_shrinker_evict_locked(struct drm_gem_object *obj)
+{
+ struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+ int err;
+
+ if (!drm_gem_shmem_is_evictable(shmem) ||
+ get_nr_swap_pages() < obj->size >> PAGE_SHIFT)
+ return false;
+
+ err = drm_gem_evict_locked(obj);
+ if (err)
+ return false;
+
+ return true;
+}
+
+static bool drm_gem_shmem_shrinker_purge_locked(struct drm_gem_object *obj)
+{
+ struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+ int err;
+
+ if (!drm_gem_shmem_is_purgeable(shmem))
+ return false;
+
+ err = drm_gem_evict_locked(obj);
+ if (err)
+ return false;
+
+ return true;
+}
+
+static unsigned long
+drm_gem_shmem_shrinker_scan_objects(struct shrinker *shrinker,
+ struct shrink_control *sc)
+{
+ struct drm_gem_shmem_shrinker *shmem_shrinker;
+ unsigned long nr_to_scan = sc->nr_to_scan;
+ unsigned long remaining = 0;
+ unsigned long freed = 0;
+
+ shmem_shrinker = to_drm_gem_shmem_shrinker(shrinker);
+
+ /* purge as many objects as we can */
+ freed += drm_gem_lru_scan(&shmem_shrinker->lru_evictable,
+ nr_to_scan, &remaining,
+ drm_gem_shmem_shrinker_purge_locked);
+
+ /* evict as many objects as we can */
+ if (freed < nr_to_scan)
+ freed += drm_gem_lru_scan(&shmem_shrinker->lru_evictable,
+ nr_to_scan - freed, &remaining,
+ drm_gem_shmem_shrinker_evict_locked);
+
+ return (freed > 0 && remaining > 0) ? freed : SHRINK_STOP;
+}
+
+static int drm_gem_shmem_shrinker_init(struct drm_gem_shmem *shmem_mm,
+ const char *shrinker_name)
+{
+ struct drm_gem_shmem_shrinker *shmem_shrinker = &shmem_mm->shrinker;
+ int err;
+
+ shmem_shrinker->base.count_objects = drm_gem_shmem_shrinker_count_objects;
+ shmem_shrinker->base.scan_objects = drm_gem_shmem_shrinker_scan_objects;
+ shmem_shrinker->base.seeks = DEFAULT_SEEKS;
+
+ mutex_init(&shmem_shrinker->lock);
+ drm_gem_lru_init(&shmem_shrinker->lru_evictable, &shmem_shrinker->lock);
+ drm_gem_lru_init(&shmem_shrinker->lru_evicted, &shmem_shrinker->lock);
+ drm_gem_lru_init(&shmem_shrinker->lru_pinned, &shmem_shrinker->lock);
+
+ err = register_shrinker(&shmem_shrinker->base, shrinker_name);
+ if (err) {
+ mutex_destroy(&shmem_shrinker->lock);
+ return err;
+ }
+
+ return 0;
+}
+
+static void drm_gem_shmem_shrinker_release(struct drm_device *dev,
+ struct drm_gem_shmem *shmem_mm)
+{
+ struct drm_gem_shmem_shrinker *shmem_shrinker = &shmem_mm->shrinker;
+
+ unregister_shrinker(&shmem_shrinker->base);
+ drm_WARN_ON(dev, !list_empty(&shmem_shrinker->lru_evictable.list));
+ drm_WARN_ON(dev, !list_empty(&shmem_shrinker->lru_evicted.list));
+ drm_WARN_ON(dev, !list_empty(&shmem_shrinker->lru_pinned.list));
+ mutex_destroy(&shmem_shrinker->lock);
+}
+
+static int drm_gem_shmem_init(struct drm_device *dev)
+{
+ int err;
+
+ if (drm_WARN_ON(dev, dev->shmem_mm))
+ return -EBUSY;
+
+ dev->shmem_mm = kzalloc(sizeof(*dev->shmem_mm), GFP_KERNEL);
+ if (!dev->shmem_mm)
+ return -ENOMEM;
+
+ err = drm_gem_shmem_shrinker_init(dev->shmem_mm, dev->unique);
+ if (err)
+ goto free_gem_shmem;
+
+ return 0;
+
+free_gem_shmem:
+ kfree(dev->shmem_mm);
+ dev->shmem_mm = NULL;
+
+ return err;
+}
+
+static void drm_gem_shmem_release(struct drm_device *dev, void *ptr)
+{
+ struct drm_gem_shmem *shmem_mm = dev->shmem_mm;
+
+ drm_gem_shmem_shrinker_release(dev, shmem_mm);
+ dev->shmem_mm = NULL;
+ kfree(shmem_mm);
+}
+
+/**
+ * drmm_gem_shmem_init() - Initialize drm-shmem internals
+ * @dev: DRM device
+ *
+ * Cleanup is automatically managed as part of DRM device releasing.
+ * Calling this function multiple times will result in a error.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drmm_gem_shmem_init(struct drm_device *dev)
+{
+ int err;
+
+ err = drm_gem_shmem_init(dev);
+ if (err)
+ return err;
+
+ err = drmm_add_action_or_reset(dev, drm_gem_shmem_release, NULL);
+ if (err)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(drmm_gem_shmem_init);
+
MODULE_DESCRIPTION("DRM SHMEM memory-management helpers");
MODULE_IMPORT_NS(DMA_BUF);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 72193bd734e1..1aa94fff7072 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -15,6 +15,13 @@
#include "panfrost_gem.h"
#include "panfrost_mmu.h"

+static bool panfrost_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem)
+{
+ return (shmem->madv > 0) &&
+ !refcount_read(&shmem->pages_pin_count) && shmem->sgt &&
+ !shmem->base.dma_buf && !shmem->base.import_attach;
+}
+
static unsigned long
panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
{
@@ -27,7 +34,7 @@ panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc
return 0;

list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) {
- if (drm_gem_shmem_is_purgeable(shmem))
+ if (panfrost_gem_shmem_is_purgeable(shmem))
count += shmem->base.size >> PAGE_SHIFT;
}

diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h
index c490977ee250..9ef31573057c 100644
--- a/include/drm/drm_device.h
+++ b/include/drm/drm_device.h
@@ -16,6 +16,7 @@ struct drm_vblank_crtc;
struct drm_vma_offset_manager;
struct drm_vram_mm;
struct drm_fb_helper;
+struct drm_gem_shmem_shrinker;

struct inode;

@@ -290,8 +291,13 @@ struct drm_device {
/** @vma_offset_manager: GEM information */
struct drm_vma_offset_manager *vma_offset_manager;

- /** @vram_mm: VRAM MM memory manager */
- struct drm_vram_mm *vram_mm;
+ union {
+ /** @vram_mm: VRAM MM memory manager */
+ struct drm_vram_mm *vram_mm;
+
+ /** @shmem_mm: SHMEM GEM memory manager */
+ struct drm_gem_shmem *shmem_mm;
+ };

/**
* @switch_power_state:
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index 6aad3e27d7ee..3bb70616d095 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -6,6 +6,7 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/mutex.h>
+#include <linux/shrinker.h>

#include <drm/drm_file.h>
#include <drm/drm_gem.h>
@@ -13,6 +14,7 @@
#include <drm/drm_prime.h>

struct dma_buf_attachment;
+struct drm_device;
struct drm_mode_create_dumb;
struct drm_printer;
struct sg_table;
@@ -54,8 +56,8 @@ struct drm_gem_shmem_object {
* @madv: State for madvise
*
* 0 is active/inuse.
+ * 1 is not-needed/can-be-purged
* A negative value is the object is purged.
- * Positive values are driver specific and not used by the helpers.
*/
int madv;

@@ -102,6 +104,14 @@ struct drm_gem_shmem_object {
* @map_wc: map object write-combined (instead of using shmem defaults).
*/
bool map_wc : 1;
+
+ /**
+ * @evicted: True if shmem pages are evicted by the memory shrinker.
+ * Used internally by memory shrinker. The evicted pages can be
+ * moved back to memory using drm_gem_shmem_swapin_locked(), unlike
+ * the purged pages (madv < 0) that are destroyed permanently.
+ */
+ bool evicted : 1;
};

#define to_drm_gem_shmem_obj(obj) \
@@ -122,14 +132,19 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem,
int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma);

int drm_gem_shmem_madvise_locked(struct drm_gem_shmem_object *shmem, int madv);
+int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv);

static inline bool drm_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem)
{
- return (shmem->madv > 0) &&
- !refcount_read(&shmem->pages_pin_count) && shmem->sgt &&
+ return (shmem->madv > 0) && shmem->base.funcs->evict &&
+ refcount_read(&shmem->pages_use_count) &&
+ !refcount_read(&shmem->pages_pin_count) &&
!shmem->base.dma_buf && !shmem->base.import_attach;
}

+int drm_gem_shmem_swapin_locked(struct drm_gem_shmem_object *shmem);
+
+void drm_gem_shmem_evict_locked(struct drm_gem_shmem_object *shmem);
void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem);

struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem);
@@ -273,6 +288,53 @@ static inline int drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct v
return drm_gem_shmem_mmap(shmem, vma);
}

+/**
+ * drm_gem_shmem_object_madvise - unlocked GEM object function for drm_gem_shmem_madvise_locked()
+ * @obj: GEM object
+ * @madv: Madvise value
+ *
+ * This function wraps drm_gem_shmem_madvise_locked(), providing unlocked variant.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+static inline int drm_gem_shmem_object_madvise(struct drm_gem_object *obj, int madv)
+{
+ struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+ return drm_gem_shmem_madvise(shmem, madv);
+}
+
+/**
+ * struct drm_gem_shmem_shrinker - Memory shrinker of GEM shmem memory manager
+ */
+struct drm_gem_shmem_shrinker {
+ /** @base: Shrinker for purging shmem GEM objects */
+ struct shrinker base;
+
+ /** @lock: Protects @lru_* */
+ struct mutex lock;
+
+ /** @lru_pinned: List of pinned shmem GEM objects */
+ struct drm_gem_lru lru_pinned;
+
+ /** @lru_evictable: List of shmem GEM objects to be evicted */
+ struct drm_gem_lru lru_evictable;
+
+ /** @lru_evicted: List of evicted shmem GEM objects */
+ struct drm_gem_lru lru_evicted;
+};
+
+/**
+ * struct drm_gem_shmem - GEM shmem memory manager
+ */
+struct drm_gem_shmem {
+ /** @shrinker: GEM shmem shrinker */
+ struct drm_gem_shmem_shrinker shrinker;
+};
+
+int drmm_gem_shmem_init(struct drm_device *dev);
+
/*
* Driver ops
*/
--
2.41.0

2023-10-29 23:22:00

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 24/26] drm/virtio: Attach shmem BOs dynamically

Prepare for addition of memory shrinker support by attaching shmem pages
to host dynamically on first use. Previously the attachment vq command
wasn't fenced and there was no vq kick made in the BO creation code path,
hence the attachment already was happening dynamically, but implicitly.
Making attachment explicitly dynamic will allow to simplify and reuse more
code when shrinker will be added. The virtio_gpu_object_shmem_init() now
works under the held reservation lock, which will be important to have for
shrinker to avoid moving pages while they are in active use by the driver.

Acked-by: Gerd Hoffmann <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/virtio/virtgpu_drv.h | 7 +++
drivers/gpu/drm/virtio/virtgpu_gem.c | 26 +++++++++
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 32 +++++++----
drivers/gpu/drm/virtio/virtgpu_object.c | 73 ++++++++++++++++++++-----
drivers/gpu/drm/virtio/virtgpu_submit.c | 15 ++++-
5 files changed, 125 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 56269814fb6d..421f524ae1de 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -89,6 +89,7 @@ struct virtio_gpu_object {
uint32_t hw_res_handle;
bool dumb;
bool created;
+ bool detached;
bool host3d_blob, guest_blob;
uint32_t blob_mem, blob_flags;

@@ -313,6 +314,8 @@ void virtio_gpu_array_put_free(struct virtio_gpu_object_array *objs);
void virtio_gpu_array_put_free_delayed(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_array *objs);
void virtio_gpu_array_put_free_work(struct work_struct *work);
+int virtio_gpu_array_prepare(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object_array *objs);
int virtio_gpu_gem_pin(struct virtio_gpu_object *bo);
void virtio_gpu_gem_unpin(struct virtio_gpu_object *bo);

@@ -453,6 +456,10 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,

bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo);

+int virtio_gpu_reattach_shmem_object_locked(struct virtio_gpu_object *bo);
+
+int virtio_gpu_reattach_shmem_object(struct virtio_gpu_object *bo);
+
int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
uint32_t *resid);
/* virtgpu_prime.c */
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 625c05d625bf..97e67064c97e 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -295,6 +295,26 @@ void virtio_gpu_array_put_free_work(struct work_struct *work)
spin_unlock(&vgdev->obj_free_lock);
}

+int virtio_gpu_array_prepare(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object_array *objs)
+{
+ struct virtio_gpu_object *bo;
+ int ret = 0;
+ u32 i;
+
+ for (i = 0; i < objs->nents; i++) {
+ bo = gem_to_virtio_gpu_obj(objs->objs[i]);
+
+ if (virtio_gpu_is_shmem(bo) && bo->detached) {
+ ret = virtio_gpu_reattach_shmem_object_locked(bo);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
int virtio_gpu_gem_pin(struct virtio_gpu_object *bo)
{
int err;
@@ -303,6 +323,12 @@ int virtio_gpu_gem_pin(struct virtio_gpu_object *bo)
err = drm_gem_shmem_pin(&bo->base);
if (err)
return err;
+
+ err = virtio_gpu_reattach_shmem_object(bo);
+ if (err) {
+ drm_gem_shmem_unpin(&bo->base);
+ return err;
+ }
}

return 0;
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index b24b11f25197..070c29cea26a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -246,6 +246,10 @@ static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev,
if (ret != 0)
goto err_put_free;

+ ret = virtio_gpu_array_prepare(vgdev, objs);
+ if (ret)
+ goto err_unlock;
+
fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0);
if (!fence) {
ret = -ENOMEM;
@@ -288,11 +292,25 @@ static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
goto err_put_free;
}

+ ret = virtio_gpu_array_lock_resv(objs);
+ if (ret != 0)
+ goto err_put_free;
+
+ ret = virtio_gpu_array_prepare(vgdev, objs);
+ if (ret)
+ goto err_unlock;
+
+ fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0);
+ if (!fence) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+
if (!vgdev->has_virgl_3d) {
virtio_gpu_cmd_transfer_to_host_2d
(vgdev, offset,
args->box.w, args->box.h, args->box.x, args->box.y,
- objs, NULL);
+ objs, fence);
} else {
virtio_gpu_create_context(dev, file);

@@ -301,23 +319,13 @@ static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data,
goto err_put_free;
}

- ret = virtio_gpu_array_lock_resv(objs);
- if (ret != 0)
- goto err_put_free;
-
- ret = -ENOMEM;
- fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context,
- 0);
- if (!fence)
- goto err_unlock;
-
virtio_gpu_cmd_transfer_to_host_3d
(vgdev,
vfpriv ? vfpriv->ctx_id : 0, offset, args->level,
args->stride, args->layer_stride, &args->box, objs,
fence);
- dma_fence_put(&fence->f);
}
+ dma_fence_put(&fence->f);
virtio_gpu_notify(vgdev);
return 0;

diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 998f8b05ceb1..000bb7955a57 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -143,7 +143,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
struct sg_table *pages;
int si;

- pages = drm_gem_shmem_get_pages_sgt(&bo->base);
+ pages = drm_gem_shmem_get_pages_sgt_locked(&bo->base);
if (IS_ERR(pages))
return PTR_ERR(pages);

@@ -177,6 +177,40 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
return 0;
}

+int virtio_gpu_reattach_shmem_object_locked(struct virtio_gpu_object *bo)
+{
+ struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
+ struct virtio_gpu_mem_entry *ents;
+ unsigned int nents;
+ int err;
+
+ if (!bo->detached)
+ return 0;
+
+ err = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents);
+ if (err)
+ return err;
+
+ virtio_gpu_object_attach(vgdev, bo, ents, nents);
+
+ bo->detached = false;
+
+ return 0;
+}
+
+int virtio_gpu_reattach_shmem_object(struct virtio_gpu_object *bo)
+{
+ int ret;
+
+ ret = dma_resv_lock_interruptible(bo->base.base.resv, NULL);
+ if (ret)
+ return ret;
+ ret = virtio_gpu_reattach_shmem_object_locked(bo);
+ dma_resv_unlock(bo->base.base.resv);
+
+ return ret;
+}
+
int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_params *params,
struct virtio_gpu_object **bo_ptr,
@@ -207,45 +241,56 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,

bo->dumb = params->dumb;

- ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents);
- if (ret != 0)
- goto err_put_id;
+ if (bo->blob_mem == VIRTGPU_BLOB_MEM_GUEST)
+ bo->guest_blob = true;

if (fence) {
ret = -ENOMEM;
objs = virtio_gpu_array_alloc(1);
if (!objs)
- goto err_free_entry;
+ goto err_put_id;
virtio_gpu_array_add_obj(objs, &bo->base.base);

ret = virtio_gpu_array_lock_resv(objs);
if (ret != 0)
goto err_put_objs;
+ } else {
+ ret = dma_resv_lock(bo->base.base.resv, NULL);
+ if (ret)
+ goto err_put_id;
}

if (params->blob) {
- if (params->blob_mem == VIRTGPU_BLOB_MEM_GUEST)
- bo->guest_blob = true;
+ ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents);
+ if (ret)
+ goto err_unlock_objs;
+ } else {
+ bo->detached = true;
+ }

+ if (params->blob)
virtio_gpu_cmd_resource_create_blob(vgdev, bo, params,
ents, nents);
- } else if (params->virgl) {
+ else if (params->virgl)
virtio_gpu_cmd_resource_create_3d(vgdev, bo, params,
objs, fence);
- virtio_gpu_object_attach(vgdev, bo, ents, nents);
- } else {
+ else
virtio_gpu_cmd_create_resource(vgdev, bo, params,
objs, fence);
- virtio_gpu_object_attach(vgdev, bo, ents, nents);
- }
+
+ if (!fence)
+ dma_resv_unlock(bo->base.base.resv);

*bo_ptr = bo;
return 0;

+err_unlock_objs:
+ if (fence)
+ virtio_gpu_array_unlock_resv(objs);
+ else
+ dma_resv_unlock(bo->base.base.resv);
err_put_objs:
virtio_gpu_array_put_free(objs);
-err_free_entry:
- kvfree(ents);
err_put_id:
virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle);
err_put_pages:
diff --git a/drivers/gpu/drm/virtio/virtgpu_submit.c b/drivers/gpu/drm/virtio/virtgpu_submit.c
index 5c514946bbad..6e4ef2593e8f 100644
--- a/drivers/gpu/drm/virtio/virtgpu_submit.c
+++ b/drivers/gpu/drm/virtio/virtgpu_submit.c
@@ -464,8 +464,19 @@ static void virtio_gpu_install_out_fence_fd(struct virtio_gpu_submit *submit)

static int virtio_gpu_lock_buflist(struct virtio_gpu_submit *submit)
{
- if (submit->buflist)
- return virtio_gpu_array_lock_resv(submit->buflist);
+ int err;
+
+ if (submit->buflist) {
+ err = virtio_gpu_array_lock_resv(submit->buflist);
+ if (err)
+ return err;
+
+ err = virtio_gpu_array_prepare(submit->vgdev, submit->buflist);
+ if (err) {
+ virtio_gpu_array_unlock_resv(submit->buflist);
+ return err;
+ }
+ }

return 0;
}
--
2.41.0

2023-10-29 23:23:01

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 17/26] drm/v3d: Explicitly get and put drm-shmem pages

To simplify the drm-shmem refcnt handling, we're moving away from
the implicit get_pages() that is used by get_pages_sgt(). From now on
drivers will have to pin pages while they use sgt. V3D driver doesn't
support shrinker, hence pages are pinned and sgt is valid as long as
pages' use-count > 0.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/v3d/v3d_bo.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c
index 42cd874f6810..0597c6b01b6c 100644
--- a/drivers/gpu/drm/v3d/v3d_bo.c
+++ b/drivers/gpu/drm/v3d/v3d_bo.c
@@ -47,6 +47,9 @@ void v3d_free_object(struct drm_gem_object *obj)
/* GPU execution may have dirtied any pages in the BO. */
bo->base.pages_mark_dirty_on_put = true;

+ if (!obj->import_attach)
+ drm_gem_shmem_put_pages(&bo->base);
+
drm_gem_shmem_free(&bo->base);
}

@@ -135,12 +138,18 @@ struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
return ERR_CAST(shmem_obj);
bo = to_v3d_bo(&shmem_obj->base);

- ret = v3d_bo_create_finish(&shmem_obj->base);
+ ret = drm_gem_shmem_get_pages(shmem_obj);
if (ret)
goto free_obj;

+ ret = v3d_bo_create_finish(&shmem_obj->base);
+ if (ret)
+ goto put_pages;
+
return bo;

+put_pages:
+ drm_gem_shmem_put_pages(shmem_obj);
free_obj:
drm_gem_shmem_free(shmem_obj);
return ERR_PTR(ret);
--
2.41.0

2023-10-29 23:23:05

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 18/26] drm/shmem-helper: Change sgt allocation policy

In a preparation to addition of drm-shmem memory shrinker support, change
the SGT allocation policy in this way:

1. SGT can be allocated only if shmem pages are pinned at the
time of allocation, otherwise allocation fails.

2. Drivers must ensure that pages are pinned during the time of SGT usage
and should get new SGT if pages were unpinned.

This new policy is required by the shrinker because it will move pages
to/from SWAP unless pages are pinned, invalidating SGT pointer once pages
are relocated.

Previous patches prepared drivers to the new policy.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 51 +++++++++++++-------------
1 file changed, 26 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index f371ebc6f85c..1420d2166b76 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -133,6 +133,14 @@ drm_gem_shmem_free_pages(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;

+ if (shmem->sgt) {
+ dma_unmap_sgtable(obj->dev->dev, shmem->sgt,
+ DMA_BIDIRECTIONAL, 0);
+ sg_free_table(shmem->sgt);
+ kfree(shmem->sgt);
+ shmem->sgt = NULL;
+ }
+
#ifdef CONFIG_X86
if (shmem->map_wc)
set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
@@ -155,23 +163,12 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;

- if (obj->import_attach) {
+ if (obj->import_attach)
drm_prime_gem_destroy(obj, shmem->sgt);
- } else {
- drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
-
- if (shmem->sgt) {
- dma_unmap_sgtable(obj->dev->dev, shmem->sgt,
- DMA_BIDIRECTIONAL, 0);
- sg_free_table(shmem->sgt);
- kfree(shmem->sgt);
- }
- if (shmem->pages)
- drm_gem_shmem_put_pages_locked(shmem);

- drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
- drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));
- }
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));

drm_gem_object_release(obj);
kfree(shmem);
@@ -705,6 +702,9 @@ struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem)

drm_WARN_ON(obj->dev, obj->import_attach);

+ if (drm_WARN_ON(obj->dev, !shmem->pages))
+ return ERR_PTR(-ENOMEM);
+
return drm_prime_pages_to_sg(obj->dev, shmem->pages, obj->size >> PAGE_SHIFT);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_get_sg_table);
@@ -720,15 +720,10 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_

drm_WARN_ON(obj->dev, obj->import_attach);

- ret = drm_gem_shmem_get_pages_locked(shmem);
- if (ret)
- return ERR_PTR(ret);
-
sgt = drm_gem_shmem_get_sg_table(shmem);
- if (IS_ERR(sgt)) {
- ret = PTR_ERR(sgt);
- goto err_put_pages;
- }
+ if (IS_ERR(sgt))
+ return sgt;
+
/* Map the pages for use by the h/w. */
ret = dma_map_sgtable(obj->dev->dev, sgt, DMA_BIDIRECTIONAL, 0);
if (ret)
@@ -741,8 +736,6 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_
err_free_sgt:
sg_free_table(sgt);
kfree(sgt);
-err_put_pages:
- drm_gem_shmem_put_pages_locked(shmem);
return ERR_PTR(ret);
}

@@ -759,6 +752,14 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_
* and difference between dma-buf imported and natively allocated objects.
* drm_gem_shmem_get_sg_table() should not be directly called by drivers.
*
+ * Drivers should adhere to these SGT usage rules:
+ *
+ * 1. SGT should be allocated only if shmem pages are pinned at the
+ * time of allocation, otherwise allocation will fail.
+ *
+ * 2. Drivers should ensure that pages are pinned during the time of
+ * SGT usage and should get new SGT if pages were unpinned.
+ *
* Returns:
* A pointer to the scatter/gather table of pinned pages or errno on failure.
*/
--
2.41.0

2023-10-29 23:23:16

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 02/26] drm/gem: Add _locked postfix to functions that have unlocked counterpart

Add _locked postfix to drm_gem functions that have unlocked counterpart
functions to make GEM functions naming more consistent and intuitive in
regards to the locking requirements.

Reviewed-by: Boris Brezillon <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem.c | 6 +++---
include/drm/drm_gem.h | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 95327b003692..4523cd40fb2f 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1490,10 +1490,10 @@ drm_gem_lru_scan(struct drm_gem_lru *lru,
EXPORT_SYMBOL(drm_gem_lru_scan);

/**
- * drm_gem_evict - helper to evict backing pages for a GEM object
+ * drm_gem_evict_locked - helper to evict backing pages for a GEM object
* @obj: obj in question
*/
-int drm_gem_evict(struct drm_gem_object *obj)
+int drm_gem_evict_locked(struct drm_gem_object *obj)
{
dma_resv_assert_held(obj->resv);

@@ -1505,4 +1505,4 @@ int drm_gem_evict(struct drm_gem_object *obj)

return 0;
}
-EXPORT_SYMBOL(drm_gem_evict);
+EXPORT_SYMBOL(drm_gem_evict_locked);
diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
index 3daa8db644c3..c55d8571dbb3 100644
--- a/include/drm/drm_gem.h
+++ b/include/drm/drm_gem.h
@@ -551,7 +551,7 @@ unsigned long drm_gem_lru_scan(struct drm_gem_lru *lru,
unsigned long *remaining,
bool (*shrink)(struct drm_gem_object *obj));

-int drm_gem_evict(struct drm_gem_object *obj);
+int drm_gem_evict_locked(struct drm_gem_object *obj);

#ifdef CONFIG_LOCKDEP
/**
--
2.41.0

2023-10-29 23:24:25

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 26/26] drm/panfrost: Switch to generic memory shrinker

Replace Panfrost's custom memory shrinker with a common drm-shmem
memory shrinker.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/panfrost/Makefile | 1 -
drivers/gpu/drm/panfrost/panfrost_device.h | 4 -
drivers/gpu/drm/panfrost/panfrost_drv.c | 27 ++--
drivers/gpu/drm/panfrost/panfrost_gem.c | 34 +++--
drivers/gpu/drm/panfrost/panfrost_gem.h | 9 --
.../gpu/drm/panfrost/panfrost_gem_shrinker.c | 129 ------------------
drivers/gpu/drm/panfrost/panfrost_job.c | 18 ++-
drivers/gpu/drm/panfrost/panfrost_mmu.c | 18 ++-
include/drm/drm_gem_shmem_helper.h | 7 -
9 files changed, 66 insertions(+), 181 deletions(-)
delete mode 100644 drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c

diff --git a/drivers/gpu/drm/panfrost/Makefile b/drivers/gpu/drm/panfrost/Makefile
index 2c01c1e7523e..f2cb1ab0a32d 100644
--- a/drivers/gpu/drm/panfrost/Makefile
+++ b/drivers/gpu/drm/panfrost/Makefile
@@ -5,7 +5,6 @@ panfrost-y := \
panfrost_device.o \
panfrost_devfreq.o \
panfrost_gem.o \
- panfrost_gem_shrinker.o \
panfrost_gpu.o \
panfrost_job.o \
panfrost_mmu.o \
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 1e85656dc2f7..2b24a0d4f85e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -117,10 +117,6 @@ struct panfrost_device {
atomic_t pending;
} reset;

- struct mutex shrinker_lock;
- struct list_head shrinker_list;
- struct shrinker shrinker;
-
struct panfrost_devfreq pfdevfreq;

struct {
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 7f2aba96d5b9..ef520d2cc1d2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -171,7 +171,6 @@ panfrost_lookup_bos(struct drm_device *dev,
break;
}

- atomic_inc(&bo->gpu_usecount);
job->mappings[i] = mapping;
}

@@ -397,7 +396,6 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
{
struct panfrost_file_priv *priv = file_priv->driver_priv;
struct drm_panfrost_madvise *args = data;
- struct panfrost_device *pfdev = dev->dev_private;
struct drm_gem_object *gem_obj;
struct panfrost_gem_object *bo;
int ret = 0;
@@ -410,11 +408,15 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,

bo = to_panfrost_bo(gem_obj);

+ if (bo->is_heap) {
+ args->retained = 1;
+ goto out_put_object;
+ }
+
ret = dma_resv_lock_interruptible(bo->base.base.resv, NULL);
if (ret)
goto out_put_object;

- mutex_lock(&pfdev->shrinker_lock);
mutex_lock(&bo->mappings.lock);
if (args->madv == PANFROST_MADV_DONTNEED) {
struct panfrost_gem_mapping *first;
@@ -440,17 +442,8 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,

args->retained = drm_gem_shmem_madvise_locked(&bo->base, args->madv);

- if (args->retained) {
- if (args->madv == PANFROST_MADV_DONTNEED)
- list_move_tail(&bo->base.madv_list,
- &pfdev->shrinker_list);
- else if (args->madv == PANFROST_MADV_WILLNEED)
- list_del_init(&bo->base.madv_list);
- }
-
out_unlock_mappings:
mutex_unlock(&bo->mappings.lock);
- mutex_unlock(&pfdev->shrinker_lock);
dma_resv_unlock(bo->base.base.resv);
out_put_object:
drm_gem_object_put(gem_obj);
@@ -635,9 +628,6 @@ static int panfrost_probe(struct platform_device *pdev)
ddev->dev_private = pfdev;
pfdev->ddev = ddev;

- mutex_init(&pfdev->shrinker_lock);
- INIT_LIST_HEAD(&pfdev->shrinker_list);
-
err = panfrost_device_init(pfdev);
if (err) {
if (err != -EPROBE_DEFER)
@@ -659,10 +649,14 @@ static int panfrost_probe(struct platform_device *pdev)
if (err < 0)
goto err_out1;

- panfrost_gem_shrinker_init(ddev);
+ err = drmm_gem_shmem_init(ddev);
+ if (err < 0)
+ goto err_out2;

return 0;

+err_out2:
+ drm_dev_unregister(ddev);
err_out1:
pm_runtime_disable(pfdev->dev);
panfrost_device_fini(pfdev);
@@ -678,7 +672,6 @@ static void panfrost_remove(struct platform_device *pdev)
struct drm_device *ddev = pfdev->ddev;

drm_dev_unregister(ddev);
- panfrost_gem_shrinker_cleanup(ddev);

pm_runtime_get_sync(pfdev->dev);
pm_runtime_disable(pfdev->dev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index bb9d43cf7c3c..a6128e32f303 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -19,16 +19,6 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
struct panfrost_gem_object *bo = to_panfrost_bo(obj);
struct panfrost_device *pfdev = obj->dev->dev_private;

- /*
- * Make sure the BO is no longer inserted in the shrinker list before
- * taking care of the destruction itself. If we don't do that we have a
- * race condition between this function and what's done in
- * panfrost_gem_shrinker_scan().
- */
- mutex_lock(&pfdev->shrinker_lock);
- list_del_init(&bo->base.madv_list);
- mutex_unlock(&pfdev->shrinker_lock);
-
/*
* If we still have mappings attached to the BO, there's a problem in
* our refcounting.
@@ -94,7 +84,11 @@ static void panfrost_gem_mapping_release(struct kref *kref)

mapping = container_of(kref, struct panfrost_gem_mapping, refcount);

+ /* shrinker that may purge mapping at the same time */
+ dma_resv_lock(mapping->obj->base.base.resv, NULL);
panfrost_gem_teardown_mapping(mapping);
+ dma_resv_unlock(mapping->obj->base.base.resv);
+
drm_gem_object_put(&mapping->obj->base.base);
panfrost_mmu_ctx_put(mapping->mmu);
kfree(mapping);
@@ -228,6 +222,25 @@ static size_t panfrost_gem_rss(struct drm_gem_object *obj)
return 0;
}

+static int panfrost_shmem_evict(struct drm_gem_object *obj)
+{
+ struct panfrost_gem_object *bo = to_panfrost_bo(obj);
+
+ if (!drm_gem_shmem_is_purgeable(&bo->base))
+ return -EBUSY;
+
+ if (!mutex_trylock(&bo->mappings.lock))
+ return -EBUSY;
+
+ panfrost_gem_teardown_mappings_locked(bo);
+
+ drm_gem_shmem_purge_locked(&bo->base);
+
+ mutex_unlock(&bo->mappings.lock);
+
+ return 0;
+}
+
static const struct drm_gem_object_funcs panfrost_gem_funcs = {
.free = panfrost_gem_free_object,
.open = panfrost_gem_open,
@@ -242,6 +255,7 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = {
.status = panfrost_gem_status,
.rss = panfrost_gem_rss,
.vm_ops = &drm_gem_shmem_vm_ops,
+ .evict = panfrost_shmem_evict,
};

/**
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 13c0a8149c3a..8ddc2d310d29 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -30,12 +30,6 @@ struct panfrost_gem_object {
struct mutex lock;
} mappings;

- /*
- * Count the number of jobs referencing this BO so we don't let the
- * shrinker reclaim this object prematurely.
- */
- atomic_t gpu_usecount;
-
/*
* Object chunk size currently mapped onto physical memory
*/
@@ -86,7 +80,4 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);

-void panfrost_gem_shrinker_init(struct drm_device *dev);
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
-
#endif /* __PANFROST_GEM_H__ */
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
deleted file mode 100644
index 1aa94fff7072..000000000000
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ /dev/null
@@ -1,129 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2019 Arm Ltd.
- *
- * Based on msm_gem_freedreno.c:
- * Copyright (C) 2016 Red Hat
- * Author: Rob Clark <[email protected]>
- */
-
-#include <linux/list.h>
-
-#include <drm/drm_device.h>
-#include <drm/drm_gem_shmem_helper.h>
-
-#include "panfrost_device.h"
-#include "panfrost_gem.h"
-#include "panfrost_mmu.h"
-
-static bool panfrost_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem)
-{
- return (shmem->madv > 0) &&
- !refcount_read(&shmem->pages_pin_count) && shmem->sgt &&
- !shmem->base.dma_buf && !shmem->base.import_attach;
-}
-
-static unsigned long
-panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
-{
- struct panfrost_device *pfdev =
- container_of(shrinker, struct panfrost_device, shrinker);
- struct drm_gem_shmem_object *shmem;
- unsigned long count = 0;
-
- if (!mutex_trylock(&pfdev->shrinker_lock))
- return 0;
-
- list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) {
- if (panfrost_gem_shmem_is_purgeable(shmem))
- count += shmem->base.size >> PAGE_SHIFT;
- }
-
- mutex_unlock(&pfdev->shrinker_lock);
-
- return count;
-}
-
-static bool panfrost_gem_purge(struct drm_gem_object *obj)
-{
- struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
- struct panfrost_gem_object *bo = to_panfrost_bo(obj);
- bool ret = false;
-
- if (atomic_read(&bo->gpu_usecount))
- return false;
-
- if (!mutex_trylock(&bo->mappings.lock))
- return false;
-
- if (!dma_resv_trylock(shmem->base.resv))
- goto unlock_mappings;
-
- panfrost_gem_teardown_mappings_locked(bo);
- drm_gem_shmem_purge_locked(&bo->base);
- ret = true;
-
- dma_resv_unlock(shmem->base.resv);
-
-unlock_mappings:
- mutex_unlock(&bo->mappings.lock);
- return ret;
-}
-
-static unsigned long
-panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
-{
- struct panfrost_device *pfdev =
- container_of(shrinker, struct panfrost_device, shrinker);
- struct drm_gem_shmem_object *shmem, *tmp;
- unsigned long freed = 0;
-
- if (!mutex_trylock(&pfdev->shrinker_lock))
- return SHRINK_STOP;
-
- list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) {
- if (freed >= sc->nr_to_scan)
- break;
- if (drm_gem_shmem_is_purgeable(shmem) &&
- panfrost_gem_purge(&shmem->base)) {
- freed += shmem->base.size >> PAGE_SHIFT;
- list_del_init(&shmem->madv_list);
- }
- }
-
- mutex_unlock(&pfdev->shrinker_lock);
-
- if (freed > 0)
- pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT);
-
- return freed;
-}
-
-/**
- * panfrost_gem_shrinker_init - Initialize panfrost shrinker
- * @dev: DRM device
- *
- * This function registers and sets up the panfrost shrinker.
- */
-void panfrost_gem_shrinker_init(struct drm_device *dev)
-{
- struct panfrost_device *pfdev = dev->dev_private;
- pfdev->shrinker.count_objects = panfrost_gem_shrinker_count;
- pfdev->shrinker.scan_objects = panfrost_gem_shrinker_scan;
- pfdev->shrinker.seeks = DEFAULT_SEEKS;
- WARN_ON(register_shrinker(&pfdev->shrinker, "drm-panfrost"));
-}
-
-/**
- * panfrost_gem_shrinker_cleanup - Clean up panfrost shrinker
- * @dev: DRM device
- *
- * This function unregisters the panfrost shrinker.
- */
-void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
-{
- struct panfrost_device *pfdev = dev->dev_private;
-
- if (pfdev->shrinker.nr_deferred) {
- unregister_shrinker(&pfdev->shrinker);
- }
-}
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index fb16de2d0420..da6be590557f 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -289,6 +289,19 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos,
dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
}

+static int panfrost_objects_prepare(struct drm_gem_object **bos, int bo_count)
+{
+ struct panfrost_gem_object *bo;
+ int ret = 0;
+
+ while (!ret && bo_count--) {
+ bo = to_panfrost_bo(bos[bo_count]);
+ ret = bo->base.madv ? -ENOMEM : 0;
+ }
+
+ return ret;
+}
+
int panfrost_job_push(struct panfrost_job *job)
{
struct panfrost_device *pfdev = job->pfdev;
@@ -300,6 +313,10 @@ int panfrost_job_push(struct panfrost_job *job)
if (ret)
return ret;

+ ret = panfrost_objects_prepare(job->bos, job->bo_count);
+ if (ret)
+ goto unlock;
+
mutex_lock(&pfdev->sched_lock);
drm_sched_job_arm(&job->base);

@@ -341,7 +358,6 @@ static void panfrost_job_cleanup(struct kref *ref)
if (!job->mappings[i])
break;

- atomic_dec(&job->mappings[i]->obj->gpu_usecount);
panfrost_gem_mapping_put(job->mappings[i]);
}
kvfree(job->mappings);
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index ac145a98377b..01cd97011ea5 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -326,6 +326,7 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
struct sg_table *sgt;
int prot = IOMMU_READ | IOMMU_WRITE;
+ int ret = 0;

if (WARN_ON(mapping->active))
return 0;
@@ -333,15 +334,26 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
if (bo->noexec)
prot |= IOMMU_NOEXEC;

+ if (!obj->import_attach) {
+ ret = drm_gem_shmem_pin(shmem);
+ if (ret)
+ return ret;
+ }
+
sgt = drm_gem_shmem_get_pages_sgt(shmem);
- if (WARN_ON(IS_ERR(sgt)))
- return PTR_ERR(sgt);
+ if (WARN_ON(IS_ERR(sgt))) {
+ ret = PTR_ERR(sgt);
+ goto unpin;
+ }

mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
prot, sgt);
mapping->active = true;
+unpin:
+ if (!obj->import_attach)
+ drm_gem_shmem_unpin(shmem);

- return 0;
+ return ret;
}

void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index 6ac77c2082ed..2a506074da46 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -61,13 +61,6 @@ struct drm_gem_shmem_object {
*/
int madv;

- /**
- * @madv_list: List entry for madvise tracking
- *
- * Typically used by drivers to track purgeable objects
- */
- struct list_head madv_list;
-
/**
* @sgt: Scatter/gather table for imported PRIME buffers
*/
--
2.41.0

2023-10-29 23:24:33

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 25/26] drm/virtio: Support shmem shrinking

Support generic drm-shmem memory shrinker and add new madvise IOCTL to
the VirtIO-GPU driver. BO cache manager of Mesa driver will mark BOs as
"don't need" using the new IOCTL to let shrinker purge the marked BOs on
OOM, the shrinker will also evict unpurgeable shmem BOs from memory if
guest supports SWAP file or partition.

Acked-by: Gerd Hoffmann <[email protected]>
Signed-off-by: Daniel Almeida <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/virtio/virtgpu_drv.h | 13 +++++-
drivers/gpu/drm/virtio/virtgpu_gem.c | 35 ++++++++++++++
drivers/gpu/drm/virtio/virtgpu_ioctl.c | 25 ++++++++++
drivers/gpu/drm/virtio/virtgpu_kms.c | 8 ++++
drivers/gpu/drm/virtio/virtgpu_object.c | 61 +++++++++++++++++++++++++
drivers/gpu/drm/virtio/virtgpu_vq.c | 40 ++++++++++++++++
include/uapi/drm/virtgpu_drm.h | 14 ++++++
7 files changed, 195 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 421f524ae1de..33a78b24c272 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -278,7 +278,7 @@ struct virtio_gpu_fpriv {
};

/* virtgpu_ioctl.c */
-#define DRM_VIRTIO_NUM_IOCTLS 12
+#define DRM_VIRTIO_NUM_IOCTLS 13
extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
void virtio_gpu_create_context(struct drm_device *dev, struct drm_file *file);

@@ -316,6 +316,8 @@ void virtio_gpu_array_put_free_delayed(struct virtio_gpu_device *vgdev,
void virtio_gpu_array_put_free_work(struct work_struct *work);
int virtio_gpu_array_prepare(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_array *objs);
+int virtio_gpu_gem_host_mem_release(struct virtio_gpu_object *bo);
+int virtio_gpu_gem_madvise(struct virtio_gpu_object *obj, int madv);
int virtio_gpu_gem_pin(struct virtio_gpu_object *bo);
void virtio_gpu_gem_unpin(struct virtio_gpu_object *bo);

@@ -329,6 +331,8 @@ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
struct virtio_gpu_fence *fence);
void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo);
+int virtio_gpu_cmd_release_resource(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo);
void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
uint64_t offset,
uint32_t width, uint32_t height,
@@ -349,6 +353,9 @@ void virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *obj,
struct virtio_gpu_mem_entry *ents,
unsigned int nents);
+void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *obj,
+ struct virtio_gpu_fence *fence);
void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
struct virtio_gpu_output *output);
int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev);
@@ -492,4 +499,8 @@ void virtio_gpu_vram_unmap_dma_buf(struct device *dev,
int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);

+/* virtgpu_gem_shrinker.c */
+int virtio_gpu_gem_shrinker_init(struct virtio_gpu_device *vgdev);
+void virtio_gpu_gem_shrinker_fini(struct virtio_gpu_device *vgdev);
+
#endif
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index 97e67064c97e..748f7bbb0e6d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -147,10 +147,20 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
struct virtio_gpu_device *vgdev = obj->dev->dev_private;
struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
struct virtio_gpu_object_array *objs;
+ struct virtio_gpu_object *bo;

if (!vgdev->has_virgl_3d)
return;

+ bo = gem_to_virtio_gpu_obj(obj);
+
+ /*
+ * Purged BO was already detached and released, the resource ID
+ * is invalid by now.
+ */
+ if (!virtio_gpu_gem_madvise(bo, VIRTGPU_MADV_WILLNEED))
+ return;
+
objs = virtio_gpu_array_alloc(1);
if (!objs)
return;
@@ -315,6 +325,31 @@ int virtio_gpu_array_prepare(struct virtio_gpu_device *vgdev,
return ret;
}

+int virtio_gpu_gem_madvise(struct virtio_gpu_object *bo, int madv)
+{
+ if (virtio_gpu_is_shmem(bo))
+ return drm_gem_shmem_object_madvise(&bo->base.base, madv);
+
+ return 1;
+}
+
+int virtio_gpu_gem_host_mem_release(struct virtio_gpu_object *bo)
+{
+ struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
+ int err;
+
+ if (bo->created) {
+ err = virtio_gpu_cmd_release_resource(vgdev, bo);
+ if (err)
+ return err;
+
+ virtio_gpu_notify(vgdev);
+ bo->created = false;
+ }
+
+ return 0;
+}
+
int virtio_gpu_gem_pin(struct virtio_gpu_object *bo)
{
int err;
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index 070c29cea26a..44a99166efdc 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -676,6 +676,28 @@ static int virtio_gpu_context_init_ioctl(struct drm_device *dev,
return ret;
}

+static int virtio_gpu_madvise_ioctl(struct drm_device *dev,
+ void *data,
+ struct drm_file *file)
+{
+ struct drm_virtgpu_madvise *args = data;
+ struct virtio_gpu_object *bo;
+ struct drm_gem_object *obj;
+
+ if (args->madv > VIRTGPU_MADV_DONTNEED)
+ return -EOPNOTSUPP;
+
+ obj = drm_gem_object_lookup(file, args->bo_handle);
+ if (!obj)
+ return -ENOENT;
+
+ bo = gem_to_virtio_gpu_obj(obj);
+ args->retained = virtio_gpu_gem_madvise(bo, args->madv);
+ drm_gem_object_put(obj);
+
+ return 0;
+}
+
struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
DRM_IOCTL_DEF_DRV(VIRTGPU_MAP, virtio_gpu_map_ioctl,
DRM_RENDER_ALLOW),
@@ -715,4 +737,7 @@ struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {

DRM_IOCTL_DEF_DRV(VIRTGPU_CONTEXT_INIT, virtio_gpu_context_init_ioctl,
DRM_RENDER_ALLOW),
+
+ DRM_IOCTL_DEF_DRV(VIRTGPU_MADVISE, virtio_gpu_madvise_ioctl,
+ DRM_RENDER_ALLOW),
};
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 5a3b5aaed1f3..43e237082cec 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -245,6 +245,12 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
goto err_scanouts;
}

+ ret = drmm_gem_shmem_init(dev);
+ if (ret) {
+ DRM_ERROR("shmem init failed\n");
+ goto err_modeset;
+ }
+
virtio_device_ready(vgdev->vdev);

if (num_capsets)
@@ -259,6 +265,8 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
}
return 0;

+err_modeset:
+ virtio_gpu_modeset_fini(vgdev);
err_scanouts:
virtio_gpu_free_vbufs(vgdev);
err_vbufs:
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 000bb7955a57..8fa5f912ae51 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -98,6 +98,60 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj)
virtio_gpu_cleanup_object(bo);
}

+static int virtio_gpu_detach_object_fenced(struct virtio_gpu_object *bo)
+{
+ struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
+ struct virtio_gpu_fence *fence;
+
+ if (bo->detached)
+ return 0;
+
+ fence = virtio_gpu_fence_alloc(vgdev, vgdev->fence_drv.context, 0);
+ if (!fence)
+ return -ENOMEM;
+
+ virtio_gpu_object_detach(vgdev, bo, fence);
+ virtio_gpu_notify(vgdev);
+
+ dma_fence_wait(&fence->f, false);
+ dma_fence_put(&fence->f);
+
+ bo->detached = true;
+
+ return 0;
+}
+
+static int virtio_gpu_shmem_evict(struct drm_gem_object *obj)
+{
+ struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
+ int err;
+
+ /* blob is not movable, it's impossible to detach it from host */
+ if (bo->blob_mem)
+ return -EBUSY;
+
+ /*
+ * At first tell host to stop using guest's memory to ensure that
+ * host won't touch the released guest's memory once it's gone.
+ */
+ err = virtio_gpu_detach_object_fenced(bo);
+ if (err)
+ return err;
+
+ if (drm_gem_shmem_is_purgeable(&bo->base)) {
+ err = virtio_gpu_gem_host_mem_release(bo);
+ if (err)
+ return err;
+
+ drm_gem_shmem_purge_locked(&bo->base);
+ } else {
+ bo->base.pages_mark_dirty_on_put = 1;
+ drm_gem_shmem_evict_locked(&bo->base);
+ }
+
+ return 0;
+}
+
static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs = {
.free = virtio_gpu_free_object,
.open = virtio_gpu_gem_object_open,
@@ -111,6 +165,7 @@ static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs = {
.vunmap = drm_gem_shmem_object_vunmap_locked,
.mmap = drm_gem_shmem_object_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
+ .evict = virtio_gpu_shmem_evict,
};

bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo)
@@ -187,6 +242,10 @@ int virtio_gpu_reattach_shmem_object_locked(struct virtio_gpu_object *bo)
if (!bo->detached)
return 0;

+ err = drm_gem_shmem_swapin_locked(&bo->base);
+ if (err)
+ return err;
+
err = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents);
if (err)
return err;
@@ -240,6 +299,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
goto err_put_pages;

bo->dumb = params->dumb;
+ bo->blob_mem = params->blob_mem;
+ bo->blob_flags = params->blob_flags;

if (bo->blob_mem == VIRTGPU_BLOB_MEM_GUEST)
bo->guest_blob = true;
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index b1a00c0c25a7..14ab470f413a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -545,6 +545,21 @@ void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev,
virtio_gpu_cleanup_object(bo);
}

+int virtio_gpu_cmd_release_resource(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *bo)
+{
+ struct virtio_gpu_resource_unref *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF);
+ cmd_p->resource_id = cpu_to_le32(bo->hw_res_handle);
+
+ return virtio_gpu_queue_ctrl_buffer(vgdev, vbuf);
+}
+
void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev,
uint32_t scanout_id, uint32_t resource_id,
uint32_t width, uint32_t height,
@@ -645,6 +660,23 @@ virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev,
virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence);
}

+static void
+virtio_gpu_cmd_resource_detach_backing(struct virtio_gpu_device *vgdev,
+ u32 resource_id,
+ struct virtio_gpu_fence *fence)
+{
+ struct virtio_gpu_resource_attach_backing *cmd_p;
+ struct virtio_gpu_vbuffer *vbuf;
+
+ cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p));
+ memset(cmd_p, 0, sizeof(*cmd_p));
+
+ cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING);
+ cmd_p->resource_id = cpu_to_le32(resource_id);
+
+ virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, fence);
+}
+
static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
struct virtio_gpu_vbuffer *vbuf)
{
@@ -1107,6 +1139,14 @@ void virtio_gpu_object_attach(struct virtio_gpu_device *vgdev,
ents, nents, NULL);
}

+void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev,
+ struct virtio_gpu_object *obj,
+ struct virtio_gpu_fence *fence)
+{
+ virtio_gpu_cmd_resource_detach_backing(vgdev, obj->hw_res_handle,
+ fence);
+}
+
void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev,
struct virtio_gpu_output *output)
{
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
index b1d0e56565bc..4caba71b2740 100644
--- a/include/uapi/drm/virtgpu_drm.h
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -48,6 +48,7 @@ extern "C" {
#define DRM_VIRTGPU_GET_CAPS 0x09
#define DRM_VIRTGPU_RESOURCE_CREATE_BLOB 0x0a
#define DRM_VIRTGPU_CONTEXT_INIT 0x0b
+#define DRM_VIRTGPU_MADVISE 0x0c

#define VIRTGPU_EXECBUF_FENCE_FD_IN 0x01
#define VIRTGPU_EXECBUF_FENCE_FD_OUT 0x02
@@ -211,6 +212,15 @@ struct drm_virtgpu_context_init {
__u64 ctx_set_params;
};

+#define VIRTGPU_MADV_WILLNEED 0
+#define VIRTGPU_MADV_DONTNEED 1
+struct drm_virtgpu_madvise {
+ __u32 bo_handle;
+ __u32 retained; /* out, non-zero if BO can be used */
+ __u32 madv;
+ __u32 pad;
+};
+
/*
* Event code that's given when VIRTGPU_CONTEXT_PARAM_POLL_RINGS_MASK is in
* effect. The event size is sizeof(drm_event), since there is no additional
@@ -261,6 +271,10 @@ struct drm_virtgpu_context_init {
DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_CONTEXT_INIT, \
struct drm_virtgpu_context_init)

+#define DRM_IOCTL_VIRTGPU_MADVISE \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MADVISE, \
+ struct drm_virtgpu_madvise)
+
#if defined(__cplusplus)
}
#endif
--
2.41.0

2023-10-29 23:24:42

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 08/26] drm/shmem-helper: Add and use lockless drm_gem_shmem_get_pages()

Add lockless drm_gem_shmem_get_pages() helper that skips taking reservation
lock if pages_use_count is non-zero, leveraging from atomicity of the
refcount_t. Make drm_gem_shmem_mmap() to utilize the new helper.

Reviewed-by: Boris Brezillon <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 6e02643ed87e..41b749bedb11 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -226,6 +226,20 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

+static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
+{
+ int ret;
+
+ if (refcount_inc_not_zero(&shmem->pages_use_count))
+ return 0;
+
+ dma_resv_lock(shmem->base.resv, NULL);
+ ret = drm_gem_shmem_get_pages_locked(shmem);
+ dma_resv_unlock(shmem->base.resv);
+
+ return ret;
+}
+
static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)
{
int ret;
@@ -609,10 +623,7 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
return ret;
}

- dma_resv_lock(shmem->base.resv, NULL);
- ret = drm_gem_shmem_get_pages_locked(shmem);
- dma_resv_unlock(shmem->base.resv);
-
+ ret = drm_gem_shmem_get_pages(shmem);
if (ret)
return ret;

--
2.41.0

2023-10-29 23:26:21

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 14/26] drm/lima: Explicitly get and put drm-shmem pages

To simplify the drm-shmem refcnt handling, we're moving away from
the implicit get_pages() that is used by get_pages_sgt(). From now on
drivers will have to pin pages while they use sgt. Lima driver doesn't
have shrinker, hence pages are pinned and sgt is valid as long as pages'
use-count > 0.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/lima/lima_gem.c | 18 ++++++++++++++++--
drivers/gpu/drm/lima/lima_gem.h | 1 +
2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c
index 988e74f67465..d255f5775dac 100644
--- a/drivers/gpu/drm/lima/lima_gem.c
+++ b/drivers/gpu/drm/lima/lima_gem.c
@@ -46,6 +46,7 @@ int lima_heap_alloc(struct lima_bo *bo, struct lima_vm *vm)
return -ENOMEM;
}

+ bo->put_pages = true;
bo->base.pages = pages;
refcount_set(&bo->base.pages_use_count, 1);

@@ -115,6 +116,7 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
return PTR_ERR(shmem);

obj = &shmem->base;
+ bo = to_lima_bo(obj);

/* Mali Utgard GPU can only support 32bit address space */
mask = mapping_gfp_mask(obj->filp->f_mapping);
@@ -123,13 +125,19 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
mapping_set_gfp_mask(obj->filp->f_mapping, mask);

if (is_heap) {
- bo = to_lima_bo(obj);
err = lima_heap_alloc(bo, NULL);
if (err)
goto out;
} else {
- struct sg_table *sgt = drm_gem_shmem_get_pages_sgt(shmem);
+ struct sg_table *sgt;
+
+ err = drm_gem_shmem_get_pages(shmem);
+ if (err)
+ goto out;
+
+ bo->put_pages = true;

+ sgt = drm_gem_shmem_get_pages_sgt(shmem);
if (IS_ERR(sgt)) {
err = PTR_ERR(sgt);
goto out;
@@ -139,6 +147,9 @@ int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
err = drm_gem_handle_create(file, obj, handle);

out:
+ if (err && bo->put_pages)
+ drm_gem_shmem_put_pages(shmem);
+
/* drop reference from allocate - handle holds it now */
drm_gem_object_put(obj);

@@ -152,6 +163,9 @@ static void lima_gem_free_object(struct drm_gem_object *obj)
if (!list_empty(&bo->va))
dev_err(obj->dev->dev, "lima gem free bo still has va\n");

+ if (bo->put_pages)
+ drm_gem_shmem_put_pages(&bo->base);
+
drm_gem_shmem_free(&bo->base);
}

diff --git a/drivers/gpu/drm/lima/lima_gem.h b/drivers/gpu/drm/lima/lima_gem.h
index ccea06142f4b..dc5a6d465c80 100644
--- a/drivers/gpu/drm/lima/lima_gem.h
+++ b/drivers/gpu/drm/lima/lima_gem.h
@@ -16,6 +16,7 @@ struct lima_bo {
struct list_head va;

size_t heap_size;
+ bool put_pages;
};

static inline struct lima_bo *
--
2.41.0

2023-10-29 23:26:51

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 13/26] drm/shmem-helper: Add drm_gem_shmem_put_pages()

We're going to move away from having implicit get_pages() done by
get_pages_sgt() to ease simplify refcnt handling. Drivers will manage
get/put_pages() by themselves. Add drm_gem_shmem_put_pages().

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 20 ++++++++++++++++++++
include/drm/drm_gem_shmem_helper.h | 1 +
2 files changed, 21 insertions(+)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index ca6f422c0dfc..f371ebc6f85c 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -217,6 +217,7 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
* @shmem: shmem GEM object
*
* This function decreases the use count and puts the backing pages when use drops to zero.
+ * Caller must hold GEM's reservation lock.
*/
void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
{
@@ -227,6 +228,25 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

+/*
+ * drm_gem_shmem_put_pages - Decrease use count on the backing pages for a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This function decreases the use count and puts the backing pages when use drops to zero.
+ * It's unlocked version of drm_gem_shmem_put_pages_locked(), caller must not hold
+ * GEM's reservation lock.
+ */
+void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
+{
+ if (refcount_dec_not_one(&shmem->pages_use_count))
+ return;
+
+ dma_resv_lock(shmem->base.resv, NULL);
+ drm_gem_shmem_put_pages_locked(shmem);
+ dma_resv_unlock(shmem->base.resv);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages);
+
/*
* drm_gem_shmem_get_pages - Increase use count on the backing pages for a shmem GEM object
* @shmem: shmem GEM object
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index 45cd293e10a4..6aad3e27d7ee 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -111,6 +111,7 @@ struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t
void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem);

int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem);
+void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem);
void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem);
int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem);
void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem);
--
2.41.0

2023-10-29 23:36:16

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 10/26] drm/shmem-helper: Use refcount_t for vmap_use_count

Use refcount_t helper for vmap_use_count to make refcounting consistent
with pages_use_count and pages_pin_count that use refcount_t. This also
makes vmapping to benefit from the refcount_t's overflow checks.

Reviewed-by: Boris Brezillon <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 28 +++++++++++---------------
include/drm/drm_gem_shmem_helper.h | 2 +-
2 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 6f963c2c1ecc..08b5a57c59d8 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -144,7 +144,7 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
} else {
dma_resv_lock(shmem->base.resv, NULL);

- drm_WARN_ON(obj->dev, shmem->vmap_use_count);
+ drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));

if (shmem->sgt) {
dma_unmap_sgtable(obj->dev->dev, shmem->sgt,
@@ -344,23 +344,25 @@ int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem,

dma_resv_assert_held(shmem->base.resv);

- if (shmem->vmap_use_count++ > 0) {
+ if (refcount_inc_not_zero(&shmem->vmap_use_count)) {
iosys_map_set_vaddr(map, shmem->vaddr);
return 0;
}

ret = drm_gem_shmem_pin_locked(shmem);
if (ret)
- goto err_zero_use;
+ return ret;

if (shmem->map_wc)
prot = pgprot_writecombine(prot);
shmem->vaddr = vmap(shmem->pages, obj->size >> PAGE_SHIFT,
VM_MAP, prot);
- if (!shmem->vaddr)
+ if (!shmem->vaddr) {
ret = -ENOMEM;
- else
+ } else {
iosys_map_set_vaddr(map, shmem->vaddr);
+ refcount_set(&shmem->vmap_use_count, 1);
+ }
}

if (ret) {
@@ -373,8 +375,6 @@ int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem,
err_put_pages:
if (!obj->import_attach)
drm_gem_shmem_unpin_locked(shmem);
-err_zero_use:
- shmem->vmap_use_count = 0;

return ret;
}
@@ -402,14 +402,10 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem,
} else {
dma_resv_assert_held(shmem->base.resv);

- if (drm_WARN_ON_ONCE(obj->dev, !shmem->vmap_use_count))
- return;
-
- if (--shmem->vmap_use_count > 0)
- return;
-
- vunmap(shmem->vaddr);
- drm_gem_shmem_unpin_locked(shmem);
+ if (refcount_dec_and_test(&shmem->vmap_use_count)) {
+ vunmap(shmem->vaddr);
+ drm_gem_shmem_unpin_locked(shmem);
+ }
}

shmem->vaddr = NULL;
@@ -655,7 +651,7 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,

drm_printf_indent(p, indent, "pages_pin_count=%u\n", refcount_read(&shmem->pages_pin_count));
drm_printf_indent(p, indent, "pages_use_count=%u\n", refcount_read(&shmem->pages_use_count));
- drm_printf_indent(p, indent, "vmap_use_count=%u\n", shmem->vmap_use_count);
+ drm_printf_indent(p, indent, "vmap_use_count=%u\n", refcount_read(&shmem->vmap_use_count));
drm_printf_indent(p, indent, "vaddr=%p\n", shmem->vaddr);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_print_info);
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index a6de11001048..e7b3f4c02bf5 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -82,7 +82,7 @@ struct drm_gem_shmem_object {
* Reference count on the virtual address.
* The address are un-mapped when the count reaches zero.
*/
- unsigned int vmap_use_count;
+ refcount_t vmap_use_count;

/**
* @pages_mark_dirty_on_put:
--
2.41.0

2023-10-29 23:36:42

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 01/26] drm/gem: Change locked/unlocked postfix of drm_gem_v/unmap() function names

Make drm/gem API function names consistent by having locked function
use the _locked postfix in the name, while the unlocked variants don't
use the _unlocked postfix. Rename drm_gem_v/unmap() function names to
make them consistent with the rest of the API functions.

Reviewed-by: Boris Brezillon <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_client.c | 6 +++---
drivers/gpu/drm/drm_gem.c | 20 ++++++++++----------
drivers/gpu/drm/drm_gem_framebuffer_helper.c | 6 +++---
drivers/gpu/drm/drm_internal.h | 4 ++--
drivers/gpu/drm/drm_prime.c | 4 ++--
drivers/gpu/drm/lima/lima_sched.c | 4 ++--
drivers/gpu/drm/panfrost/panfrost_dump.c | 4 ++--
drivers/gpu/drm/panfrost/panfrost_perfcnt.c | 6 +++---
include/drm/drm_gem.h | 4 ++--
9 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 2762572f286e..c935db1ba918 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -265,7 +265,7 @@ void drm_client_dev_restore(struct drm_device *dev)
static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
{
if (buffer->gem) {
- drm_gem_vunmap_unlocked(buffer->gem, &buffer->map);
+ drm_gem_vunmap(buffer->gem, &buffer->map);
drm_gem_object_put(buffer->gem);
}

@@ -349,7 +349,7 @@ drm_client_buffer_vmap(struct drm_client_buffer *buffer,
* fd_install step out of the driver backend hooks, to make that
* final step optional for internal users.
*/
- ret = drm_gem_vmap_unlocked(buffer->gem, map);
+ ret = drm_gem_vmap(buffer->gem, map);
if (ret)
return ret;

@@ -371,7 +371,7 @@ void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
{
struct iosys_map *map = &buffer->map;

- drm_gem_vunmap_unlocked(buffer->gem, map);
+ drm_gem_vunmap(buffer->gem, map);
}
EXPORT_SYMBOL(drm_client_buffer_vunmap);

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 44a948b80ee1..95327b003692 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1175,7 +1175,7 @@ void drm_gem_unpin(struct drm_gem_object *obj)
obj->funcs->unpin(obj);
}

-int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+int drm_gem_vmap_locked(struct drm_gem_object *obj, struct iosys_map *map)
{
int ret;

@@ -1192,9 +1192,9 @@ int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)

return 0;
}
-EXPORT_SYMBOL(drm_gem_vmap);
+EXPORT_SYMBOL(drm_gem_vmap_locked);

-void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
+void drm_gem_vunmap_locked(struct drm_gem_object *obj, struct iosys_map *map)
{
dma_resv_assert_held(obj->resv);

@@ -1207,27 +1207,27 @@ void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
/* Always set the mapping to NULL. Callers may rely on this. */
iosys_map_clear(map);
}
-EXPORT_SYMBOL(drm_gem_vunmap);
+EXPORT_SYMBOL(drm_gem_vunmap_locked);

-int drm_gem_vmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map)
+int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
int ret;

dma_resv_lock(obj->resv, NULL);
- ret = drm_gem_vmap(obj, map);
+ ret = drm_gem_vmap_locked(obj, map);
dma_resv_unlock(obj->resv);

return ret;
}
-EXPORT_SYMBOL(drm_gem_vmap_unlocked);
+EXPORT_SYMBOL(drm_gem_vmap);

-void drm_gem_vunmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map)
+void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
{
dma_resv_lock(obj->resv, NULL);
- drm_gem_vunmap(obj, map);
+ drm_gem_vunmap_locked(obj, map);
dma_resv_unlock(obj->resv);
}
-EXPORT_SYMBOL(drm_gem_vunmap_unlocked);
+EXPORT_SYMBOL(drm_gem_vunmap);

/**
* drm_gem_lock_reservations - Sets up the ww context and acquires
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
index 3bdb6ba37ff4..3808f47310bf 100644
--- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c
+++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
@@ -362,7 +362,7 @@ int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map,
ret = -EINVAL;
goto err_drm_gem_vunmap;
}
- ret = drm_gem_vmap_unlocked(obj, &map[i]);
+ ret = drm_gem_vmap(obj, &map[i]);
if (ret)
goto err_drm_gem_vunmap;
}
@@ -384,7 +384,7 @@ int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map,
obj = drm_gem_fb_get_obj(fb, i);
if (!obj)
continue;
- drm_gem_vunmap_unlocked(obj, &map[i]);
+ drm_gem_vunmap(obj, &map[i]);
}
return ret;
}
@@ -411,7 +411,7 @@ void drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map)
continue;
if (iosys_map_is_null(&map[i]))
continue;
- drm_gem_vunmap_unlocked(obj, &map[i]);
+ drm_gem_vunmap(obj, &map[i]);
}
}
EXPORT_SYMBOL(drm_gem_fb_vunmap);
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 8462b657c375..61179f89a941 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -177,8 +177,8 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent,

int drm_gem_pin(struct drm_gem_object *obj);
void drm_gem_unpin(struct drm_gem_object *obj);
-int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map);
-void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
+int drm_gem_vmap_locked(struct drm_gem_object *obj, struct iosys_map *map);
+void drm_gem_vunmap_locked(struct drm_gem_object *obj, struct iosys_map *map);

/* drm_debugfs.c drm_debugfs_crc.c */
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 63b709a67471..57ac5623f09a 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -682,7 +682,7 @@ int drm_gem_dmabuf_vmap(struct dma_buf *dma_buf, struct iosys_map *map)
{
struct drm_gem_object *obj = dma_buf->priv;

- return drm_gem_vmap(obj, map);
+ return drm_gem_vmap_locked(obj, map);
}
EXPORT_SYMBOL(drm_gem_dmabuf_vmap);

@@ -698,7 +698,7 @@ void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map)
{
struct drm_gem_object *obj = dma_buf->priv;

- drm_gem_vunmap(obj, map);
+ drm_gem_vunmap_locked(obj, map);
}
EXPORT_SYMBOL(drm_gem_dmabuf_vunmap);

diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c
index ffd91a5ee299..843487128544 100644
--- a/drivers/gpu/drm/lima/lima_sched.c
+++ b/drivers/gpu/drm/lima/lima_sched.c
@@ -371,7 +371,7 @@ static void lima_sched_build_error_task_list(struct lima_sched_task *task)
} else {
buffer_chunk->size = lima_bo_size(bo);

- ret = drm_gem_vmap_unlocked(&bo->base.base, &map);
+ ret = drm_gem_vmap(&bo->base.base, &map);
if (ret) {
kvfree(et);
goto out;
@@ -379,7 +379,7 @@ static void lima_sched_build_error_task_list(struct lima_sched_task *task)

memcpy(buffer_chunk + 1, map.vaddr, buffer_chunk->size);

- drm_gem_vunmap_unlocked(&bo->base.base, &map);
+ drm_gem_vunmap(&bo->base.base, &map);
}

buffer_chunk = (void *)(buffer_chunk + 1) + buffer_chunk->size;
diff --git a/drivers/gpu/drm/panfrost/panfrost_dump.c b/drivers/gpu/drm/panfrost/panfrost_dump.c
index e7942ac449c6..0f30bbea9895 100644
--- a/drivers/gpu/drm/panfrost/panfrost_dump.c
+++ b/drivers/gpu/drm/panfrost/panfrost_dump.c
@@ -209,7 +209,7 @@ void panfrost_core_dump(struct panfrost_job *job)
goto dump_header;
}

- ret = drm_gem_vmap_unlocked(&bo->base.base, &map);
+ ret = drm_gem_vmap(&bo->base.base, &map);
if (ret) {
dev_err(pfdev->dev, "Panfrost Dump: couldn't map Buffer Object\n");
iter.hdr->bomap.valid = 0;
@@ -236,7 +236,7 @@ void panfrost_core_dump(struct panfrost_job *job)
vaddr = map.vaddr;
memcpy(iter.data, vaddr, bo->base.base.size);

- drm_gem_vunmap_unlocked(&bo->base.base, &map);
+ drm_gem_vunmap(&bo->base.base, &map);

iter.hdr->bomap.valid = 1;

diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index ba9b6e2b2636..52befead08c6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -106,7 +106,7 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
goto err_close_bo;
}

- ret = drm_gem_vmap_unlocked(&bo->base, &map);
+ ret = drm_gem_vmap(&bo->base, &map);
if (ret)
goto err_put_mapping;
perfcnt->buf = map.vaddr;
@@ -165,7 +165,7 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
return 0;

err_vunmap:
- drm_gem_vunmap_unlocked(&bo->base, &map);
+ drm_gem_vunmap(&bo->base, &map);
err_put_mapping:
panfrost_gem_mapping_put(perfcnt->mapping);
err_close_bo:
@@ -195,7 +195,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF));

perfcnt->user = NULL;
- drm_gem_vunmap_unlocked(&perfcnt->mapping->obj->base.base, &map);
+ drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map);
perfcnt->buf = NULL;
panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv);
panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
index 16364487fde9..3daa8db644c3 100644
--- a/include/drm/drm_gem.h
+++ b/include/drm/drm_gem.h
@@ -527,8 +527,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj);
void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages,
bool dirty, bool accessed);

-int drm_gem_vmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map);
-void drm_gem_vunmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map);
+int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map);
+void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map);

int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles,
int count, struct drm_gem_object ***objs_out);
--
2.41.0

2023-10-29 23:37:13

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 12/26] drm/shmem-helper: Make drm_gem_shmem_get_pages() public

We're going to move away from having implicit get_pages() done by
get_pages_sgt() to ease simplify refcnt handling. Drivers will manage
get/put_pages() by themselves. Expose the drm_gem_shmem_get_pages()
in a public drm-shmem API.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 10 +++++++++-
include/drm/drm_gem_shmem_helper.h | 1 +
2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 24ff2b99e75b..ca6f422c0dfc 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -227,7 +227,14 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

-static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
+/*
+ * drm_gem_shmem_get_pages - Increase use count on the backing pages for a shmem GEM object
+ * @shmem: shmem GEM object
+ *
+ * This function Increases the use count and allocates the backing pages if
+ * use-count equals to zero.
+ */
+int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
{
int ret;

@@ -240,6 +247,7 @@ static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)

return ret;
}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_get_pages);

static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)
{
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index e7b3f4c02bf5..45cd293e10a4 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -110,6 +110,7 @@ struct drm_gem_shmem_object {
struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size);
void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem);

+int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem);
void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem);
int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem);
void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem);
--
2.41.0

2023-10-29 23:37:16

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 11/26] drm/shmem-helper: Prepare drm_gem_shmem_free() to shrinker addition

Prepare drm_gem_shmem_free() to addition of memory shrinker support
to drm-shmem by adding and using variant of put_pages() that doesn't
touch reservation lock. Reservation shouldn't be touched because lockdep
will trigger a bogus warning about locking contention with fs_reclaim
code paths that can't happen during the time when GEM is freed and
lockdep doesn't know about that.

Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 35 +++++++++++++-------------
1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 08b5a57c59d8..24ff2b99e75b 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -128,6 +128,22 @@ struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_create);

+static void
+drm_gem_shmem_free_pages(struct drm_gem_shmem_object *shmem)
+{
+ struct drm_gem_object *obj = &shmem->base;
+
+#ifdef CONFIG_X86
+ if (shmem->map_wc)
+ set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
+#endif
+
+ drm_gem_put_pages(obj, shmem->pages,
+ shmem->pages_mark_dirty_on_put,
+ shmem->pages_mark_accessed_on_put);
+ shmem->pages = NULL;
+}
+
/**
* drm_gem_shmem_free - Free resources associated with a shmem GEM object
* @shmem: shmem GEM object to free
@@ -142,8 +158,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
if (obj->import_attach) {
drm_prime_gem_destroy(obj, shmem->sgt);
} else {
- dma_resv_lock(shmem->base.resv, NULL);
-
drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));

if (shmem->sgt) {
@@ -157,8 +171,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)

drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));
-
- dma_resv_unlock(shmem->base.resv);
}

drm_gem_object_release(obj);
@@ -208,21 +220,10 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
*/
void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
{
- struct drm_gem_object *obj = &shmem->base;
-
dma_resv_assert_held(shmem->base.resv);

- if (refcount_dec_and_test(&shmem->pages_use_count)) {
-#ifdef CONFIG_X86
- if (shmem->map_wc)
- set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
-#endif
-
- drm_gem_put_pages(obj, shmem->pages,
- shmem->pages_mark_dirty_on_put,
- shmem->pages_mark_accessed_on_put);
- shmem->pages = NULL;
- }
+ if (refcount_dec_and_test(&shmem->pages_use_count))
+ drm_gem_shmem_free_pages(shmem);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

--
2.41.0

2023-10-29 23:43:09

by Dmitry Osipenko

[permalink] [raw]
Subject: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

Add locked and remove unlocked postfixes from drm-shmem function names,
making names consistent with the drm/gem core code.

Reviewed-by: Boris Brezillon <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
drivers/gpu/drm/lima/lima_gem.c | 8 +--
drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
.../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
include/drm/drm_gem_shmem_helper.h | 36 +++++------
9 files changed, 64 insertions(+), 64 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 0d61f2b3e213..154585ddae08 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
- .vmap = drm_gem_shmem_object_vmap,
- .vunmap = drm_gem_shmem_object_vunmap,
+ .vmap = drm_gem_shmem_object_vmap_locked,
+ .vunmap = drm_gem_shmem_object_vunmap_locked,
.mmap = drm_gem_shmem_object_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
};
@@ -153,7 +153,7 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
kfree(shmem->sgt);
}
if (shmem->pages)
- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);

drm_WARN_ON(obj->dev, shmem->pages_use_count);

@@ -165,7 +165,7 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_free);

-static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
+static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
struct page **pages;
@@ -199,12 +199,12 @@ static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
}

/*
- * drm_gem_shmem_put_pages - Decrease use count on the backing pages for a shmem GEM object
+ * drm_gem_shmem_put_pages_locked - Decrease use count on the backing pages for a shmem GEM object
* @shmem: shmem GEM object
*
* This function decreases the use count and puts the backing pages when use drops to zero.
*/
-void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
+void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;

@@ -226,7 +226,7 @@ void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
shmem->pages_mark_accessed_on_put);
shmem->pages = NULL;
}
-EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);

static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)
{
@@ -234,7 +234,7 @@ static int drm_gem_shmem_pin_locked(struct drm_gem_shmem_object *shmem)

dma_resv_assert_held(shmem->base.resv);

- ret = drm_gem_shmem_get_pages(shmem);
+ ret = drm_gem_shmem_get_pages_locked(shmem);

return ret;
}
@@ -243,7 +243,7 @@ static void drm_gem_shmem_unpin_locked(struct drm_gem_shmem_object *shmem)
{
dma_resv_assert_held(shmem->base.resv);

- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);
}

/**
@@ -293,7 +293,7 @@ void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem)
EXPORT_SYMBOL_GPL(drm_gem_shmem_unpin);

/*
- * drm_gem_shmem_vmap - Create a virtual mapping for a shmem GEM object
+ * drm_gem_shmem_vmap_locked - Create a virtual mapping for a shmem GEM object
* @shmem: shmem GEM object
* @map: Returns the kernel virtual address of the SHMEM GEM object's backing
* store.
@@ -302,13 +302,13 @@ EXPORT_SYMBOL_GPL(drm_gem_shmem_unpin);
* exists for the buffer backing the shmem GEM object. It hides the differences
* between dma-buf imported and natively allocated objects.
*
- * Acquired mappings should be cleaned up by calling drm_gem_shmem_vunmap().
+ * Acquired mappings should be cleaned up by calling drm_gem_shmem_vunmap_locked().
*
* Returns:
* 0 on success or a negative error code on failure.
*/
-int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,
- struct iosys_map *map)
+int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem,
+ struct iosys_map *map)
{
struct drm_gem_object *obj = &shmem->base;
int ret = 0;
@@ -331,7 +331,7 @@ int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,
return 0;
}

- ret = drm_gem_shmem_get_pages(shmem);
+ ret = drm_gem_shmem_get_pages_locked(shmem);
if (ret)
goto err_zero_use;

@@ -354,28 +354,28 @@ int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,

err_put_pages:
if (!obj->import_attach)
- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);
err_zero_use:
shmem->vmap_use_count = 0;

return ret;
}
-EXPORT_SYMBOL_GPL(drm_gem_shmem_vmap);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vmap_locked);

/*
- * drm_gem_shmem_vunmap - Unmap a virtual mapping for a shmem GEM object
+ * drm_gem_shmem_vunmap_locked - Unmap a virtual mapping for a shmem GEM object
* @shmem: shmem GEM object
* @map: Kernel virtual address where the SHMEM GEM object was mapped
*
* This function cleans up a kernel virtual address mapping acquired by
- * drm_gem_shmem_vmap(). The mapping is only removed when the use count drops to
- * zero.
+ * drm_gem_shmem_vmap_locked(). The mapping is only removed when the use count
+ * drops to zero.
*
* This function hides the differences between dma-buf imported and natively
* allocated objects.
*/
-void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem,
- struct iosys_map *map)
+void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem,
+ struct iosys_map *map)
{
struct drm_gem_object *obj = &shmem->base;

@@ -391,12 +391,12 @@ void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem,
return;

vunmap(shmem->vaddr);
- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);
}

shmem->vaddr = NULL;
}
-EXPORT_SYMBOL_GPL(drm_gem_shmem_vunmap);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vunmap_locked);

static int
drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
@@ -424,7 +424,7 @@ drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
/* Update madvise status, returns true if not purged, else
* false or -errno.
*/
-int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv)
+int drm_gem_shmem_madvise_locked(struct drm_gem_shmem_object *shmem, int madv)
{
dma_resv_assert_held(shmem->base.resv);

@@ -435,9 +435,9 @@ int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv)

return (madv >= 0);
}
-EXPORT_SYMBOL_GPL(drm_gem_shmem_madvise);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_madvise_locked);

-void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
+void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
struct drm_device *dev = obj->dev;
@@ -451,7 +451,7 @@ void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
kfree(shmem->sgt);
shmem->sgt = NULL;

- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);

shmem->madv = -1;

@@ -467,7 +467,7 @@ void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)

invalidate_mapping_pages(file_inode(obj->filp)->i_mapping, 0, (loff_t)-1);
}
-EXPORT_SYMBOL_GPL(drm_gem_shmem_purge);
+EXPORT_SYMBOL_GPL(drm_gem_shmem_purge_locked);

/**
* drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
@@ -564,7 +564,7 @@ static void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);

dma_resv_lock(shmem->base.resv, NULL);
- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);
dma_resv_unlock(shmem->base.resv);

drm_gem_vm_close(vma);
@@ -611,7 +611,7 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
}

dma_resv_lock(shmem->base.resv, NULL);
- ret = drm_gem_shmem_get_pages(shmem);
+ ret = drm_gem_shmem_get_pages_locked(shmem);
dma_resv_unlock(shmem->base.resv);

if (ret)
@@ -679,7 +679,7 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_

drm_WARN_ON(obj->dev, obj->import_attach);

- ret = drm_gem_shmem_get_pages(shmem);
+ ret = drm_gem_shmem_get_pages_locked(shmem);
if (ret)
return ERR_PTR(ret);

@@ -701,7 +701,7 @@ static struct sg_table *drm_gem_shmem_get_pages_sgt_locked(struct drm_gem_shmem_
sg_free_table(sgt);
kfree(sgt);
err_put_pages:
- drm_gem_shmem_put_pages(shmem);
+ drm_gem_shmem_put_pages_locked(shmem);
return ERR_PTR(ret);
}

diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c
index 4f9736e5f929..62d4a409faa8 100644
--- a/drivers/gpu/drm/lima/lima_gem.c
+++ b/drivers/gpu/drm/lima/lima_gem.c
@@ -180,7 +180,7 @@ static int lima_gem_pin(struct drm_gem_object *obj)
if (bo->heap_size)
return -EINVAL;

- return drm_gem_shmem_pin(&bo->base);
+ return drm_gem_shmem_object_pin(obj);
}

static int lima_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
@@ -190,7 +190,7 @@ static int lima_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
if (bo->heap_size)
return -EINVAL;

- return drm_gem_shmem_vmap(&bo->base, map);
+ return drm_gem_shmem_object_vmap_locked(obj, map);
}

static int lima_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
@@ -200,7 +200,7 @@ static int lima_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
if (bo->heap_size)
return -EINVAL;

- return drm_gem_shmem_mmap(&bo->base, vma);
+ return drm_gem_shmem_object_mmap(obj, vma);
}

static const struct drm_gem_object_funcs lima_gem_funcs = {
@@ -212,7 +212,7 @@ static const struct drm_gem_object_funcs lima_gem_funcs = {
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
.vmap = lima_gem_vmap,
- .vunmap = drm_gem_shmem_object_vunmap,
+ .vunmap = drm_gem_shmem_object_vunmap_locked,
.mmap = lima_gem_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
};
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index b834777b409b..7f2aba96d5b9 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -438,7 +438,7 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
}
}

- args->retained = drm_gem_shmem_madvise(&bo->base, args->madv);
+ args->retained = drm_gem_shmem_madvise_locked(&bo->base, args->madv);

if (args->retained) {
if (args->madv == PANFROST_MADV_DONTNEED)
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 0cf64456e29a..6b77d8cebcb2 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -192,7 +192,7 @@ static int panfrost_gem_pin(struct drm_gem_object *obj)
if (bo->is_heap)
return -EINVAL;

- return drm_gem_shmem_pin(&bo->base);
+ return drm_gem_shmem_object_pin(obj);
}

static enum drm_gem_object_status panfrost_gem_status(struct drm_gem_object *obj)
@@ -231,8 +231,8 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = {
.pin = panfrost_gem_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
- .vmap = drm_gem_shmem_object_vmap,
- .vunmap = drm_gem_shmem_object_vunmap,
+ .vmap = drm_gem_shmem_object_vmap_locked,
+ .vunmap = drm_gem_shmem_object_vunmap_locked,
.mmap = drm_gem_shmem_object_mmap,
.status = panfrost_gem_status,
.rss = panfrost_gem_rss,
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 6a71a2555f85..72193bd734e1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -52,7 +52,7 @@ static bool panfrost_gem_purge(struct drm_gem_object *obj)
goto unlock_mappings;

panfrost_gem_teardown_mappings_locked(bo);
- drm_gem_shmem_purge(&bo->base);
+ drm_gem_shmem_purge_locked(&bo->base);
ret = true;

dma_resv_unlock(shmem->base.resv);
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 846dd697c410..9fd4a89c52dd 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -536,7 +536,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
err_map:
sg_free_table(sgt);
err_pages:
- drm_gem_shmem_put_pages(&bo->base);
+ drm_gem_shmem_put_pages_locked(&bo->base);
err_unlock:
dma_resv_unlock(obj->resv);
err_bo:
diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c
index 8b3229a37c6d..42cd874f6810 100644
--- a/drivers/gpu/drm/v3d/v3d_bo.c
+++ b/drivers/gpu/drm/v3d/v3d_bo.c
@@ -56,8 +56,8 @@ static const struct drm_gem_object_funcs v3d_gem_funcs = {
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
- .vmap = drm_gem_shmem_object_vmap,
- .vunmap = drm_gem_shmem_object_vunmap,
+ .vmap = drm_gem_shmem_object_vmap_locked,
+ .vunmap = drm_gem_shmem_object_vunmap_locked,
.mmap = drm_gem_shmem_object_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
};
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index c7e74cf13022..ee5d2a70656b 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -106,8 +106,8 @@ static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs = {
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
- .vmap = drm_gem_shmem_object_vmap,
- .vunmap = drm_gem_shmem_object_vunmap,
+ .vmap = drm_gem_shmem_object_vmap_locked,
+ .vunmap = drm_gem_shmem_object_vunmap_locked,
.mmap = drm_gem_shmem_object_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
};
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index bf0c31aa8fbe..6ee4a4046980 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -99,16 +99,16 @@ struct drm_gem_shmem_object {
struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size);
void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem);

-void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem);
+void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem);
int drm_gem_shmem_pin(struct drm_gem_shmem_object *shmem);
void drm_gem_shmem_unpin(struct drm_gem_shmem_object *shmem);
-int drm_gem_shmem_vmap(struct drm_gem_shmem_object *shmem,
- struct iosys_map *map);
-void drm_gem_shmem_vunmap(struct drm_gem_shmem_object *shmem,
- struct iosys_map *map);
+int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem,
+ struct iosys_map *map);
+void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem,
+ struct iosys_map *map);
int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma);

-int drm_gem_shmem_madvise(struct drm_gem_shmem_object *shmem, int madv);
+int drm_gem_shmem_madvise_locked(struct drm_gem_shmem_object *shmem, int madv);

static inline bool drm_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem)
{
@@ -117,7 +117,7 @@ static inline bool drm_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem
!shmem->base.dma_buf && !shmem->base.import_attach;
}

-void drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem);
+void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem);

struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem);
struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem);
@@ -208,22 +208,22 @@ static inline struct sg_table *drm_gem_shmem_object_get_sg_table(struct drm_gem_
}

/*
- * drm_gem_shmem_object_vmap - GEM object function for drm_gem_shmem_vmap()
+ * drm_gem_shmem_object_vmap_locked - GEM object function for drm_gem_shmem_vmap_locked()
* @obj: GEM object
* @map: Returns the kernel virtual address of the SHMEM GEM object's backing store.
*
- * This function wraps drm_gem_shmem_vmap(). Drivers that employ the shmem helpers should
- * use it as their &drm_gem_object_funcs.vmap handler.
+ * This function wraps drm_gem_shmem_vmap_locked(). Drivers that employ the shmem
+ * helpers should use it as their &drm_gem_object_funcs.vmap handler.
*
* Returns:
* 0 on success or a negative error code on failure.
*/
-static inline int drm_gem_shmem_object_vmap(struct drm_gem_object *obj,
- struct iosys_map *map)
+static inline int drm_gem_shmem_object_vmap_locked(struct drm_gem_object *obj,
+ struct iosys_map *map)
{
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);

- return drm_gem_shmem_vmap(shmem, map);
+ return drm_gem_shmem_vmap_locked(shmem, map);
}

/*
@@ -231,15 +231,15 @@ static inline int drm_gem_shmem_object_vmap(struct drm_gem_object *obj,
* @obj: GEM object
* @map: Kernel virtual address where the SHMEM GEM object was mapped
*
- * This function wraps drm_gem_shmem_vunmap(). Drivers that employ the shmem helpers should
- * use it as their &drm_gem_object_funcs.vunmap handler.
+ * This function wraps drm_gem_shmem_vunmap_locked(). Drivers that employ the shmem
+ * helpers should use it as their &drm_gem_object_funcs.vunmap handler.
*/
-static inline void drm_gem_shmem_object_vunmap(struct drm_gem_object *obj,
- struct iosys_map *map)
+static inline void drm_gem_shmem_object_vunmap_locked(struct drm_gem_object *obj,
+ struct iosys_map *map)
{
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);

- drm_gem_shmem_vunmap(shmem, map);
+ drm_gem_shmem_vunmap_locked(shmem, map);
}

/**
--
2.41.0

2023-11-06 02:03:26

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 25/26] drm/virtio: Support shmem shrinking

On 11/4/23 01:55, Gurchetan Singh wrote:
> On Sun, Oct 29, 2023 at 4:03 PM Dmitry Osipenko <
> [email protected]> wrote:
>
>> Support generic drm-shmem memory shrinker and add new madvise IOCTL to
>> the VirtIO-GPU driver. BO cache manager of Mesa driver will mark BOs as
>> "don't need" using the new IOCTL to let shrinker purge the marked BOs on
>> OOM, the shrinker will also evict unpurgeable shmem BOs from memory if
>> guest supports SWAP file or partition.
>>
>> Acked-by: Gerd Hoffmann <[email protected]>
>> Signed-off-by: Daniel Almeida <[email protected]>
>> Signed-off-by: Dmitry Osipenko <[email protected]>
>> ---
>> drivers/gpu/drm/virtio/virtgpu_drv.h | 13 +++++-
>> drivers/gpu/drm/virtio/virtgpu_gem.c | 35 ++++++++++++++
>> drivers/gpu/drm/virtio/virtgpu_ioctl.c | 25 ++++++++++
>> drivers/gpu/drm/virtio/virtgpu_kms.c | 8 ++++
>> drivers/gpu/drm/virtio/virtgpu_object.c | 61 +++++++++++++++++++++++++
>> drivers/gpu/drm/virtio/virtgpu_vq.c | 40 ++++++++++++++++
>> include/uapi/drm/virtgpu_drm.h | 14 ++++++
>> 7 files changed, 195 insertions(+), 1 deletion(-)
...
>
> Link to open-source userspace?

https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15278

I'll add it to the commit message

> Also, don't you need a VIRTGPU_PARAM_MADVISE_SUPPORTED or is the plan to
> use a DRM version?

The ioctl() returns error if DRM_VIRTGPU_MADVISE unsupported, extra
flags not needed by userspace

> Overall, the series for a generic shrinker seems useful for a wide variety
> of DRM drivers, such as Panfrost.
>
> For virtio-gpu, it could be a tad VIRGIL specific: since other context
> types (gfxstream gles, DRM, vk contexts) decrease memory consumption by
> either not allocating shadow buffers for textures/buffers, or using blob
> memory.
>
> Maybe we need to design with blob in mind, since we expect virgl to be
> deprecated. On Android, it basically is with ANGLE and native contexts.
> On Linux, Zink looks good too. Even with memory issues fixed, virgl is
> unattractive compared to those solutions.

We should finish shmem first since started with it, then move to blobs.

My rough idea for the blobs is to use a timer-based approach to avoid
frequent virtio syncing with host that will be bad for performance
otherwise. Virtio-gpu kernel driver will maintain a list of purgeable
blobs and will send the list of idling blobs down to host after a period
of inactivity.

Virgl may be not interesting for CrOS, but Qemu will continue supporting
it. I also expect that today's ARM Chromebooks which use Virgl and only
support GL will continue to use Virgl for the next 4 years.

> Android specific idea: I wonder if we could tie GEM blob buffers usage to
> the lmkd and kill based on that ... ?
>
> https://source.android.com/docs/core/perf/lmkd
>
> Is there GEM buffer accounting infrastructure already?

Are you talking about killing a guest APP that uses host blobs when host
is under pressure? I'm not familiar with how arcvm works, but may assume
that it runs a VM per APP. In that case VM is just another process from
the lmkd perspective that will be taken down on OOM, i.e. blob buffers
already should be seen as a part of a VM's memory by lmkd when they
reside in sysmem.

--
Best regards,
Dmitry

2023-11-29 13:10:08

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, Nov 29, 2023 at 08:53:30AM +0100, Boris Brezillon wrote:
> On Wed, 29 Nov 2023 01:05:14 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
> > On 11/28/23 15:37, Boris Brezillon wrote:
> > > On Tue, 28 Nov 2023 12:14:42 +0100
> > > Maxime Ripard <[email protected]> wrote:
> > >
> > >> Hi,
> > >>
> > >> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> > >>> On Fri, 24 Nov 2023 11:40:06 +0100
> > >>> Maxime Ripard <[email protected]> wrote:
> > >>>
> > >>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > >>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
> > >>>>> making names consistent with the drm/gem core code.
> > >>>>>
> > >>>>> Reviewed-by: Boris Brezillon <[email protected]>
> > >>>>> Suggested-by: Boris Brezillon <[email protected]>
> > >>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
> > >>>>
> > >>>> This contradicts my earlier ack on a patch but...
> > >>>>
> > >>>>> ---
> > >>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > >>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > >>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > >>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > >>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > >>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > >>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > >>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > >>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > >>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
> > >>>>>
> > >>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > >>>>> index 0d61f2b3e213..154585ddae08 100644
> > >>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > >>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > >>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > >>>>> .pin = drm_gem_shmem_object_pin,
> > >>>>> .unpin = drm_gem_shmem_object_unpin,
> > >>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > >>>>> - .vmap = drm_gem_shmem_object_vmap,
> > >>>>> - .vunmap = drm_gem_shmem_object_vunmap,
> > >>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
> > >>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
> > >>>>
> > >>>> While I think we should indeed be consistent with the names, I would
> > >>>> also expect helpers to get the locking right by default.
> > >>>
> > >>> Wait, actually I think this patch does what you suggest already. The
> > >>> _locked() prefix tells the caller: "you should take care of the locking,
> > >>> I expect the lock to be held when this is hook/function is called". So
> > >>> helpers without the _locked() prefix take care of the locking (which I
> > >>> guess matches your 'helpers get the locking right' expectation), and
> > >>> those with the _locked() prefix don't.
> > >>
> > >> What I meant by "getting the locking right" is indeed a bit ambiguous,
> > >> sorry. What I'm trying to say I guess is that, in this particular case,
> > >> I don't think you can expect the vmap implementation to be called with
> > >> or without the locks held. The doc for that function will say that it's
> > >> either one or the other, but not both.
> > >>
> > >> So helpers should follow what is needed to provide a default vmap/vunmap
> > >> implementation, including what locking is expected from a vmap/vunmap
> > >> implementation.
> > >
> > > Hm, yeah, I think that's a matter of taste. When locking is often
> > > deferrable, like it is in DRM, I find it beneficial for funcions and
> > > function pointers to reflect the locking scheme, rather than relying on
> > > people properly reading the doc, especially when this is the only
> > > outlier in the group of drm_gem_object_funcs we already have, and it's
> > > not event documented at the drm_gem_object_funcs level [1] :P.
> > >
> > >>
> > >> If that means that vmap is always called with the locks taken, then
> > >> drm_gem_shmem_object_vmap can just assume that it will be called with
> > >> the locks taken and there's no need to mention it in the name (and you
> > >> can probably sprinkle a couple of lockdep assertion to make sure the
> > >> locking is indeed consistent).
> > >
> > > Things get very confusing when you end up having drm_gem_shmem helpers
> > > that are suffixed with _locked() to encode the fact locking is the
> > > caller's responsibility and no suffix for the
> > > callee-takes-care-of-the-locking semantics, while other helpers that are
> > > not suffixed at all actually implement the
> > > caller-should-take-care-of-the-locking semantics.
> > >
> > >>
> > >>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> > >>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> > >>>> convert whatever function needs to be converted to the unlock suffix so
> > >>>> we get a consistent naming.
> > >>>
> > >>> That would be an _unlocked() prefix if we do it the other way around. I
> > >>> think the main confusion comes from the names of the hooks in
> > >>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> > >>> are called with the GEM resv lock held, and locking is handled by the
> > >>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
> > >>> without the GEM resv lock held, and locking is deferred to the
> > >>> implementation. As I said, I don't mind prefixing hooks/helpers with
> > >>> _unlocked() for those that take care of the locking, and no prefix for
> > >>> those that expects locks to be held, as long as it's consistent, but I
> > >>> just wanted to make sure we're on the same page :-).
> > >>
> > >> What about _nolock then? It's the same number of characters than
> > >> _locked, plus it expresses what the function is (not) doing, not what
> > >> context it's supposed to be called in?
> > >
> > > Just did a quick
> > >
> > > git grep _nolock drivers/gpu/drm
> > >
> > > and it returns zero result, where the _locked/_unlocked pattern seems
> > > to already be widely used. Not saying we shouldn't change that, but it
> > > doesn't feel like a change we should do as part of this series.
> > >
> > > Regards,
> > >
> > > Boris
> > >
> > > [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155
> >
> > I'm fine with dropping the _locked() postfix from the common GEM helpers
> > and documenting the locking rule in drm_gem. Thank you all for the
> > suggestions :)
>
> Sorry to disagree, but I think a proper function name/suffix is
> sometimes worth a few lines of doc. Not saying we should do one or the
> other, I think we should do both. But when I see a function suffixed
> _locked, _unlocked or _nolock, I can immediately tell if this function
> defers the locking to the caller or not, and then go check which lock
> in the function doc.
>
> And the second thing I'm not happy with, is the fact we go back to an
> inconsistent naming in drm_gem_shmem_helper.c, where some functions
> deferring the locking to the caller are suffixed _locked and others are
> not, because ultimately, you need a different name when you expose the
> two variants...

I guess one of the point I was trying to make was also: why do you need
both?

If one is better than the other (whatever better means here), then all
drivers should use it.

The counterpart being that if provided a choice, you can be sure that a
lot of people will get it wrong. The one example I have in mind for
example was the drm_atomic_helper_commit_tail vs
drm_atomic_helper_commit_tail_rpm. The latter is now widely used, and
most of it is cargo-cult.

I think you were referring to the locks being deferred vs taken right
now before, why do we need to have the choice between the two?

Maxime


Attachments:
(No filename) (7.97 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-20 11:02:55

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 11/26] drm/shmem-helper: Prepare drm_gem_shmem_free() to shrinker addition

On 11/10/23 13:16, Boris Brezillon wrote:
> On Mon, 30 Oct 2023 02:01:50 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
>> Prepare drm_gem_shmem_free() to addition of memory shrinker support
>> to drm-shmem by adding and using variant of put_pages() that doesn't
>> touch reservation lock. Reservation shouldn't be touched because lockdep
>> will trigger a bogus warning about locking contention with fs_reclaim
>> code paths that can't happen during the time when GEM is freed and
>> lockdep doesn't know about that.
>>
>> Signed-off-by: Dmitry Osipenko <[email protected]>
>> ---
>> drivers/gpu/drm/drm_gem_shmem_helper.c | 35 +++++++++++++-------------
>> 1 file changed, 18 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> index 08b5a57c59d8..24ff2b99e75b 100644
>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> @@ -128,6 +128,22 @@ struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t
>> }
>> EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
>>
>> +static void
>> +drm_gem_shmem_free_pages(struct drm_gem_shmem_object *shmem)
>> +{
>> + struct drm_gem_object *obj = &shmem->base;
>> +
>> +#ifdef CONFIG_X86
>> + if (shmem->map_wc)
>> + set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
>> +#endif
>> +
>> + drm_gem_put_pages(obj, shmem->pages,
>> + shmem->pages_mark_dirty_on_put,
>> + shmem->pages_mark_accessed_on_put);
>> + shmem->pages = NULL;
>> +}
>> +
>> /**
>> * drm_gem_shmem_free - Free resources associated with a shmem GEM object
>> * @shmem: shmem GEM object to free
>> @@ -142,8 +158,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
>> if (obj->import_attach) {
>> drm_prime_gem_destroy(obj, shmem->sgt);
>> } else {
>> - dma_resv_lock(shmem->base.resv, NULL);
>> -
>> drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
>>
>> if (shmem->sgt) {
>> @@ -157,8 +171,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
>>
> If you drop the dma_resv_lock/unlock(), you should also replace the
> drm_gem_shmem_put_pages_locked() by a drm_gem_shmem_free_pages() in this
> commit.

drm_gem_shmem_put_pages_locked() is exported by a later patch of this
series, it's not worthwhile to remove this function

--
Best regards,
Dmitry

2023-11-20 11:23:37

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 11/26] drm/shmem-helper: Prepare drm_gem_shmem_free() to shrinker addition

On Mon, 20 Nov 2023 14:02:29 +0300
Dmitry Osipenko <[email protected]> wrote:

> On 11/10/23 13:16, Boris Brezillon wrote:
> > On Mon, 30 Oct 2023 02:01:50 +0300
> > Dmitry Osipenko <[email protected]> wrote:
> >
> >> Prepare drm_gem_shmem_free() to addition of memory shrinker support
> >> to drm-shmem by adding and using variant of put_pages() that doesn't
> >> touch reservation lock. Reservation shouldn't be touched because lockdep
> >> will trigger a bogus warning about locking contention with fs_reclaim
> >> code paths that can't happen during the time when GEM is freed and
> >> lockdep doesn't know about that.
> >>
> >> Signed-off-by: Dmitry Osipenko <[email protected]>
> >> ---
> >> drivers/gpu/drm/drm_gem_shmem_helper.c | 35 +++++++++++++-------------
> >> 1 file changed, 18 insertions(+), 17 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >> index 08b5a57c59d8..24ff2b99e75b 100644
> >> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> >> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >> @@ -128,6 +128,22 @@ struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t
> >> }
> >> EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
> >>
> >> +static void
> >> +drm_gem_shmem_free_pages(struct drm_gem_shmem_object *shmem)
> >> +{
> >> + struct drm_gem_object *obj = &shmem->base;
> >> +
> >> +#ifdef CONFIG_X86
> >> + if (shmem->map_wc)
> >> + set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT);
> >> +#endif
> >> +
> >> + drm_gem_put_pages(obj, shmem->pages,
> >> + shmem->pages_mark_dirty_on_put,
> >> + shmem->pages_mark_accessed_on_put);
> >> + shmem->pages = NULL;
> >> +}
> >> +
> >> /**
> >> * drm_gem_shmem_free - Free resources associated with a shmem GEM object
> >> * @shmem: shmem GEM object to free
> >> @@ -142,8 +158,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
> >> if (obj->import_attach) {
> >> drm_prime_gem_destroy(obj, shmem->sgt);
> >> } else {
> >> - dma_resv_lock(shmem->base.resv, NULL);
> >> -
> >> drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
> >>
> >> if (shmem->sgt) {
> >> @@ -157,8 +171,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
> >>
> > If you drop the dma_resv_lock/unlock(), you should also replace the
> > drm_gem_shmem_put_pages_locked() by a drm_gem_shmem_free_pages() in this
> > commit.
>
> drm_gem_shmem_put_pages_locked() is exported by a later patch of this
> series, it's not worthwhile to remove this function

I'm not talking about removing drm_gem_shmem_put_pages_locked(), but
replacing the drm_gem_shmem_put_pages_locked() call you have in
drm_gem_shmem_free() by a drm_gem_shmem_free_pages(), so you don't end
up with a lockdep warning when you stop exactly here in the patch
series, which is important if we want to keep things bisectable.

2023-11-20 11:38:31

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 11/26] drm/shmem-helper: Prepare drm_gem_shmem_free() to shrinker addition

On 11/20/23 14:19, Boris Brezillon wrote:
...
>>>> - dma_resv_lock(shmem->base.resv, NULL);
>>>> -
>>>> drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
>>>>
>>>> if (shmem->sgt) {
>>>> @@ -157,8 +171,6 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
>>>>
>>> If you drop the dma_resv_lock/unlock(), you should also replace the
>>> drm_gem_shmem_put_pages_locked() by a drm_gem_shmem_free_pages() in this
>>> commit.
>>
>> drm_gem_shmem_put_pages_locked() is exported by a later patch of this
>> series, it's not worthwhile to remove this function
>
> I'm not talking about removing drm_gem_shmem_put_pages_locked(), but
> replacing the drm_gem_shmem_put_pages_locked() call you have in
> drm_gem_shmem_free() by a drm_gem_shmem_free_pages(), so you don't end
> up with a lockdep warning when you stop exactly here in the patch
> series, which is important if we want to keep things bisectable.

Indeed, there is assert_locked() there. Thanks for the clarification :)

--
Best regards,
Dmitry

2023-11-22 22:06:39

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

On 11/10/23 13:53, Boris Brezillon wrote:
> Hm, there was no drm_gem_shmem_get_pages_sgt() call here, why should we
> add a drm_gem_shmem_get_pages()? What we should do instead is add a
> drm_gem_shmem_get_pages() for each drm_gem_shmem_get_pages_sgt() we
> have in the driver (in panfrost_mmu_map()), and add
> drm_gem_shmem_put_pages() calls where they are missing
> (panfrost_mmu_unmap()).
>
>> + if (err)
>> + goto err_free;
>> + }
>> +
>> return bo;
>> +
>> +err_free:
>> + drm_gem_shmem_free(&bo->base);
>> +
>> + return ERR_PTR(err);
>> }
>>
>> struct drm_gem_object *
>> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
>> index 770dab1942c2..ac145a98377b 100644
>> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
>> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
>> @@ -504,7 +504,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
>> if (IS_ERR(pages[i])) {
>> ret = PTR_ERR(pages[i]);
>> pages[i] = NULL;
>> - goto err_pages;
>> + goto err_unlock;
>> }
>> }
>>
>> @@ -512,7 +512,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
>> ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
>> NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL);
>> if (ret)
>> - goto err_pages;
>> + goto err_unlock;
> Feels like the panfrost_gem_mapping object should hold a ref on the BO
> pages, not the BO itself, because, ultimately, the user of the BO is
> the GPU. This matches what I was saying about moving get/put_pages() to
> panfrost_mmu_map/unmap(): everytime a panfrost_gem_mapping becomes
> active, to want to take a pages ref, every time it becomes inactive,
> you should release the pages ref.

The panfrost_mmu_unmap() is also used by shrinker when BO is purged. I'm
unhappy with how icky it all becomes if unmap is made to put pages.

Previously map() was implicitly allocating pages with get_sgt() and then
pages were implicitly released by drm_gem_shmem_free(). A non-heap BO is
mapped when it's created by Panfrost, hence the actual lifetime of pages
is kept unchanged by this patch. The implicit allocation is turned into
explicit one, i.e. pages are explicitly allocated before BO is mapped.

--
Best regards,
Dmitry

2023-11-22 22:31:40

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 22/26] drm/shmem-helper: Don't free refcounted GEM

On 11/13/23 12:54, Boris Brezillon wrote:
> On Mon, 30 Oct 2023 02:02:01 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
>> Don't free refcounted shmem object to prevent use-after-free bug that
>> is worse than a memory leak.
>>
>> Signed-off-by: Dmitry Osipenko <[email protected]>
>> ---
>> drivers/gpu/drm/drm_gem_shmem_helper.c | 7 ++++---
>> 1 file changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> index 6dd087f19ea3..4253c367dc07 100644
>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> @@ -203,9 +203,10 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
>> if (obj->import_attach)
>> drm_prime_gem_destroy(obj, shmem->sgt);
>>
>> - drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
>> - drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
>> - drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));
>> + if (drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count)) ||
>> + drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count)) ||
>> + drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count)))
>> + return;
>
> I guess you're worried about ->sgt being referenced by the driver after
> the GEM is destroyed. If we assume drivers don't cache the sgt and
> always call get_pages_sgt() when they need it that shouldn't be an
> issue. What we really don't want to release is the pages themselves,
> but the GPU MMU might still have active mappings pointing to these
> pages.
>
> In any case, I'm not against leaking the GEM object when any of these
> counters are not zero, but can we at least have a comment in the
> code explaining why we're doing that, so people don't have to go look
> at the git history to figure it out.

This patch is a minor improvement, it doesn't address any specific
issue. This should be a common pattern in kernel. If you're giving a
warning and know about the inevitable catastrophe, then avoid it if you can.

Actually, there are other similar cases in drm-shmem that can be improved.

--
Best regards,
Dmitry

2023-11-22 22:41:32

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 24/26] drm/virtio: Attach shmem BOs dynamically

On 11/13/23 12:57, Boris Brezillon wrote:
> On Mon, 30 Oct 2023 02:02:03 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
>> Prepare for addition of memory shrinker support by attaching shmem pages
>> to host dynamically on first use. Previously the attachment vq command
>> wasn't fenced and there was no vq kick made in the BO creation code path,
>> hence the attachment already was happening dynamically, but implicitly.
>> Making attachment explicitly dynamic will allow to simplify and reuse more
>> code when shrinker will be added. The virtio_gpu_object_shmem_init() now
>> works under the held reservation lock, which will be important to have for
>> shrinker to avoid moving pages while they are in active use by the driver.
> Ah, this commit might actually help getting rid of the workaround
> introduced in "drm/shmem-helper: Add common memory shrinker".

Not really. The virtio_gpu_free_object() is unchanged, it's only
allocation that is being deferred and it's only done for a one BO type
(virtio-gpu has multiple BO types).

--
Best regards,
Dmitry

2023-11-22 22:42:31

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 24/26] drm/virtio: Attach shmem BOs dynamically

On 11/23/23 01:37, Dmitry Osipenko wrote:
> On 11/13/23 12:57, Boris Brezillon wrote:
>> On Mon, 30 Oct 2023 02:02:03 +0300
>> Dmitry Osipenko <[email protected]> wrote:
>>
>>> Prepare for addition of memory shrinker support by attaching shmem pages
>>> to host dynamically on first use. Previously the attachment vq command
>>> wasn't fenced and there was no vq kick made in the BO creation code path,
>>> hence the attachment already was happening dynamically, but implicitly.
>>> Making attachment explicitly dynamic will allow to simplify and reuse more
>>> code when shrinker will be added. The virtio_gpu_object_shmem_init() now
>>> works under the held reservation lock, which will be important to have for
>>> shrinker to avoid moving pages while they are in active use by the driver.
>> Ah, this commit might actually help getting rid of the workaround
>> introduced in "drm/shmem-helper: Add common memory shrinker".
>
> Not really. The virtio_gpu_free_object() is unchanged, it's only
> allocation that is being deferred and it's only done for a one BO type
> (virtio-gpu has multiple BO types).

s/allocation/attachment/

Pages are still allocated by virtio_gpu_object_create().

--
Best regards,
Dmitry

2023-11-23 09:06:20

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

On Thu, 23 Nov 2023 01:04:56 +0300
Dmitry Osipenko <[email protected]> wrote:

> On 11/10/23 13:53, Boris Brezillon wrote:
> > Hm, there was no drm_gem_shmem_get_pages_sgt() call here, why should we
> > add a drm_gem_shmem_get_pages()? What we should do instead is add a
> > drm_gem_shmem_get_pages() for each drm_gem_shmem_get_pages_sgt() we
> > have in the driver (in panfrost_mmu_map()), and add
> > drm_gem_shmem_put_pages() calls where they are missing
> > (panfrost_mmu_unmap()).
> >
> >> + if (err)
> >> + goto err_free;
> >> + }
> >> +
> >> return bo;
> >> +
> >> +err_free:
> >> + drm_gem_shmem_free(&bo->base);
> >> +
> >> + return ERR_PTR(err);
> >> }
> >>
> >> struct drm_gem_object *
> >> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> >> index 770dab1942c2..ac145a98377b 100644
> >> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> >> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> >> @@ -504,7 +504,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
> >> if (IS_ERR(pages[i])) {
> >> ret = PTR_ERR(pages[i]);
> >> pages[i] = NULL;
> >> - goto err_pages;
> >> + goto err_unlock;
> >> }
> >> }
> >>
> >> @@ -512,7 +512,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
> >> ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
> >> NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL);
> >> if (ret)
> >> - goto err_pages;
> >> + goto err_unlock;
> > Feels like the panfrost_gem_mapping object should hold a ref on the BO
> > pages, not the BO itself, because, ultimately, the user of the BO is
> > the GPU. This matches what I was saying about moving get/put_pages() to
> > panfrost_mmu_map/unmap(): everytime a panfrost_gem_mapping becomes
> > active, to want to take a pages ref, every time it becomes inactive,
> > you should release the pages ref.
>
> The panfrost_mmu_unmap() is also used by shrinker when BO is purged. I'm
> unhappy with how icky it all becomes if unmap is made to put pages.

Why, that's exactly what's supposed to happen. If you mmu_unmap(), that
means you no longer need the pages ref you got.

>
> Previously map() was implicitly allocating pages with get_sgt() and then
> pages were implicitly released by drm_gem_shmem_free(). A non-heap BO is
> mapped when it's created by Panfrost, hence the actual lifetime of pages
> is kept unchanged by this patch.

But the whole point of making it explicit is to control when pages are
needed or not, isn't it. The fact we mmu_map() the BO at open time, and
keep it mapped until it's not longer referenced is an implementation
choice, and I don't think having pages_put() in mmu_unmap() changes
that.

> The implicit allocation is turned into
> explicit one, i.e. pages are explicitly allocated before BO is mapped.
>

2023-11-23 09:08:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 22/26] drm/shmem-helper: Don't free refcounted GEM

On Thu, 23 Nov 2023 01:30:24 +0300
Dmitry Osipenko <[email protected]> wrote:

> On 11/13/23 12:54, Boris Brezillon wrote:
> > On Mon, 30 Oct 2023 02:02:01 +0300
> > Dmitry Osipenko <[email protected]> wrote:
> >
> >> Don't free refcounted shmem object to prevent use-after-free bug that
> >> is worse than a memory leak.
> >>
> >> Signed-off-by: Dmitry Osipenko <[email protected]>
> >> ---
> >> drivers/gpu/drm/drm_gem_shmem_helper.c | 7 ++++---
> >> 1 file changed, 4 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >> index 6dd087f19ea3..4253c367dc07 100644
> >> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> >> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >> @@ -203,9 +203,10 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
> >> if (obj->import_attach)
> >> drm_prime_gem_destroy(obj, shmem->sgt);
> >>
> >> - drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
> >> - drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
> >> - drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));
> >> + if (drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count)) ||
> >> + drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count)) ||
> >> + drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count)))
> >> + return;
> >
> > I guess you're worried about ->sgt being referenced by the driver after
> > the GEM is destroyed. If we assume drivers don't cache the sgt and
> > always call get_pages_sgt() when they need it that shouldn't be an
> > issue. What we really don't want to release is the pages themselves,
> > but the GPU MMU might still have active mappings pointing to these
> > pages.
> >
> > In any case, I'm not against leaking the GEM object when any of these
> > counters are not zero, but can we at least have a comment in the
> > code explaining why we're doing that, so people don't have to go look
> > at the git history to figure it out.
>
> This patch is a minor improvement, it doesn't address any specific
> issue. This should be a common pattern in kernel. If you're giving a
> warning and know about the inevitable catastrophe, then avoid it if you can.

Sure, I'm just asking that we add a comment to explain why we leak
memory here. Is that too much to ask?

2023-11-23 12:24:54

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

On 11/23/23 12:05, Boris Brezillon wrote:
> On Thu, 23 Nov 2023 01:04:56 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
>> On 11/10/23 13:53, Boris Brezillon wrote:
>>> Hm, there was no drm_gem_shmem_get_pages_sgt() call here, why should we
>>> add a drm_gem_shmem_get_pages()? What we should do instead is add a
>>> drm_gem_shmem_get_pages() for each drm_gem_shmem_get_pages_sgt() we
>>> have in the driver (in panfrost_mmu_map()), and add
>>> drm_gem_shmem_put_pages() calls where they are missing
>>> (panfrost_mmu_unmap()).
>>>
>>>> + if (err)
>>>> + goto err_free;
>>>> + }
>>>> +
>>>> return bo;
>>>> +
>>>> +err_free:
>>>> + drm_gem_shmem_free(&bo->base);
>>>> +
>>>> + return ERR_PTR(err);
>>>> }
>>>>
>>>> struct drm_gem_object *
>>>> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
>>>> index 770dab1942c2..ac145a98377b 100644
>>>> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
>>>> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
>>>> @@ -504,7 +504,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
>>>> if (IS_ERR(pages[i])) {
>>>> ret = PTR_ERR(pages[i]);
>>>> pages[i] = NULL;
>>>> - goto err_pages;
>>>> + goto err_unlock;
>>>> }
>>>> }
>>>>
>>>> @@ -512,7 +512,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
>>>> ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
>>>> NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL);
>>>> if (ret)
>>>> - goto err_pages;
>>>> + goto err_unlock;
>>> Feels like the panfrost_gem_mapping object should hold a ref on the BO
>>> pages, not the BO itself, because, ultimately, the user of the BO is
>>> the GPU. This matches what I was saying about moving get/put_pages() to
>>> panfrost_mmu_map/unmap(): everytime a panfrost_gem_mapping becomes
>>> active, to want to take a pages ref, every time it becomes inactive,
>>> you should release the pages ref.
>>
>> The panfrost_mmu_unmap() is also used by shrinker when BO is purged. I'm
>> unhappy with how icky it all becomes if unmap is made to put pages.
>
> Why, that's exactly what's supposed to happen. If you mmu_unmap(), that
> means you no longer need the pages ref you got.

The drm_gem_shmem_purge() frees the pages. If mmu_unmap() frees pages too, then it becomes odd for drm_gem_shmem_purge() that it needs to free pages that were already freed.

>> Previously map() was implicitly allocating pages with get_sgt() and then
>> pages were implicitly released by drm_gem_shmem_free(). A non-heap BO is
>> mapped when it's created by Panfrost, hence the actual lifetime of pages
>> is kept unchanged by this patch.
>
> But the whole point of making it explicit is to control when pages are
> needed or not, isn't it. The fact we mmu_map() the BO at open time, and
> keep it mapped until it's not longer referenced is an implementation
> choice, and I don't think having pages_put() in mmu_unmap() changes
> that.

Previously, when the last mmu_unmap() was done, the pages were not released.

If you'll make unmap to put pages, then you can't map BO again because pages are released by the last put() of unmap. In order to keep the old pages allocation logic unchanged, the pages must be referenced while BO is alive, not while mapping is alive.

Technically, the code can be changed to put pages on unmap. This requires adding special quirk to drm_gem_shmem_purge() and then for Panfrost pages should have the same lifetime as BO, hence why bother?


diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 5ee98b6f0c94..5492610802a1 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -600,7 +600,9 @@ drm_gem_shmem_shrinker_put_pages_locked(struct drm_gem_shmem_object *shmem)
if (shmem->evicted)
return;

- drm_gem_shmem_free_pages(shmem);
+ if (refcount_read(&shmem->pages_use_count))
+ drm_gem_shmem_free_pages(shmem);
+
drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
}

@@ -608,7 +610,8 @@ void drm_gem_shmem_purge_locked(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;

- drm_WARN_ON(obj->dev, !drm_gem_shmem_is_purgeable(shmem));
+ if (refcount_read(&shmem->pages_use_count))
+ drm_WARN_ON(obj->dev, !drm_gem_shmem_is_purgeable(shmem));

drm_gem_shmem_shrinker_put_pages_locked(shmem);
drm_gem_free_mmap_offset(obj);
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index a6128e32f303..499964c43a7b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -41,9 +41,6 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
drm_gem_shmem_put_pages(&bo->base);
}

- if (!bo->is_heap && !obj->import_attach)
- drm_gem_shmem_put_pages(&bo->base);
-
drm_gem_shmem_free(&bo->base);
}

@@ -302,12 +299,6 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
bo->is_heap = !!(flags & PANFROST_BO_HEAP);

- if (!bo->is_heap) {
- err = drm_gem_shmem_get_pages(shmem);
- if (err)
- goto err_free;
- }
-
return bo;

err_free:
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 01cd97011ea5..4ed4ba5df420 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -335,9 +335,13 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
prot |= IOMMU_NOEXEC;

if (!obj->import_attach) {
- ret = drm_gem_shmem_pin(shmem);
+ ret = drm_gem_shmem_get_pages(shmem);
if (ret)
return ret;
+
+ ret = drm_gem_shmem_pin(shmem);
+ if (ret)
+ goto put_pages;
}

sgt = drm_gem_shmem_get_pages_sgt(shmem);
@@ -349,9 +353,18 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
prot, sgt);
mapping->active = true;
+
+ if (!obj->import_attach)
+ drm_gem_shmem_unpin(shmem);
+
+ return 0;
+
unpin:
if (!obj->import_attach)
drm_gem_shmem_unpin(shmem);
+put_pages:
+ if (!obj->import_attach)
+ drm_gem_shmem_put_pages(shmem);

return ret;
}
@@ -360,6 +373,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
{
struct panfrost_gem_object *bo = mapping->obj;
struct drm_gem_object *obj = &bo->base.base;
+ struct drm_gem_shmem_object *shmem = &bo->base;
struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
struct io_pgtable_ops *ops = mapping->mmu->pgtbl_ops;
u64 iova = mapping->mmnode.start << PAGE_SHIFT;
@@ -389,6 +403,9 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
panfrost_mmu_flush_range(pfdev, mapping->mmu,
mapping->mmnode.start << PAGE_SHIFT, len);
mapping->active = false;
+
+ if (!bo->is_heap && !obj->import_attach)
+ drm_gem_shmem_put_pages_locked(shmem);
}

static void mmu_tlb_inv_context_s1(void *cookie)


--
Best regards,
Dmitry

2023-11-23 12:37:12

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 22/26] drm/shmem-helper: Don't free refcounted GEM

On 11/23/23 12:08, Boris Brezillon wrote:
> On Thu, 23 Nov 2023 01:30:24 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
>> On 11/13/23 12:54, Boris Brezillon wrote:
>>> On Mon, 30 Oct 2023 02:02:01 +0300
>>> Dmitry Osipenko <[email protected]> wrote:
>>>
>>>> Don't free refcounted shmem object to prevent use-after-free bug that
>>>> is worse than a memory leak.
>>>>
>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
>>>> ---
>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 7 ++++---
>>>> 1 file changed, 4 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>> index 6dd087f19ea3..4253c367dc07 100644
>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>> @@ -203,9 +203,10 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
>>>> if (obj->import_attach)
>>>> drm_prime_gem_destroy(obj, shmem->sgt);
>>>>
>>>> - drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count));
>>>> - drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count));
>>>> - drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count));
>>>> + if (drm_WARN_ON(obj->dev, refcount_read(&shmem->vmap_use_count)) ||
>>>> + drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_use_count)) ||
>>>> + drm_WARN_ON(obj->dev, refcount_read(&shmem->pages_pin_count)))
>>>> + return;
>>>
>>> I guess you're worried about ->sgt being referenced by the driver after
>>> the GEM is destroyed. If we assume drivers don't cache the sgt and
>>> always call get_pages_sgt() when they need it that shouldn't be an
>>> issue. What we really don't want to release is the pages themselves,
>>> but the GPU MMU might still have active mappings pointing to these
>>> pages.
>>>
>>> In any case, I'm not against leaking the GEM object when any of these
>>> counters are not zero, but can we at least have a comment in the
>>> code explaining why we're doing that, so people don't have to go look
>>> at the git history to figure it out.
>>
>> This patch is a minor improvement, it doesn't address any specific
>> issue. This should be a common pattern in kernel. If you're giving a
>> warning and know about the inevitable catastrophe, then avoid it if you can.
>
> Sure, I'm just asking that we add a comment to explain why we leak
> memory here. Is that too much to ask?

Will add the comment. The reason why I added this patch was unrelated to
the sgt, that's what I'm talking about.

--
Best regards,
Dmitry

2023-11-23 14:34:17

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

On Thu, 23 Nov 2023 15:24:32 +0300
Dmitry Osipenko <[email protected]> wrote:

> On 11/23/23 12:05, Boris Brezillon wrote:
> > On Thu, 23 Nov 2023 01:04:56 +0300
> > Dmitry Osipenko <[email protected]> wrote:
> >
> >> On 11/10/23 13:53, Boris Brezillon wrote:
> >>> Hm, there was no drm_gem_shmem_get_pages_sgt() call here, why
> >>> should we add a drm_gem_shmem_get_pages()? What we should do
> >>> instead is add a drm_gem_shmem_get_pages() for each
> >>> drm_gem_shmem_get_pages_sgt() we have in the driver (in
> >>> panfrost_mmu_map()), and add drm_gem_shmem_put_pages() calls
> >>> where they are missing (panfrost_mmu_unmap()).
> >>>
> >>>> + if (err)
> >>>> + goto err_free;
> >>>> + }
> >>>> +
> >>>> return bo;
> >>>> +
> >>>> +err_free:
> >>>> + drm_gem_shmem_free(&bo->base);
> >>>> +
> >>>> + return ERR_PTR(err);
> >>>> }
> >>>>
> >>>> struct drm_gem_object *
> >>>> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> >>>> b/drivers/gpu/drm/panfrost/panfrost_mmu.c index
> >>>> 770dab1942c2..ac145a98377b 100644 ---
> >>>> a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++
> >>>> b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -504,7 +504,7 @@
> >>>> static int panfrost_mmu_map_fault_addr(struct panfrost_device
> >>>> *pfdev, int as, if (IS_ERR(pages[i])) { ret = PTR_ERR(pages[i]);
> >>>> pages[i] = NULL;
> >>>> - goto err_pages;
> >>>> + goto err_unlock;
> >>>> }
> >>>> }
> >>>>
> >>>> @@ -512,7 +512,7 @@ static int
> >>>> panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int
> >>>> as, ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
> >>>> NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL); if (ret)
> >>>> - goto err_pages;
> >>>> + goto err_unlock;
> >>> Feels like the panfrost_gem_mapping object should hold a ref on
> >>> the BO pages, not the BO itself, because, ultimately, the user of
> >>> the BO is the GPU. This matches what I was saying about moving
> >>> get/put_pages() to panfrost_mmu_map/unmap(): everytime a
> >>> panfrost_gem_mapping becomes active, to want to take a pages ref,
> >>> every time it becomes inactive, you should release the pages ref.
> >>>
> >>
> >> The panfrost_mmu_unmap() is also used by shrinker when BO is
> >> purged. I'm unhappy with how icky it all becomes if unmap is made
> >> to put pages.
> >
> > Why, that's exactly what's supposed to happen. If you mmu_unmap(),
> > that means you no longer need the pages ref you got.
>
> The drm_gem_shmem_purge() frees the pages. If mmu_unmap() frees pages
> too, then it becomes odd for drm_gem_shmem_purge() that it needs to
> free pages that were already freed.

Hm, I didn't consider the mmu_unmap() call in the eviction path.

>
> >> Previously map() was implicitly allocating pages with get_sgt()
> >> and then pages were implicitly released by drm_gem_shmem_free(). A
> >> non-heap BO is mapped when it's created by Panfrost, hence the
> >> actual lifetime of pages is kept unchanged by this patch.
> >
> > But the whole point of making it explicit is to control when pages
> > are needed or not, isn't it. The fact we mmu_map() the BO at open
> > time, and keep it mapped until it's not longer referenced is an
> > implementation choice, and I don't think having pages_put() in
> > mmu_unmap() changes that.
>
> Previously, when the last mmu_unmap() was done, the pages were not
> released.
>
> If you'll make unmap to put pages, then you can't map BO again
> because pages are released by the last put() of unmap.

Well, you could, if panfrost_gem_mapping_get() was not only
returning an existing mapping, but was also creating one when none
exist. But you're right, it messes up with the shmem shrinker and also
changes the way we are doing things now.

> In order to
> keep the old pages allocation logic unchanged, the pages must be
> referenced while BO is alive, not while mapping is alive.

Correct.

>
> Technically, the code can be changed to put pages on unmap. This
> requires adding special quirk to drm_gem_shmem_purge() and then for
> Panfrost pages should have the same lifetime as BO, hence why bother?

No, we certainly don't want to do that.

2023-11-23 14:49:16

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

On Mon, 30 Oct 2023 02:01:54 +0300
Dmitry Osipenko <[email protected]> wrote:

> To simplify the drm-shmem refcnt handling, we're moving away from
> the implicit get_pages() that is used by get_pages_sgt(). From now on
> drivers will have to pin pages while they use sgt. Panfrost's shrinker
> doesn't support swapping out BOs, hence pages are pinned and sgt is valid
> as long as pages' use-count > 0.
>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> drivers/gpu/drm/panfrost/panfrost_gem.c | 17 +++++++++++++++++
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 6 ++----
> 2 files changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> index 6b77d8cebcb2..bb9d43cf7c3c 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
> @@ -47,8 +47,13 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
> }
> }
> kvfree(bo->sgts);
> +
> + drm_gem_shmem_put_pages(&bo->base);
> }
>
> + if (!bo->is_heap && !obj->import_attach)
> + drm_gem_shmem_put_pages(&bo->base);
> +
> drm_gem_shmem_free(&bo->base);
> }
>
> @@ -269,6 +274,7 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
> {
> struct drm_gem_shmem_object *shmem;
> struct panfrost_gem_object *bo;
> + int err;
>
> /* Round up heap allocations to 2MB to keep fault handling simple */
> if (flags & PANFROST_BO_HEAP)
> @@ -282,7 +288,18 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
> bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
> bo->is_heap = !!(flags & PANFROST_BO_HEAP);
>
> + if (!bo->is_heap) {
> + err = drm_gem_shmem_get_pages(shmem);

I really hate the fact we request pages here while we call
panfrost_mmu_map() in panfrost_gem_open(), because ultimately, pages
are requested for the MMU mapping. Also hate the quirk we have in shmem
to call free_pages() instead of put_pages_locked() when the BO refcount
dropped to zero, and I was hoping we could get rid of it at some point
by teaching drivers to request pages when they actually need it instead
of tying pages lifetime to the GEM object lifetime.

Maybe what we should do instead is move the get/put_pages() in
panfrost_mmu_map/unmap() (as I suggested), but have a special mapping
panfrost_mmu_evict/restore() helpers that kill/restore the MMU mappings
without releasing/acquiring the pages ref.

2023-11-24 09:42:26

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 15/26] drm/panfrost: Explicitly get and put drm-shmem pages

On Thu, 23 Nov 2023 15:48:48 +0100
Boris Brezillon <[email protected]> wrote:

> On Mon, 30 Oct 2023 02:01:54 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
> > To simplify the drm-shmem refcnt handling, we're moving away from
> > the implicit get_pages() that is used by get_pages_sgt(). From now on
> > drivers will have to pin pages while they use sgt. Panfrost's shrinker
> > doesn't support swapping out BOs, hence pages are pinned and sgt is valid
> > as long as pages' use-count > 0.
> >
> > Signed-off-by: Dmitry Osipenko <[email protected]>
> > ---
> > drivers/gpu/drm/panfrost/panfrost_gem.c | 17 +++++++++++++++++
> > drivers/gpu/drm/panfrost/panfrost_mmu.c | 6 ++----
> > 2 files changed, 19 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> > index 6b77d8cebcb2..bb9d43cf7c3c 100644
> > --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> > +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
> > @@ -47,8 +47,13 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
> > }
> > }
> > kvfree(bo->sgts);
> > +
> > + drm_gem_shmem_put_pages(&bo->base);
> > }
> >
> > + if (!bo->is_heap && !obj->import_attach)
> > + drm_gem_shmem_put_pages(&bo->base);
> > +
> > drm_gem_shmem_free(&bo->base);
> > }
> >
> > @@ -269,6 +274,7 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
> > {
> > struct drm_gem_shmem_object *shmem;
> > struct panfrost_gem_object *bo;
> > + int err;
> >
> > /* Round up heap allocations to 2MB to keep fault handling simple */
> > if (flags & PANFROST_BO_HEAP)
> > @@ -282,7 +288,18 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
> > bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
> > bo->is_heap = !!(flags & PANFROST_BO_HEAP);
> >
> > + if (!bo->is_heap) {
> > + err = drm_gem_shmem_get_pages(shmem);
>
> I really hate the fact we request pages here while we call
> panfrost_mmu_map() in panfrost_gem_open(), because ultimately, pages
> are requested for the MMU mapping. Also hate the quirk we have in shmem
> to call free_pages() instead of put_pages_locked() when the BO refcount
> dropped to zero, and I was hoping we could get rid of it at some point
> by teaching drivers to request pages when they actually need it instead
> of tying pages lifetime to the GEM object lifetime.
>
> Maybe what we should do instead is move the get/put_pages() in
> panfrost_mmu_map/unmap() (as I suggested), but have a special mapping
> panfrost_mmu_evict/restore() helpers that kill/restore the MMU mappings
> without releasing/acquiring the pages ref.

Okay, so I played with your branch and did what I suggested here ^. The
end result is available here [1]. I also split this patch in two:

- A fix for the error path in panfrost_mmu_map_fault_addr() [2]
- The explicit get/put_pages() stuff with pages ref owned by the
panfrost_gem_mapping object [3]

[1]https://gitlab.freedesktop.org/bbrezillon/linux/-/commits/virtio-gpu-shrinker-v18
[2]https://gitlab.freedesktop.org/bbrezillon/linux/-/commit/9d499e971fdae4d6e52f8871ca27c24b2a2c43d6
[3]https://gitlab.freedesktop.org/bbrezillon/linux/-/commit/ba3de65bf1cf0ca95710e743ec85ca67ff1aa223

2023-11-24 10:04:56

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 26/26] drm/panfrost: Switch to generic memory shrinker

On Mon, 30 Oct 2023 02:02:05 +0300
Dmitry Osipenko <[email protected]> wrote:

> Replace Panfrost's custom memory shrinker with a common drm-shmem
> memory shrinker.
>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> drivers/gpu/drm/panfrost/Makefile | 1 -
> drivers/gpu/drm/panfrost/panfrost_device.h | 4 -
> drivers/gpu/drm/panfrost/panfrost_drv.c | 27 ++--
> drivers/gpu/drm/panfrost/panfrost_gem.c | 34 +++--
> drivers/gpu/drm/panfrost/panfrost_gem.h | 9 --
> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 129 ------------------
> drivers/gpu/drm/panfrost/panfrost_job.c | 18 ++-
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 18 ++-
> include/drm/drm_gem_shmem_helper.h | 7 -

Nice diffstat.

> 9 files changed, 66 insertions(+), 181 deletions(-)
> delete mode 100644 drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
>
> diff --git a/drivers/gpu/drm/panfrost/Makefile b/drivers/gpu/drm/panfrost/Makefile
> index 2c01c1e7523e..f2cb1ab0a32d 100644
> --- a/drivers/gpu/drm/panfrost/Makefile
> +++ b/drivers/gpu/drm/panfrost/Makefile
> @@ -5,7 +5,6 @@ panfrost-y := \
> panfrost_device.o \
> panfrost_devfreq.o \
> panfrost_gem.o \
> - panfrost_gem_shrinker.o \
> panfrost_gpu.o \
> panfrost_job.o \
> panfrost_mmu.o \
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
> index 1e85656dc2f7..2b24a0d4f85e 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.h
> @@ -117,10 +117,6 @@ struct panfrost_device {
> atomic_t pending;
> } reset;
>
> - struct mutex shrinker_lock;
> - struct list_head shrinker_list;
> - struct shrinker shrinker;
> -
> struct panfrost_devfreq pfdevfreq;
>
> struct {
> diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
> index 7f2aba96d5b9..ef520d2cc1d2 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_drv.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
> @@ -171,7 +171,6 @@ panfrost_lookup_bos(struct drm_device *dev,
> break;
> }
>
> - atomic_inc(&bo->gpu_usecount);
> job->mappings[i] = mapping;
> }
>
> @@ -397,7 +396,6 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
> {
> struct panfrost_file_priv *priv = file_priv->driver_priv;
> struct drm_panfrost_madvise *args = data;
> - struct panfrost_device *pfdev = dev->dev_private;
> struct drm_gem_object *gem_obj;
> struct panfrost_gem_object *bo;
> int ret = 0;
> @@ -410,11 +408,15 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
>
> bo = to_panfrost_bo(gem_obj);
>
> + if (bo->is_heap) {
> + args->retained = 1;
> + goto out_put_object;
> + }

After digging a bit, I finally got why you do that:
panfrost_gem_shmem_is_purgeable() had a shmem->sgt != NULL test, and
shmem->sgt is never populated for heap BOs (those have a separate sgts
table, each entry containing an sgt covering a 2MB portion of the
buffer). Looking at the code, I don't think making heap BO
non-reclaimable was intentional, otherwise we'd have filtered it out in
panfrost_ioctl_madvise() to avoid inserting an element that will never
be reclaimable. TLDR; I understand why you do that, and I agree this is
the right thing to do (even if I doubt we have any userspace using the
MADV ioctl on heap BOs), but it definitely deserves a comment
explaining that this is here to keep a pre-existing behavior, so people
are not tempted to remove it, and if they do, they must have a good
explanation.

> +
> ret = dma_resv_lock_interruptible(bo->base.base.resv, NULL);
> if (ret)
> goto out_put_object;
>
> - mutex_lock(&pfdev->shrinker_lock);
> mutex_lock(&bo->mappings.lock);
> if (args->madv == PANFROST_MADV_DONTNEED) {
> struct panfrost_gem_mapping *first;
> @@ -440,17 +442,8 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
>
> args->retained = drm_gem_shmem_madvise_locked(&bo->base, args->madv);
>
> - if (args->retained) {
> - if (args->madv == PANFROST_MADV_DONTNEED)
> - list_move_tail(&bo->base.madv_list,
> - &pfdev->shrinker_list);
> - else if (args->madv == PANFROST_MADV_WILLNEED)
> - list_del_init(&bo->base.madv_list);
> - }
> -
> out_unlock_mappings:
> mutex_unlock(&bo->mappings.lock);
> - mutex_unlock(&pfdev->shrinker_lock);
> dma_resv_unlock(bo->base.base.resv);
> out_put_object:
> drm_gem_object_put(gem_obj);
> @@ -635,9 +628,6 @@ static int panfrost_probe(struct platform_device *pdev)
> ddev->dev_private = pfdev;
> pfdev->ddev = ddev;
>
> - mutex_init(&pfdev->shrinker_lock);
> - INIT_LIST_HEAD(&pfdev->shrinker_list);
> -
> err = panfrost_device_init(pfdev);
> if (err) {
> if (err != -EPROBE_DEFER)
> @@ -659,10 +649,14 @@ static int panfrost_probe(struct platform_device *pdev)
> if (err < 0)
> goto err_out1;
>
> - panfrost_gem_shrinker_init(ddev);
> + err = drmm_gem_shmem_init(ddev);
> + if (err < 0)
> + goto err_out2;
>
> return 0;
>
> +err_out2:

Let's stop this nonsense, and give err labels meaningful names
(err_unregister_dev for that one).

> + drm_dev_unregister(ddev);
> err_out1:
> pm_runtime_disable(pfdev->dev);
> panfrost_device_fini(pfdev);
> @@ -678,7 +672,6 @@ static void panfrost_remove(struct platform_device *pdev)
> struct drm_device *ddev = pfdev->ddev;
>
> drm_dev_unregister(ddev);
> - panfrost_gem_shrinker_cleanup(ddev);
>
> pm_runtime_get_sync(pfdev->dev);
> pm_runtime_disable(pfdev->dev);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
> index bb9d43cf7c3c..a6128e32f303 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
> @@ -19,16 +19,6 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
> struct panfrost_gem_object *bo = to_panfrost_bo(obj);
> struct panfrost_device *pfdev = obj->dev->dev_private;
>
> - /*
> - * Make sure the BO is no longer inserted in the shrinker list before
> - * taking care of the destruction itself. If we don't do that we have a
> - * race condition between this function and what's done in
> - * panfrost_gem_shrinker_scan().
> - */
> - mutex_lock(&pfdev->shrinker_lock);
> - list_del_init(&bo->base.madv_list);
> - mutex_unlock(&pfdev->shrinker_lock);
> -
> /*
> * If we still have mappings attached to the BO, there's a problem in
> * our refcounting.
> @@ -94,7 +84,11 @@ static void panfrost_gem_mapping_release(struct kref *kref)
>
> mapping = container_of(kref, struct panfrost_gem_mapping, refcount);
>
> + /* shrinker that may purge mapping at the same time */

Let's give a detailed explanation here:

/* We need to do the unmapping with the resv lock held, because
* the shrinker might be purging mappings concurrently.
*/

> + dma_resv_lock(mapping->obj->base.base.resv, NULL);

This makes me think that maybe we could drop gem->mappings.lock and
protect the mappings list with the GEM resv. Not asking you to do that
in this patch though, it can be addressed in a follow-up.

> panfrost_gem_teardown_mapping(mapping);
> + dma_resv_unlock(mapping->obj->base.base.resv);
> +
> drm_gem_object_put(&mapping->obj->base.base);
> panfrost_mmu_ctx_put(mapping->mmu);
> kfree(mapping);
> @@ -228,6 +222,25 @@ static size_t panfrost_gem_rss(struct drm_gem_object *obj)
> return 0;
> }
>
> +static int panfrost_shmem_evict(struct drm_gem_object *obj)
> +{
> + struct panfrost_gem_object *bo = to_panfrost_bo(obj);
> +
> + if (!drm_gem_shmem_is_purgeable(&bo->base))
> + return -EBUSY;
> +
> + if (!mutex_trylock(&bo->mappings.lock))
> + return -EBUSY;
> +
> + panfrost_gem_teardown_mappings_locked(bo);

Okay, so I've modified and renamed
panfrost_gem_teardown_mappings_locked() here [1]. The new version is
called panfrost_gem_evict_mappings_locked(), and it just kills the MMU
mapping (with a panfrost_mmu_unmap() call). The mm_node (VA range) is
kept allocated until the panfrost_gem_mapping object is destroyed, which
better matches the eviction semantics IMHO.

> +
> + drm_gem_shmem_purge_locked(&bo->base);
> +
> + mutex_unlock(&bo->mappings.lock);
> +
> + return 0;
> +}
> +
> static const struct drm_gem_object_funcs panfrost_gem_funcs = {
> .free = panfrost_gem_free_object,
> .open = panfrost_gem_open,
> @@ -242,6 +255,7 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = {
> .status = panfrost_gem_status,
> .rss = panfrost_gem_rss,
> .vm_ops = &drm_gem_shmem_vm_ops,
> + .evict = panfrost_shmem_evict,
> };
>
> /**
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
> index 13c0a8149c3a..8ddc2d310d29 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gem.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
> @@ -30,12 +30,6 @@ struct panfrost_gem_object {
> struct mutex lock;
> } mappings;
>
> - /*
> - * Count the number of jobs referencing this BO so we don't let the
> - * shrinker reclaim this object prematurely.
> - */
> - atomic_t gpu_usecount;
> -
> /*
> * Object chunk size currently mapped onto physical memory
> */
> @@ -86,7 +80,4 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo,
> void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping);
> void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo);
>
> -void panfrost_gem_shrinker_init(struct drm_device *dev);
> -void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
> -
> #endif /* __PANFROST_GEM_H__ */
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> deleted file mode 100644
> index 1aa94fff7072..000000000000
> --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
> +++ /dev/null
> @@ -1,129 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -/* Copyright (C) 2019 Arm Ltd.
> - *
> - * Based on msm_gem_freedreno.c:
> - * Copyright (C) 2016 Red Hat
> - * Author: Rob Clark <[email protected]>
> - */
> -
> -#include <linux/list.h>
> -
> -#include <drm/drm_device.h>
> -#include <drm/drm_gem_shmem_helper.h>
> -
> -#include "panfrost_device.h"
> -#include "panfrost_gem.h"
> -#include "panfrost_mmu.h"
> -
> -static bool panfrost_gem_shmem_is_purgeable(struct drm_gem_shmem_object *shmem)
> -{
> - return (shmem->madv > 0) &&
> - !refcount_read(&shmem->pages_pin_count) && shmem->sgt &&
> - !shmem->base.dma_buf && !shmem->base.import_attach;
> -}
> -
> -static unsigned long
> -panfrost_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
> -{
> - struct panfrost_device *pfdev =
> - container_of(shrinker, struct panfrost_device, shrinker);
> - struct drm_gem_shmem_object *shmem;
> - unsigned long count = 0;
> -
> - if (!mutex_trylock(&pfdev->shrinker_lock))
> - return 0;
> -
> - list_for_each_entry(shmem, &pfdev->shrinker_list, madv_list) {
> - if (panfrost_gem_shmem_is_purgeable(shmem))
> - count += shmem->base.size >> PAGE_SHIFT;
> - }
> -
> - mutex_unlock(&pfdev->shrinker_lock);
> -
> - return count;
> -}
> -
> -static bool panfrost_gem_purge(struct drm_gem_object *obj)
> -{
> - struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
> - struct panfrost_gem_object *bo = to_panfrost_bo(obj);
> - bool ret = false;
> -
> - if (atomic_read(&bo->gpu_usecount))
> - return false;
> -
> - if (!mutex_trylock(&bo->mappings.lock))
> - return false;
> -
> - if (!dma_resv_trylock(shmem->base.resv))
> - goto unlock_mappings;
> -
> - panfrost_gem_teardown_mappings_locked(bo);
> - drm_gem_shmem_purge_locked(&bo->base);
> - ret = true;
> -
> - dma_resv_unlock(shmem->base.resv);
> -
> -unlock_mappings:
> - mutex_unlock(&bo->mappings.lock);
> - return ret;
> -}
> -
> -static unsigned long
> -panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
> -{
> - struct panfrost_device *pfdev =
> - container_of(shrinker, struct panfrost_device, shrinker);
> - struct drm_gem_shmem_object *shmem, *tmp;
> - unsigned long freed = 0;
> -
> - if (!mutex_trylock(&pfdev->shrinker_lock))
> - return SHRINK_STOP;
> -
> - list_for_each_entry_safe(shmem, tmp, &pfdev->shrinker_list, madv_list) {
> - if (freed >= sc->nr_to_scan)
> - break;
> - if (drm_gem_shmem_is_purgeable(shmem) &&
> - panfrost_gem_purge(&shmem->base)) {
> - freed += shmem->base.size >> PAGE_SHIFT;
> - list_del_init(&shmem->madv_list);
> - }
> - }
> -
> - mutex_unlock(&pfdev->shrinker_lock);
> -
> - if (freed > 0)
> - pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT);
> -
> - return freed;
> -}
> -
> -/**
> - * panfrost_gem_shrinker_init - Initialize panfrost shrinker
> - * @dev: DRM device
> - *
> - * This function registers and sets up the panfrost shrinker.
> - */
> -void panfrost_gem_shrinker_init(struct drm_device *dev)
> -{
> - struct panfrost_device *pfdev = dev->dev_private;
> - pfdev->shrinker.count_objects = panfrost_gem_shrinker_count;
> - pfdev->shrinker.scan_objects = panfrost_gem_shrinker_scan;
> - pfdev->shrinker.seeks = DEFAULT_SEEKS;
> - WARN_ON(register_shrinker(&pfdev->shrinker, "drm-panfrost"));
> -}
> -
> -/**
> - * panfrost_gem_shrinker_cleanup - Clean up panfrost shrinker
> - * @dev: DRM device
> - *
> - * This function unregisters the panfrost shrinker.
> - */
> -void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
> -{
> - struct panfrost_device *pfdev = dev->dev_private;
> -
> - if (pfdev->shrinker.nr_deferred) {
> - unregister_shrinker(&pfdev->shrinker);
> - }
> -}
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index fb16de2d0420..da6be590557f 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
> @@ -289,6 +289,19 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos,
> dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
> }
>
> +static int panfrost_objects_prepare(struct drm_gem_object **bos, int bo_count)
> +{
> + struct panfrost_gem_object *bo;
> + int ret = 0;
> +
> + while (!ret && bo_count--) {
> + bo = to_panfrost_bo(bos[bo_count]);
> + ret = bo->base.madv ? -ENOMEM : 0;

That's more an -EINVAL: users are not supposed to pass BOs that have an
madv != WILL_NEED, if they do, that's their mistake, not a memory
allocation failure.

> + }
> +
> + return ret;
> +}
> +
> int panfrost_job_push(struct panfrost_job *job)
> {
> struct panfrost_device *pfdev = job->pfdev;
> @@ -300,6 +313,10 @@ int panfrost_job_push(struct panfrost_job *job)
> if (ret)
> return ret;
>
> + ret = panfrost_objects_prepare(job->bos, job->bo_count);
> + if (ret)
> + goto unlock;
> +
> mutex_lock(&pfdev->sched_lock);
> drm_sched_job_arm(&job->base);
>
> @@ -341,7 +358,6 @@ static void panfrost_job_cleanup(struct kref *ref)
> if (!job->mappings[i])
> break;
>
> - atomic_dec(&job->mappings[i]->obj->gpu_usecount);
> panfrost_gem_mapping_put(job->mappings[i]);
> }
> kvfree(job->mappings);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> index ac145a98377b..01cd97011ea5 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> @@ -326,6 +326,7 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
> struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
> struct sg_table *sgt;
> int prot = IOMMU_READ | IOMMU_WRITE;
> + int ret = 0;
>
> if (WARN_ON(mapping->active))
> return 0;
> @@ -333,15 +334,26 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
> if (bo->noexec)
> prot |= IOMMU_NOEXEC;
>
> + if (!obj->import_attach) {
> + ret = drm_gem_shmem_pin(shmem);

I see why you do that. In practice panfrost_mmu_map() is called
before the object has been exposed, so I don't think it can be
purged/evicted at that point (and _get_pages() should have been called
before panfrost_mmu_map()), but I don't mind the extra safety net. Can
we just add a comment explaining the pin/get_pages_sgt/unpin dance here?

> + if (ret)
> + return ret;
> + }
> +
> sgt = drm_gem_shmem_get_pages_sgt(shmem);
> - if (WARN_ON(IS_ERR(sgt)))
> - return PTR_ERR(sgt);
> + if (WARN_ON(IS_ERR(sgt))) {
> + ret = PTR_ERR(sgt);
> + goto unpin;
> + }
>
> mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
> prot, sgt);
> mapping->active = true;

nit: add a blank line here, please.

> +unpin:
> + if (!obj->import_attach)
> + drm_gem_shmem_unpin(shmem);
>
> - return 0;
> + return ret;
> }
>
> void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
> diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
> index 6ac77c2082ed..2a506074da46 100644
> --- a/include/drm/drm_gem_shmem_helper.h
> +++ b/include/drm/drm_gem_shmem_helper.h
> @@ -61,13 +61,6 @@ struct drm_gem_shmem_object {
> */
> int madv;
>
> - /**
> - * @madv_list: List entry for madvise tracking
> - *
> - * Typically used by drivers to track purgeable objects
> - */
> - struct list_head madv_list;
> -
> /**
> * @sgt: Scatter/gather table for imported PRIME buffers
> */

[1]https://gitlab.freedesktop.org/bbrezillon/linux/-/commit/8401ba620de63073089517805f9413fac88653cd

2023-11-24 10:35:16

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 01/26] drm/gem: Change locked/unlocked postfix of drm_gem_v/unmap() function names

On Mon, 30 Oct 2023 02:01:40 +0300, Dmitry Osipenko wrote:
> Make drm/gem API function names consistent by having locked function
> use the _locked postfix in the name, while the unlocked variants don't
> use the _unlocked postfix. Rename drm_gem_v/unmap() function names to
> make them consistent with the rest of the API functions.
>
>
> [ ... ]

Acked-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-11-24 10:35:52

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 02/26] drm/gem: Add _locked postfix to functions that have unlocked counterpart

On Mon, 30 Oct 2023 02:01:41 +0300, Dmitry Osipenko wrote:
> Add _locked postfix to drm_gem functions that have unlocked counterpart
> functions to make GEM functions naming more consistent and intuitive in
> regards to the locking requirements.
>
> Reviewed-by: Boris Brezillon <[email protected]>
>
> [ ... ]

Acked-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-11-24 10:36:59

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 03/26] drm/shmem-helper: Make all exported symbols GPL

On Mon, 30 Oct 2023 02:01:42 +0300, Dmitry Osipenko wrote:
> Make all drm-shmem exported symbols GPL to make them consistent with
> the rest of drm-shmem symbols.
>
> Reviewed-by: Boris Brezillon <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
>
> [ ... ]

Acked-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-11-24 10:40:16

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> Add locked and remove unlocked postfixes from drm-shmem function names,
> making names consistent with the drm/gem core code.
>
> Reviewed-by: Boris Brezillon <[email protected]>
> Suggested-by: Boris Brezillon <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>

This contradicts my earlier ack on a patch but...

> ---
> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> drivers/gpu/drm/lima/lima_gem.c | 8 +--
> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> include/drm/drm_gem_shmem_helper.h | 36 +++++------
> 9 files changed, 64 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> index 0d61f2b3e213..154585ddae08 100644
> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> .pin = drm_gem_shmem_object_pin,
> .unpin = drm_gem_shmem_object_unpin,
> .get_sg_table = drm_gem_shmem_object_get_sg_table,
> - .vmap = drm_gem_shmem_object_vmap,
> - .vunmap = drm_gem_shmem_object_vunmap,
> + .vmap = drm_gem_shmem_object_vmap_locked,
> + .vunmap = drm_gem_shmem_object_vunmap_locked,

While I think we should indeed be consistent with the names, I would
also expect helpers to get the locking right by default.

I'm not sure how reasonable it is, but I think I'd prefer to turn this
around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
convert whatever function needs to be converted to the unlock suffix so
we get a consistent naming.

Does that make sense?
Maxime


Attachments:
(No filename) (2.06 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-24 10:40:39

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 05/26] drm/shmem-helper: Remove obsoleted is_iomem test

On Mon, 30 Oct 2023 02:01:44 +0300, Dmitry Osipenko wrote:
> Everything that uses the mapped buffer should be agnostic to is_iomem.
> The only reason for the is_iomem test is that we're setting shmem->vaddr
> to the returned map->vaddr. Now that the shmem->vaddr code is gone, remove
> the obsoleted is_iomem test to clean up the code.
>
>
> [ ... ]

Acked-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-11-24 10:44:57

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Fri, 24 Nov 2023 11:40:06 +0100
Maxime Ripard <[email protected]> wrote:

> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > Add locked and remove unlocked postfixes from drm-shmem function names,
> > making names consistent with the drm/gem core code.
> >
> > Reviewed-by: Boris Brezillon <[email protected]>
> > Suggested-by: Boris Brezillon <[email protected]>
> > Signed-off-by: Dmitry Osipenko <[email protected]>
>
> This contradicts my earlier ack on a patch but...
>
> > ---
> > drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > 9 files changed, 64 insertions(+), 64 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > index 0d61f2b3e213..154585ddae08 100644
> > --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > .pin = drm_gem_shmem_object_pin,
> > .unpin = drm_gem_shmem_object_unpin,
> > .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > - .vmap = drm_gem_shmem_object_vmap,
> > - .vunmap = drm_gem_shmem_object_vunmap,
> > + .vmap = drm_gem_shmem_object_vmap_locked,
> > + .vunmap = drm_gem_shmem_object_vunmap_locked,
>
> While I think we should indeed be consistent with the names, I would
> also expect helpers to get the locking right by default.
>
> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> convert whatever function needs to be converted to the unlock suffix so
> we get a consistent naming.
>
> Does that make sense?

I don't mind, as long as it's consistent, it's just that that there's
probably more to patch if we do it the other way around.

2023-11-24 10:45:12

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 07/26] drm/shmem-helper: Use refcount_t for pages_use_count

On Mon, 30 Oct 2023 02:01:46 +0300, Dmitry Osipenko wrote:
> Use atomic refcount_t helper for pages_use_count to optimize pin/unpin
> functions by skipping reservation locking while GEM's pin refcount > 1.
>
> Reviewed-by: Boris Brezillon <[email protected]>
> Suggested-by: Boris Brezillon <[email protected]>
>
> [ ... ]

Acked-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-11-24 10:48:11

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 08/26] drm/shmem-helper: Add and use lockless drm_gem_shmem_get_pages()

On Mon, Oct 30, 2023 at 02:01:47AM +0300, Dmitry Osipenko wrote:
> Add lockless drm_gem_shmem_get_pages() helper that skips taking reservation
> lock if pages_use_count is non-zero, leveraging from atomicity of the
> refcount_t. Make drm_gem_shmem_mmap() to utilize the new helper.
>
> Reviewed-by: Boris Brezillon <[email protected]>
> Suggested-by: Boris Brezillon <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> drivers/gpu/drm/drm_gem_shmem_helper.c | 19 +++++++++++++++----
> 1 file changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> index 6e02643ed87e..41b749bedb11 100644
> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> @@ -226,6 +226,20 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
> }
> EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);
>
> +static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
> +{
> + int ret;
> +
> + if (refcount_inc_not_zero(&shmem->pages_use_count))
> + return 0;
> +
> + dma_resv_lock(shmem->base.resv, NULL);
> + ret = drm_gem_shmem_get_pages_locked(shmem);
> + dma_resv_unlock(shmem->base.resv);
> +
> + return ret;
> +}
> +

Wait, so the locked suffix is to indicate that we need to take the lock
before we call it? I think that's the opposite to all(?) the naming
convention we have

Especially since the function name doesn't describe what the function
does anymore, but the context in which to call it. I'm sure if I was to
use it, I would have gotten it wrong, or at the very least been very
confused about it.

Maxime


Attachments:
(No filename) (1.73 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-24 10:49:20

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 10/26] drm/shmem-helper: Use refcount_t for vmap_use_count

On Mon, 30 Oct 2023 02:01:49 +0300, Dmitry Osipenko wrote:
> Use refcount_t helper for vmap_use_count to make refcounting consistent
> with pages_use_count and pages_pin_count that use refcount_t. This also
> makes vmapping to benefit from the refcount_t's overflow checks.
>
> Reviewed-by: Boris Brezillon <[email protected]>
>
> [ ... ]

Acked-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-11-24 10:59:33

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Fri, 24 Nov 2023 11:40:06 +0100
Maxime Ripard <[email protected]> wrote:

> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > Add locked and remove unlocked postfixes from drm-shmem function names,
> > making names consistent with the drm/gem core code.
> >
> > Reviewed-by: Boris Brezillon <[email protected]>
> > Suggested-by: Boris Brezillon <[email protected]>
> > Signed-off-by: Dmitry Osipenko <[email protected]>
>
> This contradicts my earlier ack on a patch but...
>
> > ---
> > drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > 9 files changed, 64 insertions(+), 64 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > index 0d61f2b3e213..154585ddae08 100644
> > --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > .pin = drm_gem_shmem_object_pin,
> > .unpin = drm_gem_shmem_object_unpin,
> > .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > - .vmap = drm_gem_shmem_object_vmap,
> > - .vunmap = drm_gem_shmem_object_vunmap,
> > + .vmap = drm_gem_shmem_object_vmap_locked,
> > + .vunmap = drm_gem_shmem_object_vunmap_locked,
>
> While I think we should indeed be consistent with the names, I would
> also expect helpers to get the locking right by default.

Wait, actually I think this patch does what you suggest already. The
_locked() prefix tells the caller: "you should take care of the locking,
I expect the lock to be held when this is hook/function is called". So
helpers without the _locked() prefix take care of the locking (which I
guess matches your 'helpers get the locking right' expectation), and
those with the _locked() prefix don't.

>
> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> convert whatever function needs to be converted to the unlock suffix so
> we get a consistent naming.

That would be an _unlocked() prefix if we do it the other way around. I
think the main confusion comes from the names of the hooks in
drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
are called with the GEM resv lock held, and locking is handled by the
core, others, like drm_gem_shmem_funcs::[un]pin() are called
without the GEM resv lock held, and locking is deferred to the
implementation. As I said, I don't mind prefixing hooks/helpers with
_unlocked() for those that take care of the locking, and no prefix for
those that expects locks to be held, as long as it's consistent, but I
just wanted to make sure we're on the same page :-).

2023-11-24 11:21:20

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 08/26] drm/shmem-helper: Add and use lockless drm_gem_shmem_get_pages()

On Fri, 24 Nov 2023 11:47:57 +0100
Maxime Ripard <[email protected]> wrote:

> On Mon, Oct 30, 2023 at 02:01:47AM +0300, Dmitry Osipenko wrote:
> > Add lockless drm_gem_shmem_get_pages() helper that skips taking reservation
> > lock if pages_use_count is non-zero, leveraging from atomicity of the
> > refcount_t. Make drm_gem_shmem_mmap() to utilize the new helper.
> >
> > Reviewed-by: Boris Brezillon <[email protected]>
> > Suggested-by: Boris Brezillon <[email protected]>
> > Signed-off-by: Dmitry Osipenko <[email protected]>
> > ---
> > drivers/gpu/drm/drm_gem_shmem_helper.c | 19 +++++++++++++++----
> > 1 file changed, 15 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > index 6e02643ed87e..41b749bedb11 100644
> > --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > @@ -226,6 +226,20 @@ void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
> > }
> > EXPORT_SYMBOL_GPL(drm_gem_shmem_put_pages_locked);
> >
> > +static int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
> > +{
> > + int ret;
> > +
> > + if (refcount_inc_not_zero(&shmem->pages_use_count))
> > + return 0;
> > +
> > + dma_resv_lock(shmem->base.resv, NULL);
> > + ret = drm_gem_shmem_get_pages_locked(shmem);
> > + dma_resv_unlock(shmem->base.resv);
> > +
> > + return ret;
> > +}
> > +
>
> Wait, so the locked suffix is to indicate that we need to take the lock
> before we call it? I think that's the opposite to all(?) the naming
> convention we have

If you grep for "_locked(" and "_unlocked(" in the DRM sub-tree, you'll
see it's actually mixed, with maybe a few more helpers suffixed
_locked() than we have suffixed with _unlocked().

>
> Especially since the function name doesn't describe what the function
> does anymore, but the context in which to call it.

Well, that's the same for "_unlocked", and we do have to pick one of
the _locked/_unlocked pattern if we want to expose both flavors.

> I'm sure if I was to
> use it, I would have gotten it wrong, or at the very least been very
> confused about it.

I personally find both equally confusing tbh, but we do have cases
where we need to expose the exact same functionality without the extra
locking. I do have a slight preference for _locked though, because it's
two characters shorter ;-).

2023-11-28 11:16:25

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

Hi,

On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> On Fri, 24 Nov 2023 11:40:06 +0100
> Maxime Ripard <[email protected]> wrote:
>
> > On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > > Add locked and remove unlocked postfixes from drm-shmem function names,
> > > making names consistent with the drm/gem core code.
> > >
> > > Reviewed-by: Boris Brezillon <[email protected]>
> > > Suggested-by: Boris Brezillon <[email protected]>
> > > Signed-off-by: Dmitry Osipenko <[email protected]>
> >
> > This contradicts my earlier ack on a patch but...
> >
> > > ---
> > > drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > > drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > > drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > > drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > > .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > > drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > > drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > > drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > > include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > > 9 files changed, 64 insertions(+), 64 deletions(-)
> > >
> > > diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > index 0d61f2b3e213..154585ddae08 100644
> > > --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > > .pin = drm_gem_shmem_object_pin,
> > > .unpin = drm_gem_shmem_object_unpin,
> > > .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > > - .vmap = drm_gem_shmem_object_vmap,
> > > - .vunmap = drm_gem_shmem_object_vunmap,
> > > + .vmap = drm_gem_shmem_object_vmap_locked,
> > > + .vunmap = drm_gem_shmem_object_vunmap_locked,
> >
> > While I think we should indeed be consistent with the names, I would
> > also expect helpers to get the locking right by default.
>
> Wait, actually I think this patch does what you suggest already. The
> _locked() prefix tells the caller: "you should take care of the locking,
> I expect the lock to be held when this is hook/function is called". So
> helpers without the _locked() prefix take care of the locking (which I
> guess matches your 'helpers get the locking right' expectation), and
> those with the _locked() prefix don't.

What I meant by "getting the locking right" is indeed a bit ambiguous,
sorry. What I'm trying to say I guess is that, in this particular case,
I don't think you can expect the vmap implementation to be called with
or without the locks held. The doc for that function will say that it's
either one or the other, but not both.

So helpers should follow what is needed to provide a default vmap/vunmap
implementation, including what locking is expected from a vmap/vunmap
implementation.

If that means that vmap is always called with the locks taken, then
drm_gem_shmem_object_vmap can just assume that it will be called with
the locks taken and there's no need to mention it in the name (and you
can probably sprinkle a couple of lockdep assertion to make sure the
locking is indeed consistent).

> > I'm not sure how reasonable it is, but I think I'd prefer to turn this
> > around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> > convert whatever function needs to be converted to the unlock suffix so
> > we get a consistent naming.
>
> That would be an _unlocked() prefix if we do it the other way around. I
> think the main confusion comes from the names of the hooks in
> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> are called with the GEM resv lock held, and locking is handled by the
> core, others, like drm_gem_shmem_funcs::[un]pin() are called
> without the GEM resv lock held, and locking is deferred to the
> implementation. As I said, I don't mind prefixing hooks/helpers with
> _unlocked() for those that take care of the locking, and no prefix for
> those that expects locks to be held, as long as it's consistent, but I
> just wanted to make sure we're on the same page :-).

What about _nolock then? It's the same number of characters than
_locked, plus it expresses what the function is (not) doing, not what
context it's supposed to be called in?

Maxime


Attachments:
(No filename) (4.42 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-28 12:37:26

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Tue, 28 Nov 2023 12:14:42 +0100
Maxime Ripard <[email protected]> wrote:

> Hi,
>
> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> > On Fri, 24 Nov 2023 11:40:06 +0100
> > Maxime Ripard <[email protected]> wrote:
> >
> > > On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > > > Add locked and remove unlocked postfixes from drm-shmem function names,
> > > > making names consistent with the drm/gem core code.
> > > >
> > > > Reviewed-by: Boris Brezillon <[email protected]>
> > > > Suggested-by: Boris Brezillon <[email protected]>
> > > > Signed-off-by: Dmitry Osipenko <[email protected]>
> > >
> > > This contradicts my earlier ack on a patch but...
> > >
> > > > ---
> > > > drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > > > drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > > > drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > > > drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > > > .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > > > drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > > > drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > > > drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > > > include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > > > 9 files changed, 64 insertions(+), 64 deletions(-)
> > > >
> > > > diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > > index 0d61f2b3e213..154585ddae08 100644
> > > > --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > > +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > > @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > > > .pin = drm_gem_shmem_object_pin,
> > > > .unpin = drm_gem_shmem_object_unpin,
> > > > .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > > > - .vmap = drm_gem_shmem_object_vmap,
> > > > - .vunmap = drm_gem_shmem_object_vunmap,
> > > > + .vmap = drm_gem_shmem_object_vmap_locked,
> > > > + .vunmap = drm_gem_shmem_object_vunmap_locked,
> > >
> > > While I think we should indeed be consistent with the names, I would
> > > also expect helpers to get the locking right by default.
> >
> > Wait, actually I think this patch does what you suggest already. The
> > _locked() prefix tells the caller: "you should take care of the locking,
> > I expect the lock to be held when this is hook/function is called". So
> > helpers without the _locked() prefix take care of the locking (which I
> > guess matches your 'helpers get the locking right' expectation), and
> > those with the _locked() prefix don't.
>
> What I meant by "getting the locking right" is indeed a bit ambiguous,
> sorry. What I'm trying to say I guess is that, in this particular case,
> I don't think you can expect the vmap implementation to be called with
> or without the locks held. The doc for that function will say that it's
> either one or the other, but not both.
>
> So helpers should follow what is needed to provide a default vmap/vunmap
> implementation, including what locking is expected from a vmap/vunmap
> implementation.

Hm, yeah, I think that's a matter of taste. When locking is often
deferrable, like it is in DRM, I find it beneficial for funcions and
function pointers to reflect the locking scheme, rather than relying on
people properly reading the doc, especially when this is the only
outlier in the group of drm_gem_object_funcs we already have, and it's
not event documented at the drm_gem_object_funcs level [1] :P.

>
> If that means that vmap is always called with the locks taken, then
> drm_gem_shmem_object_vmap can just assume that it will be called with
> the locks taken and there's no need to mention it in the name (and you
> can probably sprinkle a couple of lockdep assertion to make sure the
> locking is indeed consistent).

Things get very confusing when you end up having drm_gem_shmem helpers
that are suffixed with _locked() to encode the fact locking is the
caller's responsibility and no suffix for the
callee-takes-care-of-the-locking semantics, while other helpers that are
not suffixed at all actually implement the
caller-should-take-care-of-the-locking semantics.

>
> > > I'm not sure how reasonable it is, but I think I'd prefer to turn this
> > > around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> > > convert whatever function needs to be converted to the unlock suffix so
> > > we get a consistent naming.
> >
> > That would be an _unlocked() prefix if we do it the other way around. I
> > think the main confusion comes from the names of the hooks in
> > drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> > are called with the GEM resv lock held, and locking is handled by the
> > core, others, like drm_gem_shmem_funcs::[un]pin() are called
> > without the GEM resv lock held, and locking is deferred to the
> > implementation. As I said, I don't mind prefixing hooks/helpers with
> > _unlocked() for those that take care of the locking, and no prefix for
> > those that expects locks to be held, as long as it's consistent, but I
> > just wanted to make sure we're on the same page :-).
>
> What about _nolock then? It's the same number of characters than
> _locked, plus it expresses what the function is (not) doing, not what
> context it's supposed to be called in?

Just did a quick

git grep _nolock drivers/gpu/drm

and it returns zero result, where the _locked/_unlocked pattern seems
to already be widely used. Not saying we shouldn't change that, but it
doesn't feel like a change we should do as part of this series.

Regards,

Boris

[1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155

2023-11-28 22:05:40

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On 11/28/23 15:37, Boris Brezillon wrote:
> On Tue, 28 Nov 2023 12:14:42 +0100
> Maxime Ripard <[email protected]> wrote:
>
>> Hi,
>>
>> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
>>> On Fri, 24 Nov 2023 11:40:06 +0100
>>> Maxime Ripard <[email protected]> wrote:
>>>
>>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
>>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
>>>>> making names consistent with the drm/gem core code.
>>>>>
>>>>> Reviewed-by: Boris Brezillon <[email protected]>
>>>>> Suggested-by: Boris Brezillon <[email protected]>
>>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
>>>>
>>>> This contradicts my earlier ack on a patch but...
>>>>
>>>>> ---
>>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
>>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
>>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
>>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
>>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
>>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
>>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
>>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
>>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
>>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>>> index 0d61f2b3e213..154585ddae08 100644
>>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
>>>>> .pin = drm_gem_shmem_object_pin,
>>>>> .unpin = drm_gem_shmem_object_unpin,
>>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
>>>>> - .vmap = drm_gem_shmem_object_vmap,
>>>>> - .vunmap = drm_gem_shmem_object_vunmap,
>>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
>>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
>>>>
>>>> While I think we should indeed be consistent with the names, I would
>>>> also expect helpers to get the locking right by default.
>>>
>>> Wait, actually I think this patch does what you suggest already. The
>>> _locked() prefix tells the caller: "you should take care of the locking,
>>> I expect the lock to be held when this is hook/function is called". So
>>> helpers without the _locked() prefix take care of the locking (which I
>>> guess matches your 'helpers get the locking right' expectation), and
>>> those with the _locked() prefix don't.
>>
>> What I meant by "getting the locking right" is indeed a bit ambiguous,
>> sorry. What I'm trying to say I guess is that, in this particular case,
>> I don't think you can expect the vmap implementation to be called with
>> or without the locks held. The doc for that function will say that it's
>> either one or the other, but not both.
>>
>> So helpers should follow what is needed to provide a default vmap/vunmap
>> implementation, including what locking is expected from a vmap/vunmap
>> implementation.
>
> Hm, yeah, I think that's a matter of taste. When locking is often
> deferrable, like it is in DRM, I find it beneficial for funcions and
> function pointers to reflect the locking scheme, rather than relying on
> people properly reading the doc, especially when this is the only
> outlier in the group of drm_gem_object_funcs we already have, and it's
> not event documented at the drm_gem_object_funcs level [1] :P.
>
>>
>> If that means that vmap is always called with the locks taken, then
>> drm_gem_shmem_object_vmap can just assume that it will be called with
>> the locks taken and there's no need to mention it in the name (and you
>> can probably sprinkle a couple of lockdep assertion to make sure the
>> locking is indeed consistent).
>
> Things get very confusing when you end up having drm_gem_shmem helpers
> that are suffixed with _locked() to encode the fact locking is the
> caller's responsibility and no suffix for the
> callee-takes-care-of-the-locking semantics, while other helpers that are
> not suffixed at all actually implement the
> caller-should-take-care-of-the-locking semantics.
>
>>
>>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
>>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
>>>> convert whatever function needs to be converted to the unlock suffix so
>>>> we get a consistent naming.
>>>
>>> That would be an _unlocked() prefix if we do it the other way around. I
>>> think the main confusion comes from the names of the hooks in
>>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
>>> are called with the GEM resv lock held, and locking is handled by the
>>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
>>> without the GEM resv lock held, and locking is deferred to the
>>> implementation. As I said, I don't mind prefixing hooks/helpers with
>>> _unlocked() for those that take care of the locking, and no prefix for
>>> those that expects locks to be held, as long as it's consistent, but I
>>> just wanted to make sure we're on the same page :-).
>>
>> What about _nolock then? It's the same number of characters than
>> _locked, plus it expresses what the function is (not) doing, not what
>> context it's supposed to be called in?
>
> Just did a quick
>
> git grep _nolock drivers/gpu/drm
>
> and it returns zero result, where the _locked/_unlocked pattern seems
> to already be widely used. Not saying we shouldn't change that, but it
> doesn't feel like a change we should do as part of this series.
>
> Regards,
>
> Boris
>
> [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155

I'm fine with dropping the _locked() postfix from the common GEM helpers
and documenting the locking rule in drm_gem. Thank you all for the
suggestions :)

--
Best regards,
Dmitry

2023-11-29 07:54:02

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, 29 Nov 2023 01:05:14 +0300
Dmitry Osipenko <[email protected]> wrote:

> On 11/28/23 15:37, Boris Brezillon wrote:
> > On Tue, 28 Nov 2023 12:14:42 +0100
> > Maxime Ripard <[email protected]> wrote:
> >
> >> Hi,
> >>
> >> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> >>> On Fri, 24 Nov 2023 11:40:06 +0100
> >>> Maxime Ripard <[email protected]> wrote:
> >>>
> >>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> >>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
> >>>>> making names consistent with the drm/gem core code.
> >>>>>
> >>>>> Reviewed-by: Boris Brezillon <[email protected]>
> >>>>> Suggested-by: Boris Brezillon <[email protected]>
> >>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
> >>>>
> >>>> This contradicts my earlier ack on a patch but...
> >>>>
> >>>>> ---
> >>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> >>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
> >>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> >>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> >>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> >>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> >>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> >>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> >>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
> >>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >>>>> index 0d61f2b3e213..154585ddae08 100644
> >>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> >>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> >>>>> .pin = drm_gem_shmem_object_pin,
> >>>>> .unpin = drm_gem_shmem_object_unpin,
> >>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
> >>>>> - .vmap = drm_gem_shmem_object_vmap,
> >>>>> - .vunmap = drm_gem_shmem_object_vunmap,
> >>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
> >>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
> >>>>
> >>>> While I think we should indeed be consistent with the names, I would
> >>>> also expect helpers to get the locking right by default.
> >>>
> >>> Wait, actually I think this patch does what you suggest already. The
> >>> _locked() prefix tells the caller: "you should take care of the locking,
> >>> I expect the lock to be held when this is hook/function is called". So
> >>> helpers without the _locked() prefix take care of the locking (which I
> >>> guess matches your 'helpers get the locking right' expectation), and
> >>> those with the _locked() prefix don't.
> >>
> >> What I meant by "getting the locking right" is indeed a bit ambiguous,
> >> sorry. What I'm trying to say I guess is that, in this particular case,
> >> I don't think you can expect the vmap implementation to be called with
> >> or without the locks held. The doc for that function will say that it's
> >> either one or the other, but not both.
> >>
> >> So helpers should follow what is needed to provide a default vmap/vunmap
> >> implementation, including what locking is expected from a vmap/vunmap
> >> implementation.
> >
> > Hm, yeah, I think that's a matter of taste. When locking is often
> > deferrable, like it is in DRM, I find it beneficial for funcions and
> > function pointers to reflect the locking scheme, rather than relying on
> > people properly reading the doc, especially when this is the only
> > outlier in the group of drm_gem_object_funcs we already have, and it's
> > not event documented at the drm_gem_object_funcs level [1] :P.
> >
> >>
> >> If that means that vmap is always called with the locks taken, then
> >> drm_gem_shmem_object_vmap can just assume that it will be called with
> >> the locks taken and there's no need to mention it in the name (and you
> >> can probably sprinkle a couple of lockdep assertion to make sure the
> >> locking is indeed consistent).
> >
> > Things get very confusing when you end up having drm_gem_shmem helpers
> > that are suffixed with _locked() to encode the fact locking is the
> > caller's responsibility and no suffix for the
> > callee-takes-care-of-the-locking semantics, while other helpers that are
> > not suffixed at all actually implement the
> > caller-should-take-care-of-the-locking semantics.
> >
> >>
> >>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> >>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> >>>> convert whatever function needs to be converted to the unlock suffix so
> >>>> we get a consistent naming.
> >>>
> >>> That would be an _unlocked() prefix if we do it the other way around. I
> >>> think the main confusion comes from the names of the hooks in
> >>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> >>> are called with the GEM resv lock held, and locking is handled by the
> >>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
> >>> without the GEM resv lock held, and locking is deferred to the
> >>> implementation. As I said, I don't mind prefixing hooks/helpers with
> >>> _unlocked() for those that take care of the locking, and no prefix for
> >>> those that expects locks to be held, as long as it's consistent, but I
> >>> just wanted to make sure we're on the same page :-).
> >>
> >> What about _nolock then? It's the same number of characters than
> >> _locked, plus it expresses what the function is (not) doing, not what
> >> context it's supposed to be called in?
> >
> > Just did a quick
> >
> > git grep _nolock drivers/gpu/drm
> >
> > and it returns zero result, where the _locked/_unlocked pattern seems
> > to already be widely used. Not saying we shouldn't change that, but it
> > doesn't feel like a change we should do as part of this series.
> >
> > Regards,
> >
> > Boris
> >
> > [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155
>
> I'm fine with dropping the _locked() postfix from the common GEM helpers
> and documenting the locking rule in drm_gem. Thank you all for the
> suggestions :)

Sorry to disagree, but I think a proper function name/suffix is
sometimes worth a few lines of doc. Not saying we should do one or the
other, I think we should do both. But when I see a function suffixed
_locked, _unlocked or _nolock, I can immediately tell if this function
defers the locking to the caller or not, and then go check which lock
in the function doc.

And the second thing I'm not happy with, is the fact we go back to an
inconsistent naming in drm_gem_shmem_helper.c, where some functions
deferring the locking to the caller are suffixed _locked and others are
not, because ultimately, you need a different name when you expose the
two variants...

2023-11-29 10:47:38

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On 11/29/23 10:53, Boris Brezillon wrote:
> On Wed, 29 Nov 2023 01:05:14 +0300
> Dmitry Osipenko <[email protected]> wrote:
>
>> On 11/28/23 15:37, Boris Brezillon wrote:
>>> On Tue, 28 Nov 2023 12:14:42 +0100
>>> Maxime Ripard <[email protected]> wrote:
>>>
>>>> Hi,
>>>>
>>>> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
>>>>> On Fri, 24 Nov 2023 11:40:06 +0100
>>>>> Maxime Ripard <[email protected]> wrote:
>>>>>
>>>>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
>>>>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
>>>>>>> making names consistent with the drm/gem core code.
>>>>>>>
>>>>>>> Reviewed-by: Boris Brezillon <[email protected]>
>>>>>>> Suggested-by: Boris Brezillon <[email protected]>
>>>>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
>>>>>>
>>>>>> This contradicts my earlier ack on a patch but...
>>>>>>
>>>>>>> ---
>>>>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
>>>>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
>>>>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
>>>>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
>>>>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
>>>>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
>>>>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
>>>>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
>>>>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
>>>>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>>>>> index 0d61f2b3e213..154585ddae08 100644
>>>>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>>>>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
>>>>>>> .pin = drm_gem_shmem_object_pin,
>>>>>>> .unpin = drm_gem_shmem_object_unpin,
>>>>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
>>>>>>> - .vmap = drm_gem_shmem_object_vmap,
>>>>>>> - .vunmap = drm_gem_shmem_object_vunmap,
>>>>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
>>>>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
>>>>>>
>>>>>> While I think we should indeed be consistent with the names, I would
>>>>>> also expect helpers to get the locking right by default.
>>>>>
>>>>> Wait, actually I think this patch does what you suggest already. The
>>>>> _locked() prefix tells the caller: "you should take care of the locking,
>>>>> I expect the lock to be held when this is hook/function is called". So
>>>>> helpers without the _locked() prefix take care of the locking (which I
>>>>> guess matches your 'helpers get the locking right' expectation), and
>>>>> those with the _locked() prefix don't.
>>>>
>>>> What I meant by "getting the locking right" is indeed a bit ambiguous,
>>>> sorry. What I'm trying to say I guess is that, in this particular case,
>>>> I don't think you can expect the vmap implementation to be called with
>>>> or without the locks held. The doc for that function will say that it's
>>>> either one or the other, but not both.
>>>>
>>>> So helpers should follow what is needed to provide a default vmap/vunmap
>>>> implementation, including what locking is expected from a vmap/vunmap
>>>> implementation.
>>>
>>> Hm, yeah, I think that's a matter of taste. When locking is often
>>> deferrable, like it is in DRM, I find it beneficial for funcions and
>>> function pointers to reflect the locking scheme, rather than relying on
>>> people properly reading the doc, especially when this is the only
>>> outlier in the group of drm_gem_object_funcs we already have, and it's
>>> not event documented at the drm_gem_object_funcs level [1] :P.
>>>
>>>>
>>>> If that means that vmap is always called with the locks taken, then
>>>> drm_gem_shmem_object_vmap can just assume that it will be called with
>>>> the locks taken and there's no need to mention it in the name (and you
>>>> can probably sprinkle a couple of lockdep assertion to make sure the
>>>> locking is indeed consistent).
>>>
>>> Things get very confusing when you end up having drm_gem_shmem helpers
>>> that are suffixed with _locked() to encode the fact locking is the
>>> caller's responsibility and no suffix for the
>>> callee-takes-care-of-the-locking semantics, while other helpers that are
>>> not suffixed at all actually implement the
>>> caller-should-take-care-of-the-locking semantics.
>>>
>>>>
>>>>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
>>>>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
>>>>>> convert whatever function needs to be converted to the unlock suffix so
>>>>>> we get a consistent naming.
>>>>>
>>>>> That would be an _unlocked() prefix if we do it the other way around. I
>>>>> think the main confusion comes from the names of the hooks in
>>>>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
>>>>> are called with the GEM resv lock held, and locking is handled by the
>>>>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
>>>>> without the GEM resv lock held, and locking is deferred to the
>>>>> implementation. As I said, I don't mind prefixing hooks/helpers with
>>>>> _unlocked() for those that take care of the locking, and no prefix for
>>>>> those that expects locks to be held, as long as it's consistent, but I
>>>>> just wanted to make sure we're on the same page :-).
>>>>
>>>> What about _nolock then? It's the same number of characters than
>>>> _locked, plus it expresses what the function is (not) doing, not what
>>>> context it's supposed to be called in?
>>>
>>> Just did a quick
>>>
>>> git grep _nolock drivers/gpu/drm
>>>
>>> and it returns zero result, where the _locked/_unlocked pattern seems
>>> to already be widely used. Not saying we shouldn't change that, but it
>>> doesn't feel like a change we should do as part of this series.
>>>
>>> Regards,
>>>
>>> Boris
>>>
>>> [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155
>>
>> I'm fine with dropping the _locked() postfix from the common GEM helpers
>> and documenting the locking rule in drm_gem. Thank you all for the
>> suggestions :)
>
> Sorry to disagree, but I think a proper function name/suffix is
> sometimes worth a few lines of doc. Not saying we should do one or the
> other, I think we should do both. But when I see a function suffixed
> _locked, _unlocked or _nolock, I can immediately tell if this function
> defers the locking to the caller or not, and then go check which lock
> in the function doc.
>
> And the second thing I'm not happy with, is the fact we go back to an
> inconsistent naming in drm_gem_shmem_helper.c, where some functions
> deferring the locking to the caller are suffixed _locked and others are
> not, because ultimately, you need a different name when you expose the
> two variants...

By the `common GEM helpers` I meant the .vmap drm-shmem common helpers
used for drm_gem_object_funcs, like was suggested by Maxime. The rest of
functions will retain the _locked part. Sorry for the confusion :)

--
Best regards,
Dmitry

2023-11-29 10:57:14

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, 29 Nov 2023 13:47:21 +0300
Dmitry Osipenko <[email protected]> wrote:

> On 11/29/23 10:53, Boris Brezillon wrote:
> > On Wed, 29 Nov 2023 01:05:14 +0300
> > Dmitry Osipenko <[email protected]> wrote:
> >
> >> On 11/28/23 15:37, Boris Brezillon wrote:
> >>> On Tue, 28 Nov 2023 12:14:42 +0100
> >>> Maxime Ripard <[email protected]> wrote:
> >>>
> >>>> Hi,
> >>>>
> >>>> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> >>>>> On Fri, 24 Nov 2023 11:40:06 +0100
> >>>>> Maxime Ripard <[email protected]> wrote:
> >>>>>
> >>>>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> >>>>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
> >>>>>>> making names consistent with the drm/gem core code.
> >>>>>>>
> >>>>>>> Reviewed-by: Boris Brezillon <[email protected]>
> >>>>>>> Suggested-by: Boris Brezillon <[email protected]>
> >>>>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
> >>>>>>
> >>>>>> This contradicts my earlier ack on a patch but...
> >>>>>>
> >>>>>>> ---
> >>>>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> >>>>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
> >>>>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> >>>>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> >>>>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> >>>>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> >>>>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> >>>>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> >>>>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
> >>>>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >>>>>>> index 0d61f2b3e213..154585ddae08 100644
> >>>>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> >>>>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> >>>>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> >>>>>>> .pin = drm_gem_shmem_object_pin,
> >>>>>>> .unpin = drm_gem_shmem_object_unpin,
> >>>>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
> >>>>>>> - .vmap = drm_gem_shmem_object_vmap,
> >>>>>>> - .vunmap = drm_gem_shmem_object_vunmap,
> >>>>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
> >>>>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
> >>>>>>
> >>>>>> While I think we should indeed be consistent with the names, I would
> >>>>>> also expect helpers to get the locking right by default.
> >>>>>
> >>>>> Wait, actually I think this patch does what you suggest already. The
> >>>>> _locked() prefix tells the caller: "you should take care of the locking,
> >>>>> I expect the lock to be held when this is hook/function is called". So
> >>>>> helpers without the _locked() prefix take care of the locking (which I
> >>>>> guess matches your 'helpers get the locking right' expectation), and
> >>>>> those with the _locked() prefix don't.
> >>>>
> >>>> What I meant by "getting the locking right" is indeed a bit ambiguous,
> >>>> sorry. What I'm trying to say I guess is that, in this particular case,
> >>>> I don't think you can expect the vmap implementation to be called with
> >>>> or without the locks held. The doc for that function will say that it's
> >>>> either one or the other, but not both.
> >>>>
> >>>> So helpers should follow what is needed to provide a default vmap/vunmap
> >>>> implementation, including what locking is expected from a vmap/vunmap
> >>>> implementation.
> >>>
> >>> Hm, yeah, I think that's a matter of taste. When locking is often
> >>> deferrable, like it is in DRM, I find it beneficial for funcions and
> >>> function pointers to reflect the locking scheme, rather than relying on
> >>> people properly reading the doc, especially when this is the only
> >>> outlier in the group of drm_gem_object_funcs we already have, and it's
> >>> not event documented at the drm_gem_object_funcs level [1] :P.
> >>>
> >>>>
> >>>> If that means that vmap is always called with the locks taken, then
> >>>> drm_gem_shmem_object_vmap can just assume that it will be called with
> >>>> the locks taken and there's no need to mention it in the name (and you
> >>>> can probably sprinkle a couple of lockdep assertion to make sure the
> >>>> locking is indeed consistent).
> >>>
> >>> Things get very confusing when you end up having drm_gem_shmem helpers
> >>> that are suffixed with _locked() to encode the fact locking is the
> >>> caller's responsibility and no suffix for the
> >>> callee-takes-care-of-the-locking semantics, while other helpers that are
> >>> not suffixed at all actually implement the
> >>> caller-should-take-care-of-the-locking semantics.
> >>>
> >>>>
> >>>>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> >>>>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> >>>>>> convert whatever function needs to be converted to the unlock suffix so
> >>>>>> we get a consistent naming.
> >>>>>
> >>>>> That would be an _unlocked() prefix if we do it the other way around. I
> >>>>> think the main confusion comes from the names of the hooks in
> >>>>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> >>>>> are called with the GEM resv lock held, and locking is handled by the
> >>>>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
> >>>>> without the GEM resv lock held, and locking is deferred to the
> >>>>> implementation. As I said, I don't mind prefixing hooks/helpers with
> >>>>> _unlocked() for those that take care of the locking, and no prefix for
> >>>>> those that expects locks to be held, as long as it's consistent, but I
> >>>>> just wanted to make sure we're on the same page :-).
> >>>>
> >>>> What about _nolock then? It's the same number of characters than
> >>>> _locked, plus it expresses what the function is (not) doing, not what
> >>>> context it's supposed to be called in?
> >>>
> >>> Just did a quick
> >>>
> >>> git grep _nolock drivers/gpu/drm
> >>>
> >>> and it returns zero result, where the _locked/_unlocked pattern seems
> >>> to already be widely used. Not saying we shouldn't change that, but it
> >>> doesn't feel like a change we should do as part of this series.
> >>>
> >>> Regards,
> >>>
> >>> Boris
> >>>
> >>> [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155
> >>
> >> I'm fine with dropping the _locked() postfix from the common GEM helpers
> >> and documenting the locking rule in drm_gem. Thank you all for the
> >> suggestions :)
> >
> > Sorry to disagree, but I think a proper function name/suffix is
> > sometimes worth a few lines of doc. Not saying we should do one or the
> > other, I think we should do both. But when I see a function suffixed
> > _locked, _unlocked or _nolock, I can immediately tell if this function
> > defers the locking to the caller or not, and then go check which lock
> > in the function doc.
> >
> > And the second thing I'm not happy with, is the fact we go back to an
> > inconsistent naming in drm_gem_shmem_helper.c, where some functions
> > deferring the locking to the caller are suffixed _locked and others are
> > not, because ultimately, you need a different name when you expose the
> > two variants...
>
> By the `common GEM helpers` I meant the .vmap drm-shmem common helpers
> used for drm_gem_object_funcs, like was suggested by Maxime. The rest of
> functions will retain the _locked part. Sorry for the confusion :)

Well, even if it's just
s/drm_gem_shmem_v[un]map_locked/drm_gem_shmem_v[un]map/, it's still
inconsistent with the rest of the helpers we have there (_locked suffix
for those deferring the locking to the caller, and no suffix when the
lock is taken by the helper). To be clear, I won't block the patch
because of that, but I still think this is the wrong move...

2023-11-29 13:46:36

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, 29 Nov 2023 14:09:47 +0100
Maxime Ripard <[email protected]> wrote:

> On Wed, Nov 29, 2023 at 08:53:30AM +0100, Boris Brezillon wrote:
> > On Wed, 29 Nov 2023 01:05:14 +0300
> > Dmitry Osipenko <[email protected]> wrote:
> >
> > > On 11/28/23 15:37, Boris Brezillon wrote:
> > > > On Tue, 28 Nov 2023 12:14:42 +0100
> > > > Maxime Ripard <[email protected]> wrote:
> > > >
> > > >> Hi,
> > > >>
> > > >> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> > > >>> On Fri, 24 Nov 2023 11:40:06 +0100
> > > >>> Maxime Ripard <[email protected]> wrote:
> > > >>>
> > > >>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > > >>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
> > > >>>>> making names consistent with the drm/gem core code.
> > > >>>>>
> > > >>>>> Reviewed-by: Boris Brezillon <[email protected]>
> > > >>>>> Suggested-by: Boris Brezillon <[email protected]>
> > > >>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
> > > >>>>
> > > >>>> This contradicts my earlier ack on a patch but...
> > > >>>>
> > > >>>>> ---
> > > >>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > > >>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > > >>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > > >>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > > >>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > > >>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > > >>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > > >>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > > >>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > > >>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
> > > >>>>>
> > > >>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > >>>>> index 0d61f2b3e213..154585ddae08 100644
> > > >>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > >>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > >>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > > >>>>> .pin = drm_gem_shmem_object_pin,
> > > >>>>> .unpin = drm_gem_shmem_object_unpin,
> > > >>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > > >>>>> - .vmap = drm_gem_shmem_object_vmap,
> > > >>>>> - .vunmap = drm_gem_shmem_object_vunmap,
> > > >>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
> > > >>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
> > > >>>>
> > > >>>> While I think we should indeed be consistent with the names, I would
> > > >>>> also expect helpers to get the locking right by default.
> > > >>>
> > > >>> Wait, actually I think this patch does what you suggest already. The
> > > >>> _locked() prefix tells the caller: "you should take care of the locking,
> > > >>> I expect the lock to be held when this is hook/function is called". So
> > > >>> helpers without the _locked() prefix take care of the locking (which I
> > > >>> guess matches your 'helpers get the locking right' expectation), and
> > > >>> those with the _locked() prefix don't.
> > > >>
> > > >> What I meant by "getting the locking right" is indeed a bit ambiguous,
> > > >> sorry. What I'm trying to say I guess is that, in this particular case,
> > > >> I don't think you can expect the vmap implementation to be called with
> > > >> or without the locks held. The doc for that function will say that it's
> > > >> either one or the other, but not both.
> > > >>
> > > >> So helpers should follow what is needed to provide a default vmap/vunmap
> > > >> implementation, including what locking is expected from a vmap/vunmap
> > > >> implementation.
> > > >
> > > > Hm, yeah, I think that's a matter of taste. When locking is often
> > > > deferrable, like it is in DRM, I find it beneficial for funcions and
> > > > function pointers to reflect the locking scheme, rather than relying on
> > > > people properly reading the doc, especially when this is the only
> > > > outlier in the group of drm_gem_object_funcs we already have, and it's
> > > > not event documented at the drm_gem_object_funcs level [1] :P.
> > > >
> > > >>
> > > >> If that means that vmap is always called with the locks taken, then
> > > >> drm_gem_shmem_object_vmap can just assume that it will be called with
> > > >> the locks taken and there's no need to mention it in the name (and you
> > > >> can probably sprinkle a couple of lockdep assertion to make sure the
> > > >> locking is indeed consistent).
> > > >
> > > > Things get very confusing when you end up having drm_gem_shmem helpers
> > > > that are suffixed with _locked() to encode the fact locking is the
> > > > caller's responsibility and no suffix for the
> > > > callee-takes-care-of-the-locking semantics, while other helpers that are
> > > > not suffixed at all actually implement the
> > > > caller-should-take-care-of-the-locking semantics.
> > > >
> > > >>
> > > >>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> > > >>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> > > >>>> convert whatever function needs to be converted to the unlock suffix so
> > > >>>> we get a consistent naming.
> > > >>>
> > > >>> That would be an _unlocked() prefix if we do it the other way around. I
> > > >>> think the main confusion comes from the names of the hooks in
> > > >>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> > > >>> are called with the GEM resv lock held, and locking is handled by the
> > > >>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
> > > >>> without the GEM resv lock held, and locking is deferred to the
> > > >>> implementation. As I said, I don't mind prefixing hooks/helpers with
> > > >>> _unlocked() for those that take care of the locking, and no prefix for
> > > >>> those that expects locks to be held, as long as it's consistent, but I
> > > >>> just wanted to make sure we're on the same page :-).
> > > >>
> > > >> What about _nolock then? It's the same number of characters than
> > > >> _locked, plus it expresses what the function is (not) doing, not what
> > > >> context it's supposed to be called in?
> > > >
> > > > Just did a quick
> > > >
> > > > git grep _nolock drivers/gpu/drm
> > > >
> > > > and it returns zero result, where the _locked/_unlocked pattern seems
> > > > to already be widely used. Not saying we shouldn't change that, but it
> > > > doesn't feel like a change we should do as part of this series.
> > > >
> > > > Regards,
> > > >
> > > > Boris
> > > >
> > > > [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155
> > >
> > > I'm fine with dropping the _locked() postfix from the common GEM helpers
> > > and documenting the locking rule in drm_gem. Thank you all for the
> > > suggestions :)
> >
> > Sorry to disagree, but I think a proper function name/suffix is
> > sometimes worth a few lines of doc. Not saying we should do one or the
> > other, I think we should do both. But when I see a function suffixed
> > _locked, _unlocked or _nolock, I can immediately tell if this function
> > defers the locking to the caller or not, and then go check which lock
> > in the function doc.
> >
> > And the second thing I'm not happy with, is the fact we go back to an
> > inconsistent naming in drm_gem_shmem_helper.c, where some functions
> > deferring the locking to the caller are suffixed _locked and others are
> > not, because ultimately, you need a different name when you expose the
> > two variants...
>
> I guess one of the point I was trying to make was also: why do you need
> both?
>
> If one is better than the other (whatever better means here), then all
> drivers should use it.
>
> The counterpart being that if provided a choice, you can be sure that a
> lot of people will get it wrong. The one example I have in mind for
> example was the drm_atomic_helper_commit_tail vs
> drm_atomic_helper_commit_tail_rpm. The latter is now widely used, and
> most of it is cargo-cult.
>
> I think you were referring to the locks being deferred vs taken right
> now before, why do we need to have the choice between the two?

Because DRM locking is complex, and you sometimes have to call some
helpers in a context where you already hold the GEM dma_resv lock.
That's not the case for _v[un]map(), because the core always takes the
lock for us if we call drm_gem_vmap_unlocked(). Now, let's assume we
drop the _locked() suffix on drm_gem_shmem_v[un]map(), but keep it on
other helpers that need both variants. This results in an inconsistent
naming scheme inside the same source file, which I find utterly
confusing.

Note that the initial reason I asked Dmitry if he could add the
_locked suffix to drm_gem_shmem_vmap() is because I started using
drm_gem_shmem_vmap() in powervr, before realizing this version wasn't
taking the lock, and I should have used drm_gem_vmap_unlocked()
instead, so this is not something I'm making up. Not saying the
confusion only comes from the naming, because the various layers of
indirection we have clearly don't help, but having a name reflecting
the fact the locking is deferred to the caller would have helped, I
think.

2023-11-29 15:15:55

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, Nov 29, 2023 at 02:46:09PM +0100, Boris Brezillon wrote:
> On Wed, 29 Nov 2023 14:09:47 +0100
> Maxime Ripard <[email protected]> wrote:
>
> > On Wed, Nov 29, 2023 at 08:53:30AM +0100, Boris Brezillon wrote:
> > > On Wed, 29 Nov 2023 01:05:14 +0300
> > > Dmitry Osipenko <[email protected]> wrote:
> > >
> > > > On 11/28/23 15:37, Boris Brezillon wrote:
> > > > > On Tue, 28 Nov 2023 12:14:42 +0100
> > > > > Maxime Ripard <[email protected]> wrote:
> > > > >
> > > > >> Hi,
> > > > >>
> > > > >> On Fri, Nov 24, 2023 at 11:59:11AM +0100, Boris Brezillon wrote:
> > > > >>> On Fri, 24 Nov 2023 11:40:06 +0100
> > > > >>> Maxime Ripard <[email protected]> wrote:
> > > > >>>
> > > > >>>> On Mon, Oct 30, 2023 at 02:01:43AM +0300, Dmitry Osipenko wrote:
> > > > >>>>> Add locked and remove unlocked postfixes from drm-shmem function names,
> > > > >>>>> making names consistent with the drm/gem core code.
> > > > >>>>>
> > > > >>>>> Reviewed-by: Boris Brezillon <[email protected]>
> > > > >>>>> Suggested-by: Boris Brezillon <[email protected]>
> > > > >>>>> Signed-off-by: Dmitry Osipenko <[email protected]>
> > > > >>>>
> > > > >>>> This contradicts my earlier ack on a patch but...
> > > > >>>>
> > > > >>>>> ---
> > > > >>>>> drivers/gpu/drm/drm_gem_shmem_helper.c | 64 +++++++++----------
> > > > >>>>> drivers/gpu/drm/lima/lima_gem.c | 8 +--
> > > > >>>>> drivers/gpu/drm/panfrost/panfrost_drv.c | 2 +-
> > > > >>>>> drivers/gpu/drm/panfrost/panfrost_gem.c | 6 +-
> > > > >>>>> .../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
> > > > >>>>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 2 +-
> > > > >>>>> drivers/gpu/drm/v3d/v3d_bo.c | 4 +-
> > > > >>>>> drivers/gpu/drm/virtio/virtgpu_object.c | 4 +-
> > > > >>>>> include/drm/drm_gem_shmem_helper.h | 36 +++++------
> > > > >>>>> 9 files changed, 64 insertions(+), 64 deletions(-)
> > > > >>>>>
> > > > >>>>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > > >>>>> index 0d61f2b3e213..154585ddae08 100644
> > > > >>>>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > > >>>>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> > > > >>>>> @@ -43,8 +43,8 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
> > > > >>>>> .pin = drm_gem_shmem_object_pin,
> > > > >>>>> .unpin = drm_gem_shmem_object_unpin,
> > > > >>>>> .get_sg_table = drm_gem_shmem_object_get_sg_table,
> > > > >>>>> - .vmap = drm_gem_shmem_object_vmap,
> > > > >>>>> - .vunmap = drm_gem_shmem_object_vunmap,
> > > > >>>>> + .vmap = drm_gem_shmem_object_vmap_locked,
> > > > >>>>> + .vunmap = drm_gem_shmem_object_vunmap_locked,
> > > > >>>>
> > > > >>>> While I think we should indeed be consistent with the names, I would
> > > > >>>> also expect helpers to get the locking right by default.
> > > > >>>
> > > > >>> Wait, actually I think this patch does what you suggest already. The
> > > > >>> _locked() prefix tells the caller: "you should take care of the locking,
> > > > >>> I expect the lock to be held when this is hook/function is called". So
> > > > >>> helpers without the _locked() prefix take care of the locking (which I
> > > > >>> guess matches your 'helpers get the locking right' expectation), and
> > > > >>> those with the _locked() prefix don't.
> > > > >>
> > > > >> What I meant by "getting the locking right" is indeed a bit ambiguous,
> > > > >> sorry. What I'm trying to say I guess is that, in this particular case,
> > > > >> I don't think you can expect the vmap implementation to be called with
> > > > >> or without the locks held. The doc for that function will say that it's
> > > > >> either one or the other, but not both.
> > > > >>
> > > > >> So helpers should follow what is needed to provide a default vmap/vunmap
> > > > >> implementation, including what locking is expected from a vmap/vunmap
> > > > >> implementation.
> > > > >
> > > > > Hm, yeah, I think that's a matter of taste. When locking is often
> > > > > deferrable, like it is in DRM, I find it beneficial for funcions and
> > > > > function pointers to reflect the locking scheme, rather than relying on
> > > > > people properly reading the doc, especially when this is the only
> > > > > outlier in the group of drm_gem_object_funcs we already have, and it's
> > > > > not event documented at the drm_gem_object_funcs level [1] :P.
> > > > >
> > > > >>
> > > > >> If that means that vmap is always called with the locks taken, then
> > > > >> drm_gem_shmem_object_vmap can just assume that it will be called with
> > > > >> the locks taken and there's no need to mention it in the name (and you
> > > > >> can probably sprinkle a couple of lockdep assertion to make sure the
> > > > >> locking is indeed consistent).
> > > > >
> > > > > Things get very confusing when you end up having drm_gem_shmem helpers
> > > > > that are suffixed with _locked() to encode the fact locking is the
> > > > > caller's responsibility and no suffix for the
> > > > > callee-takes-care-of-the-locking semantics, while other helpers that are
> > > > > not suffixed at all actually implement the
> > > > > caller-should-take-care-of-the-locking semantics.
> > > > >
> > > > >>
> > > > >>>> I'm not sure how reasonable it is, but I think I'd prefer to turn this
> > > > >>>> around and keep the drm_gem_shmem_object_vmap/unmap helpers name, and
> > > > >>>> convert whatever function needs to be converted to the unlock suffix so
> > > > >>>> we get a consistent naming.
> > > > >>>
> > > > >>> That would be an _unlocked() prefix if we do it the other way around. I
> > > > >>> think the main confusion comes from the names of the hooks in
> > > > >>> drm_gem_shmem_funcs. Some of them, like drm_gem_shmem_funcs::v[un]map()
> > > > >>> are called with the GEM resv lock held, and locking is handled by the
> > > > >>> core, others, like drm_gem_shmem_funcs::[un]pin() are called
> > > > >>> without the GEM resv lock held, and locking is deferred to the
> > > > >>> implementation. As I said, I don't mind prefixing hooks/helpers with
> > > > >>> _unlocked() for those that take care of the locking, and no prefix for
> > > > >>> those that expects locks to be held, as long as it's consistent, but I
> > > > >>> just wanted to make sure we're on the same page :-).
> > > > >>
> > > > >> What about _nolock then? It's the same number of characters than
> > > > >> _locked, plus it expresses what the function is (not) doing, not what
> > > > >> context it's supposed to be called in?
> > > > >
> > > > > Just did a quick
> > > > >
> > > > > git grep _nolock drivers/gpu/drm
> > > > >
> > > > > and it returns zero result, where the _locked/_unlocked pattern seems
> > > > > to already be widely used. Not saying we shouldn't change that, but it
> > > > > doesn't feel like a change we should do as part of this series.
> > > > >
> > > > > Regards,
> > > > >
> > > > > Boris
> > > > >
> > > > > [1]https://elixir.bootlin.com/linux/v6.7-rc3/source/include/drm/drm_gem.h#L155
> > > >
> > > > I'm fine with dropping the _locked() postfix from the common GEM helpers
> > > > and documenting the locking rule in drm_gem. Thank you all for the
> > > > suggestions :)
> > >
> > > Sorry to disagree, but I think a proper function name/suffix is
> > > sometimes worth a few lines of doc. Not saying we should do one or the
> > > other, I think we should do both. But when I see a function suffixed
> > > _locked, _unlocked or _nolock, I can immediately tell if this function
> > > defers the locking to the caller or not, and then go check which lock
> > > in the function doc.
> > >
> > > And the second thing I'm not happy with, is the fact we go back to an
> > > inconsistent naming in drm_gem_shmem_helper.c, where some functions
> > > deferring the locking to the caller are suffixed _locked and others are
> > > not, because ultimately, you need a different name when you expose the
> > > two variants...
> >
> > I guess one of the point I was trying to make was also: why do you need
> > both?
> >
> > If one is better than the other (whatever better means here), then all
> > drivers should use it.
> >
> > The counterpart being that if provided a choice, you can be sure that a
> > lot of people will get it wrong. The one example I have in mind for
> > example was the drm_atomic_helper_commit_tail vs
> > drm_atomic_helper_commit_tail_rpm. The latter is now widely used, and
> > most of it is cargo-cult.
> >
> > I think you were referring to the locks being deferred vs taken right
> > now before, why do we need to have the choice between the two?
>
> Because DRM locking is complex, and you sometimes have to call some
> helpers in a context where you already hold the GEM dma_resv lock.
> That's not the case for _v[un]map(), because the core always takes the
> lock for us if we call drm_gem_vmap_unlocked().

Ok

> Now, let's assume we drop the _locked() suffix on
> drm_gem_shmem_v[un]map(), but keep it on other helpers that need both
> variants. This results in an inconsistent naming scheme inside the
> same source file, which I find utterly confusing.
>
> Note that the initial reason I asked Dmitry if he could add the
> _locked suffix to drm_gem_shmem_vmap() is because I started using
> drm_gem_shmem_vmap() in powervr, before realizing this version wasn't
> taking the lock, and I should have used drm_gem_vmap_unlocked()
> instead, so this is not something I'm making up.

Sorry if I gave you the impression I thought that you're making that up,
I'm not.

Thanks for the explanation btw, I think I get what you're saying now:

- drm_gem_shmem_vmap() is never taking the locks because the core
expects to take them before calling them.

- drm_gem_shmem_vunmap() is never taking the locks because the core
expects to take them before calling them.

- Some other code path can still call those helpers in drivers, and the
locking isn't handled by the core anymore.

- We now have _vmap/vunmap_unlocked functions to take those locks for
those code paths

- And the variant names are now confusing, making people use the
lockless version in situations where they should have use the locked
one.

Is that a correct summary?

If so, then I agree that we need to change the name.

We discussed it some more on IRC, and we agree that the "default"
function should handle the locking properly and that's what the most
common case should use.

So that means than drm_gem_shmem_vmap/vunmap() should take the lock
itself, and drm_gem_shmem_vmap/vunmap_nolock/unlocked never does.

I think I'd prefer the nolock variant over unlocked still.

And I also think we can improve the documentation and add lockdep calls
to make sure that the difference between variants is clear in the doc,
and if someone still get confused we can catch it.

Does that sound like a plan?

Maxime


Attachments:
(No filename) (11.02 kB)
signature.asc (235.00 B)
Download all attachments

2023-11-29 15:47:22

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, 29 Nov 2023 16:15:27 +0100
Maxime Ripard <[email protected]> wrote:

> > Now, let's assume we drop the _locked() suffix on
> > drm_gem_shmem_v[un]map(), but keep it on other helpers that need both
> > variants. This results in an inconsistent naming scheme inside the
> > same source file, which I find utterly confusing.
> >
> > Note that the initial reason I asked Dmitry if he could add the
> > _locked suffix to drm_gem_shmem_vmap() is because I started using
> > drm_gem_shmem_vmap() in powervr, before realizing this version wasn't
> > taking the lock, and I should have used drm_gem_vmap_unlocked()
> > instead, so this is not something I'm making up.
>
> Sorry if I gave you the impression I thought that you're making that up,
> I'm not.
>
> Thanks for the explanation btw, I think I get what you're saying now:
>
> - drm_gem_shmem_vmap() is never taking the locks because the core
> expects to take them before calling them.
>
> - drm_gem_shmem_vunmap() is never taking the locks because the core
> expects to take them before calling them.

Correct.

>
> - Some other code path can still call those helpers in drivers, and the
> locking isn't handled by the core anymore.

They can, if they want to v[un]map a BO and they already acquired the
GEM resv lock. But I'm not sure anyone needs to do that yet. The main
reason for exposing these helpers is if one driver needs to overload the
default gem_shmem_funcs.

>
> - We now have _vmap/vunmap_unlocked functions to take those locks for
> those code paths

We don't have drm_gem_shmem_vmap/vunmap_unlocked(), we have
drm_gem_shmem_vmap/vunmap_locked(), which can be called directly, but
are mainly used to populate the drm_gem_object_funcs vtable. If drivers
want to v[un]map in a path where the resv lock is not held, they should
call drm_gem_vmap/vunmap_unlocked() (which are renamed
drm_gem_vmap/vunmap() in patch 1 of this series). Mind the **drm_gem_**
vs **drm_gem_shmem_** difference in the helper names. drm_gem_ helpers
are provided by drm_gem.c and call drm_gem_object_funcs callback, which
are supposed to be populated with drm_gem_shmem helpers.

>
> - And the variant names are now confusing, making people use the
> lockless version in situations where they should have use the locked
> one.

That's what happened to me, at least.

>
> Is that a correct summary?

Almost ;-).

>
> If so, then I agree that we need to change the name.

Cool.

>
> We discussed it some more on IRC, and we agree that the "default"
> function should handle the locking properly and that's what the most
> common case should use.

Agree if by 'default' you mean the lock is always acquired by the
helper, not 'let's decide based on what users do most of the time with
this specific helper', because otherwise we'd be back to a situation
where the name doesn't clearly encode the function behavior.

>
> So that means than drm_gem_shmem_vmap/vunmap() should take the lock
> itself, and drm_gem_shmem_vmap/vunmap_nolock/unlocked never does.

Not sure we have a need for drm_gem_shmem_vmap/vunmap(), but if we ever
add such helpers, they would acquire the resv lock, indeed.

Just to be clear, _nolock == _locked in the current semantics :-).
_nolock means 'don't take the lock', and _locked means 'lock is already
held'.

>
> I think I'd prefer the nolock variant over unlocked still.

Okay, that means s/_locked/_nolock/ in drm_gem_shmem_helpers.{c,h}, I
guess.

>
> And I also think we can improve the documentation and add lockdep calls

Lockdep asserts are already there, I think.

> to make sure that the difference between variants is clear in the doc,
> and if someone still get confused we can catch it.
>
> Does that sound like a plan?

Assuming I understood it correctly, yes. Can you just confirm my
understanding is correct though?

Regards,

Boris

2023-12-04 12:56:25

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Wed, Nov 29, 2023 at 04:47:05PM +0100, Boris Brezillon wrote:
> On Wed, 29 Nov 2023 16:15:27 +0100
> Maxime Ripard <[email protected]> wrote:
>
> > > Now, let's assume we drop the _locked() suffix on
> > > drm_gem_shmem_v[un]map(), but keep it on other helpers that need both
> > > variants. This results in an inconsistent naming scheme inside the
> > > same source file, which I find utterly confusing.
> > >
> > > Note that the initial reason I asked Dmitry if he could add the
> > > _locked suffix to drm_gem_shmem_vmap() is because I started using
> > > drm_gem_shmem_vmap() in powervr, before realizing this version wasn't
> > > taking the lock, and I should have used drm_gem_vmap_unlocked()
> > > instead, so this is not something I'm making up.
> >
> > Sorry if I gave you the impression I thought that you're making that up,
> > I'm not.
> >
> > Thanks for the explanation btw, I think I get what you're saying now:
> >
> > - drm_gem_shmem_vmap() is never taking the locks because the core
> > expects to take them before calling them.
> >
> > - drm_gem_shmem_vunmap() is never taking the locks because the core
> > expects to take them before calling them.
>
> Correct.
>
> >
> > - Some other code path can still call those helpers in drivers, and the
> > locking isn't handled by the core anymore.
>
> They can, if they want to v[un]map a BO and they already acquired the
> GEM resv lock. But I'm not sure anyone needs to do that yet. The main
> reason for exposing these helpers is if one driver needs to overload the
> default gem_shmem_funcs.
>
> >
> > - We now have _vmap/vunmap_unlocked functions to take those locks for
> > those code paths
>
> We don't have drm_gem_shmem_vmap/vunmap_unlocked(), we have
> drm_gem_shmem_vmap/vunmap_locked(), which can be called directly, but
> are mainly used to populate the drm_gem_object_funcs vtable. If drivers
> want to v[un]map in a path where the resv lock is not held, they should
> call drm_gem_vmap/vunmap_unlocked() (which are renamed
> drm_gem_vmap/vunmap() in patch 1 of this series). Mind the **drm_gem_**
> vs **drm_gem_shmem_** difference in the helper names. drm_gem_ helpers
> are provided by drm_gem.c and call drm_gem_object_funcs callback, which
> are supposed to be populated with drm_gem_shmem helpers.
>
> >
> > - And the variant names are now confusing, making people use the
> > lockless version in situations where they should have use the locked
> > one.
>
> That's what happened to me, at least.
>
> >
> > Is that a correct summary?
>
> Almost ;-).
>
> >
> > If so, then I agree that we need to change the name.
>
> Cool.
>
> >
> > We discussed it some more on IRC, and we agree that the "default"
> > function should handle the locking properly and that's what the most
> > common case should use.
>
> Agree if by 'default' you mean the lock is always acquired by the
> helper, not 'let's decide based on what users do most of the time with
> this specific helper', because otherwise we'd be back to a situation
> where the name doesn't clearly encode the function behavior.
>
> >
> > So that means than drm_gem_shmem_vmap/vunmap() should take the lock
> > itself, and drm_gem_shmem_vmap/vunmap_nolock/unlocked never does.
>
> Not sure we have a need for drm_gem_shmem_vmap/vunmap(), but if we ever
> add such helpers, they would acquire the resv lock, indeed.
>
> Just to be clear, _nolock == _locked in the current semantics :-).
> _nolock means 'don't take the lock', and _locked means 'lock is already
> held'.
>
> >
> > I think I'd prefer the nolock variant over unlocked still.
>
> Okay, that means s/_locked/_nolock/ in drm_gem_shmem_helpers.{c,h}, I
> guess.
>
> >
> > And I also think we can improve the documentation and add lockdep calls
>
> Lockdep asserts are already there, I think.
>
> > to make sure that the difference between variants is clear in the doc,
> > and if someone still get confused we can catch it.
> >
> > Does that sound like a plan?
>
> Assuming I understood it correctly, yes. Can you just confirm my
> understanding is correct though?

We are. Sorry for delaying this :)

Maxime


Attachments:
(No filename) (4.17 kB)
signature.asc (235.00 B)
Download all attachments

2023-12-05 11:43:36

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On 12/4/23 15:55, Maxime Ripard wrote:
>> Okay, that means s/_locked/_nolock/ in drm_gem_shmem_helpers.{c,h}, I
>> guess.

DRM subsys and majority of kernel uses common _locked postfix. We should
retain the old naming scheme by using _locked() in DRM. It's not
worthwhile changing the name to a much less popular variant for a no
good reason.

Maxime, are you okay with keeping the _locked name?

--
Best regards,
Dmitry

2023-12-14 18:17:09

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On Tue, Dec 05, 2023 at 02:43:16PM +0300, Dmitry Osipenko wrote:
> On 12/4/23 15:55, Maxime Ripard wrote:
> >> Okay, that means s/_locked/_nolock/ in drm_gem_shmem_helpers.{c,h}, I
> >> guess.
>
> DRM subsys and majority of kernel uses common _locked postfix. We should
> retain the old naming scheme by using _locked() in DRM. It's not
> worthwhile changing the name to a much less popular variant for a no
> good reason.
>
> Maxime, are you okay with keeping the _locked name?

Yeah... I still don't really like it, but you're right that it's best to
remain consistent over my opinion :)

Maxime


Attachments:
(No filename) (616.00 B)
signature.asc (235.00 B)
Download all attachments

2023-12-15 00:42:24

by Dmitry Osipenko

[permalink] [raw]
Subject: Re: [PATCH v18 04/26] drm/shmem-helper: Refactor locked/unlocked functions

On 12/14/23 21:16, Maxime Ripard wrote:
> On Tue, Dec 05, 2023 at 02:43:16PM +0300, Dmitry Osipenko wrote:
>> On 12/4/23 15:55, Maxime Ripard wrote:
>>>> Okay, that means s/_locked/_nolock/ in drm_gem_shmem_helpers.{c,h}, I
>>>> guess.
>>
>> DRM subsys and majority of kernel uses common _locked postfix. We should
>> retain the old naming scheme by using _locked() in DRM. It's not
>> worthwhile changing the name to a much less popular variant for a no
>> good reason.
>>
>> Maxime, are you okay with keeping the _locked name?
>
> Yeah... I still don't really like it, but you're right that it's best to
> remain consistent over my opinion :)
Thanks for the review!

Best regards,
Dmitry