On 17.04.2025 16:49, Boris Brezillon wrote:
> Currently, we pick the MMIO offset based on the size of the pgoff_t
> type seen by the process that manipulates the FD, such that a 32-bit
> process can always map the user MMIO ranges. But this approach doesn't
> work well for emulators like FEX, where the emulator is a 64-bit binary
> which might be executing 32-bit code. In that case, the kernel thinks
> it's the 64-bit process and assumes DRM_PANTHOR_USER_MMIO_OFFSET_64BIT
> is in use, but the UMD library expects DRM_PANTHOR_USER_MMIO_OFFSET_32BIT,
> because it can't mmap() anything above the pgoff_t size.
>
> In order to solve that, we need a way to explicitly set the user MMIO
> offset from the UMD, such that the kernel doesn't have to guess it
> from the TIF_32BIT flag set on user thread. We keep the old behavior
> if DRM_PANTHOR_SET_USER_MMIO_OFFSET is never called.
>
> Changes:
> - Drop the lock/immutable fields and allow SET_USER_MMIO_OFFSET
>   requests to race with mmap() requests
> - Don't do the is_user_mmio_offset test twice in panthor_mmap()
> - Improve the uAPI docs
>
> Signed-off-by: Boris Brezillon <boris.brezil...@collabora.com>

Reviewed-by: Adrián Larumbe <adrian.laru...@collabora.com>

> ---
>  drivers/gpu/drm/panthor/panthor_device.h | 18 ++++++++
>  drivers/gpu/drm/panthor/panthor_drv.c    | 56 +++++++++++++++++-------
>  include/uapi/drm/panthor_drm.h           | 38 ++++++++++++++++
>  3 files changed, 96 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_device.h 
> b/drivers/gpu/drm/panthor/panthor_device.h
> index 4c27b6d85f46..6d8c2d5042f2 100644
> --- a/drivers/gpu/drm/panthor/panthor_device.h
> +++ b/drivers/gpu/drm/panthor/panthor_device.h
> @@ -219,6 +219,24 @@ struct panthor_file {
>       /** @ptdev: Device attached to this file. */
>       struct panthor_device *ptdev;
>
> +     /** @user_mmio: User MMIO related fields. */
> +     struct {
> +             /**
> +              * @offset: Offset used for user MMIO mappings.
> +              *
> +              * This offset should not be used to check the type of mapping
> +              * except in panthor_mmap(). After that point, MMIO mapping
> +              * offsets have been adjusted to match
> +              * DRM_PANTHOR_USER_MMIO_OFFSET and this macro should be used
> +              * instead.
> +              * Make sure this rule is followed at all times, because
> +              * userspace is in control of the offset, and can change the
> +              * value behind out back, potentially leading to erronous
> +              * branching happening in kernel space.
> +              */
> +             u64 offset;
> +     } user_mmio;
> +
>       /** @vms: VM pool attached to this file. */
>       struct panthor_vm_pool *vms;
>
> diff --git a/drivers/gpu/drm/panthor/panthor_drv.c 
> b/drivers/gpu/drm/panthor/panthor_drv.c
> index 4d4a52a033f6..aedef2bfa7ac 100644
> --- a/drivers/gpu/drm/panthor/panthor_drv.c
> +++ b/drivers/gpu/drm/panthor/panthor_drv.c
> @@ -1338,6 +1338,20 @@ static int panthor_ioctl_vm_get_state(struct 
> drm_device *ddev, void *data,
>       return 0;
>  }
>
> +static int panthor_ioctl_set_user_mmio_offset(struct drm_device *ddev,
> +                                           void *data, struct drm_file *file)
> +{
> +     struct drm_panthor_set_user_mmio_offset *args = data;
> +     struct panthor_file *pfile = file->driver_priv;
> +
> +     if (args->offset != DRM_PANTHOR_USER_MMIO_OFFSET_32BIT &&
> +         args->offset != DRM_PANTHOR_USER_MMIO_OFFSET_64BIT)
> +             return -EINVAL;
> +
> +     WRITE_ONCE(pfile->user_mmio.offset, args->offset);
> +     return 0;
> +}
> +
>  static int
>  panthor_open(struct drm_device *ddev, struct drm_file *file)
>  {
> @@ -1355,6 +1369,18 @@ panthor_open(struct drm_device *ddev, struct drm_file 
> *file)
>       }
>
>       pfile->ptdev = ptdev;
> +     pfile->user_mmio.offset = DRM_PANTHOR_USER_MMIO_OFFSET;
> +
> +#ifdef CONFIG_ARM64
> +     /*
> +      * With 32-bit systems being limited by the 32-bit representation of
> +      * mmap2's pgoffset field, we need to make the MMIO offset arch
> +      * specific.
> +      */
> +     if (test_tsk_thread_flag(current, TIF_32BIT))
> +             pfile->user_mmio.offset = DRM_PANTHOR_USER_MMIO_OFFSET_32BIT;
> +#endif
> +
>
>       ret = panthor_vm_pool_create(pfile);
>       if (ret)
> @@ -1407,6 +1433,7 @@ static const struct drm_ioctl_desc 
> panthor_drm_driver_ioctls[] = {
>       PANTHOR_IOCTL(TILER_HEAP_CREATE, tiler_heap_create, DRM_RENDER_ALLOW),
>       PANTHOR_IOCTL(TILER_HEAP_DESTROY, tiler_heap_destroy, DRM_RENDER_ALLOW),
>       PANTHOR_IOCTL(GROUP_SUBMIT, group_submit, DRM_RENDER_ALLOW),
> +     PANTHOR_IOCTL(SET_USER_MMIO_OFFSET, set_user_mmio_offset, 
> DRM_RENDER_ALLOW),
>  };
>
>  static int panthor_mmap(struct file *filp, struct vm_area_struct *vma)
> @@ -1415,30 +1442,26 @@ static int panthor_mmap(struct file *filp, struct 
> vm_area_struct *vma)
>       struct panthor_file *pfile = file->driver_priv;
>       struct panthor_device *ptdev = pfile->ptdev;
>       u64 offset = (u64)vma->vm_pgoff << PAGE_SHIFT;
> +     u64 user_mmio_offset;
>       int ret, cookie;
>
>       if (!drm_dev_enter(file->minor->dev, &cookie))
>               return -ENODEV;
>
> -#ifdef CONFIG_ARM64
> -     /*
> -      * With 32-bit systems being limited by the 32-bit representation of
> -      * mmap2's pgoffset field, we need to make the MMIO offset arch
> -      * specific. This converts a user MMIO offset into something the kernel
> -      * driver understands.
> +     /* Adjust the user MMIO offset to match the offset used kernel side.
> +      * We use a local variable with a READ_ONCE() here to make sure
> +      * the user_mmio_offset we use for the is_user_mmio_mapping() check
> +      * hasn't changed when we do the offset adjustment.
>        */
> -     if (test_tsk_thread_flag(current, TIF_32BIT) &&
> -         offset >= DRM_PANTHOR_USER_MMIO_OFFSET_32BIT) {
> -             offset += DRM_PANTHOR_USER_MMIO_OFFSET_64BIT -
> -                       DRM_PANTHOR_USER_MMIO_OFFSET_32BIT;
> +     user_mmio_offset = READ_ONCE(pfile->user_mmio.offset);
> +     if (offset >= user_mmio_offset) {
> +             offset -= user_mmio_offset;
> +             offset += DRM_PANTHOR_USER_MMIO_OFFSET;
>               vma->vm_pgoff = offset >> PAGE_SHIFT;
> -     }
> -#endif
> -
> -     if (offset >= DRM_PANTHOR_USER_MMIO_OFFSET)
>               ret = panthor_device_mmap_io(ptdev, vma);
> -     else
> +     } else {
>               ret = drm_gem_mmap(filp, vma);
> +     }
>
>       drm_dev_exit(cookie);
>       return ret;
> @@ -1516,6 +1539,7 @@ static void panthor_debugfs_init(struct drm_minor 
> *minor)
>   * - 1.2 - adds DEV_QUERY_GROUP_PRIORITIES_INFO query
>   *       - adds PANTHOR_GROUP_PRIORITY_REALTIME priority
>   * - 1.3 - adds DRM_PANTHOR_GROUP_STATE_INNOCENT flag
> + * - 1.4 - adds DRM_PANTHOR_SET_USER_MMIO_OFFSET ioctl
>   */
>  static const struct drm_driver panthor_drm_driver = {
>       .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ |
> @@ -1529,7 +1553,7 @@ static const struct drm_driver panthor_drm_driver = {
>       .name = "panthor",
>       .desc = "Panthor DRM driver",
>       .major = 1,
> -     .minor = 3,
> +     .minor = 4,
>
>       .gem_create_object = panthor_gem_create_object,
>       .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
> diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
> index dbb907eae443..1d1282f2c9fa 100644
> --- a/include/uapi/drm/panthor_drm.h
> +++ b/include/uapi/drm/panthor_drm.h
> @@ -127,6 +127,20 @@ enum drm_panthor_ioctl_id {
>
>       /** @DRM_PANTHOR_TILER_HEAP_DESTROY: Destroy a tiler heap. */
>       DRM_PANTHOR_TILER_HEAP_DESTROY,
> +
> +     /**
> +      * @DRM_PANTHOR_SET_USER_MMIO_OFFSET: Set the offset to use as the user 
> MMIO offset.
> +      *
> +      * The default behavior is to pick the MMIO offset based on the size of 
> the pgoff_t
> +      * type seen by the process that manipulates the FD, such that a 32-bit 
> process can
> +      * always map the user MMIO ranges. But this approach doesn't work well 
> for emulators
> +      * like FEX, where the emulator is an 64-bit binary which might be 
> executing 32-bit
> +      * code. In that case, the kernel thinks it's the 64-bit process and 
> assumes
> +      * DRM_PANTHOR_USER_MMIO_OFFSET_64BIT is in use, but the UMD library 
> expects
> +      * DRM_PANTHOR_USER_MMIO_OFFSET_32BIT, because it can't mmap() anything 
> above the
> +      * pgoff_t size.
> +      */
> +     DRM_PANTHOR_SET_USER_MMIO_OFFSET,
>  };
>
>  /**
> @@ -980,6 +994,28 @@ struct drm_panthor_tiler_heap_destroy {
>       __u32 pad;
>  };
>
> +/**
> + * struct drm_panthor_set_user_mmio_offset - Arguments passed to
> + * DRM_IOCTL_PANTHOR_SET_USER_MMIO_OFFSET
> + *
> + * This ioctl is only really useful if you want to support userspace
> + * CPU emulation environments where the size of an unsigned long differs
> + * between the host and the guest architectures.
> + */
> +struct drm_panthor_set_user_mmio_offset {
> +     /**
> +      * @offset: User MMIO offset to use.
> +      *
> +      * Must be either DRM_PANTHOR_USER_MMIO_OFFSET_32BIT or
> +      * DRM_PANTHOR_USER_MMIO_OFFSET_64BIT.
> +      *
> +      * Use DRM_PANTHOR_USER_MMIO_OFFSET (which selects OFFSET_32BIT or
> +      * OFFSET_64BIT based on the size of an unsigned long) unless you
> +      * have a very good reason to overrule this decision.
> +      */
> +     __u64 offset;
> +};
> +
>  /**
>   * DRM_IOCTL_PANTHOR() - Build a Panthor IOCTL number
>   * @__access: Access type. Must be R, W or RW.
> @@ -1022,6 +1058,8 @@ enum {
>               DRM_IOCTL_PANTHOR(WR, TILER_HEAP_CREATE, tiler_heap_create),
>       DRM_IOCTL_PANTHOR_TILER_HEAP_DESTROY =
>               DRM_IOCTL_PANTHOR(WR, TILER_HEAP_DESTROY, tiler_heap_destroy),
> +     DRM_IOCTL_PANTHOR_SET_USER_MMIO_OFFSET =
> +             DRM_IOCTL_PANTHOR(WR, SET_USER_MMIO_OFFSET, 
> set_user_mmio_offset),
>  };
>
>  #if defined(__cplusplus)
> --
> 2.49.0

Adrian Larumbe

Reply via email to