On Wed, Aug 18, 2021 at 03:38:22PM +0800, Desmond Cheong Zhi Xi wrote:
> In a future patch, a read lock on drm_device.master_rwsem is
> held in the ioctl handler before the check for ioctl
> permissions. However, this produces the following lockdep splat:
>
> ======================================================
> WARNING: possible circular locking dependency detected
> 5.14.0-rc6-CI-Patchwork_20831+ #1 Tainted: G U
> ------------------------------------------------------
> kms_lease/1752 is trying to acquire lock:
> ffffffff827bad88 (drm_global_mutex){+.+.}-{3:3}, at: drm_open+0x64/0x280
>
> but task is already holding lock:
> ffff88812e350108 (&dev->master_rwsem){++++}-{3:3}, at:
> drm_ioctl_kernel+0xfb/0x1a0
>
> which lock already depends on the new lock.
>
> the existing dependency chain (in reverse order) is:
>
> -> #2 (&dev->master_rwsem){++++}-{3:3}:
> lock_acquire+0xd3/0x310
> down_read+0x3b/0x140
> drm_master_internal_acquire+0x1d/0x60
> drm_client_modeset_commit+0x10/0x40
> __drm_fb_helper_restore_fbdev_mode_unlocked+0x88/0xb0
> drm_fb_helper_set_par+0x34/0x40
> intel_fbdev_set_par+0x11/0x40 [i915]
> fbcon_init+0x270/0x4f0
> visual_init+0xc6/0x130
> do_bind_con_driver+0x1de/0x2c0
> do_take_over_console+0x10e/0x180
> do_fbcon_takeover+0x53/0xb0
> register_framebuffer+0x22d/0x310
> __drm_fb_helper_initial_config_and_unlock+0x36c/0x540
> intel_fbdev_initial_config+0xf/0x20 [i915]
> async_run_entry_fn+0x28/0x130
> process_one_work+0x26d/0x5c0
> worker_thread+0x37/0x390
> kthread+0x13b/0x170
> ret_from_fork+0x1f/0x30
>
> -> #1 (&helper->lock){+.+.}-{3:3}:
> lock_acquire+0xd3/0x310
> __mutex_lock+0xa8/0x930
> __drm_fb_helper_restore_fbdev_mode_unlocked+0x44/0xb0
> intel_fbdev_restore_mode+0x2b/0x50 [i915]
> drm_lastclose+0x27/0x50
> drm_release_noglobal+0x42/0x60
> __fput+0x9e/0x250
> task_work_run+0x6b/0xb0
> exit_to_user_mode_prepare+0x1c5/0x1d0
> syscall_exit_to_user_mode+0x19/0x50
> do_syscall_64+0x46/0xb0
> entry_SYSCALL_64_after_hwframe+0x44/0xae
>
> -> #0 (drm_global_mutex){+.+.}-{3:3}:
> validate_chain+0xb39/0x1e70
> __lock_acquire+0x5a1/0xb70
> lock_acquire+0xd3/0x310
> __mutex_lock+0xa8/0x930
> drm_open+0x64/0x280
> drm_stub_open+0x9f/0x100
> chrdev_open+0x9f/0x1d0
> do_dentry_open+0x14a/0x3a0
> dentry_open+0x53/0x70
> drm_mode_create_lease_ioctl+0x3cb/0x970
> drm_ioctl_kernel+0xc9/0x1a0
> drm_ioctl+0x201/0x3d0
> __x64_sys_ioctl+0x6a/0xa0
> do_syscall_64+0x37/0xb0
> entry_SYSCALL_64_after_hwframe+0x44/0xae
>
> other info that might help us debug this:
> Chain exists of:
> drm_global_mutex --> &helper->lock --> &dev->master_rwsem
> Possible unsafe locking scenario:
> CPU0 CPU1
> ---- ----
> lock(&dev->master_rwsem);
> lock(&helper->lock);
> lock(&dev->master_rwsem);
> lock(drm_global_mutex);
>
> *** DEADLOCK ***
>
> The lock hierarchy inversion happens because we grab the
> drm_global_mutex while already holding on to master_rwsem. To avoid
> this, we do some prep work to grab the drm_global_mutex before
> checking for ioctl permissions.
>
> At the same time, we update the check for the global mutex to use the
> drm_dev_needs_global_mutex helper function.
This is intentional, essentially we force all non-legacy drivers to have
unlocked ioctl (otherwise everyone forgets to set that flag).
For non-legacy drivers the global lock only ensures ordering between
drm_open and lastclose (I think at least), and between
drm_dev_register/unregister and the backwards ->load/unload callbacks
(which are called in the wrong place, but we cannot fix that for legacy
drivers).
->load/unload should be completely unused (maybe radeon still uses it),
and ->lastclose is also on the decline.
Maybe we should update the comment of drm_global_mutex to explain what it
protects and why.
I'm also confused how this patch connects to the splat, since for i915 we
shouldn't be taking the drm_global_lock here at all. The problem seems to
be the drm_open_helper when we create a new lease, which is an entirely
different can of worms.
I'm honestly not sure how to best do that, but we should be able to create
a file and then call drm_open_helper directly, or well a version of that
which never takes the drm_global_mutex. Because that is not needed for
nested drm_file opening:
- legacy drivers never go down this path because leases are only supported
with modesetting, and modesetting is only supported for non-legacy
drivers
- the races against dev->open_count due to last_close or ->load callbacks
don't matter, because for the entire ioctl we already have an open
drm_file and that wont disappear.
So this should work, but I'm not entirely sure how to make it work.
-Daniel
> Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx(a)gmail.com>
> ---
> drivers/gpu/drm/drm_ioctl.c | 18 +++++++++---------
> 1 file changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index 880fc565d599..2cb57378a787 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -779,19 +779,19 @@ long drm_ioctl_kernel(struct file *file, drm_ioctl_t *func, void *kdata,
> if (drm_dev_is_unplugged(dev))
> return -ENODEV;
>
> + /* Enforce sane locking for modern driver ioctls. */
> + if (unlikely(drm_dev_needs_global_mutex(dev)) && !(flags & DRM_UNLOCKED))
> + mutex_lock(&drm_global_mutex);
> +
> retcode = drm_ioctl_permit(flags, file_priv);
> if (unlikely(retcode))
> - return retcode;
> + goto out;
>
> - /* Enforce sane locking for modern driver ioctls. */
> - if (likely(!drm_core_check_feature(dev, DRIVER_LEGACY)) ||
> - (flags & DRM_UNLOCKED))
> - retcode = func(dev, kdata, file_priv);
> - else {
> - mutex_lock(&drm_global_mutex);
> - retcode = func(dev, kdata, file_priv);
> + retcode = func(dev, kdata, file_priv);
> +
> +out:
> + if (unlikely(drm_dev_needs_global_mutex(dev)) && !(flags & DRM_UNLOCKED))
> mutex_unlock(&drm_global_mutex);
> - }
> return retcode;
> }
> EXPORT_SYMBOL(drm_ioctl_kernel);
> --
> 2.25.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
On Wed, Aug 18, 2021 at 03:38:19PM +0800, Desmond Cheong Zhi Xi wrote:
> There are three areas where we dereference struct drm_master without
> checking if the pointer is non-NULL.
>
> 1. drm_getmagic is called from the ioctl_handler. Since
> DRM_IOCTL_GET_MAGIC has no ioctl flags, drm_getmagic is run without
> any check that drm_file.master has been set.
>
> 2. Similarly, drm_getunique is called from the ioctl_handler, but
> DRM_IOCTL_GET_UNIQUE has no ioctl flags. So there is no guarantee that
> drm_file.master has been set.
I think the above two are impossible, due to the refcounting rules for
struct file.
> 3. drm_master_release can also be called without having a
> drm_file.master set. Here is one error path:
> drm_open():
> drm_open_helper():
> drm_master_open():
> drm_new_set_master(); <--- returns -ENOMEM,
> drm_file.master not set
> drm_file_free():
> drm_master_release(); <--- NULL ptr dereference
> (file_priv->master->magic_map)
>
> Fix these by checking if the master pointers are NULL before use.
>
> Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx(a)gmail.com>
> ---
> drivers/gpu/drm/drm_auth.c | 16 ++++++++++++++--
> drivers/gpu/drm/drm_ioctl.c | 5 +++++
> 2 files changed, 19 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
> index f9267b21556e..b7230604496b 100644
> --- a/drivers/gpu/drm/drm_auth.c
> +++ b/drivers/gpu/drm/drm_auth.c
> @@ -95,11 +95,18 @@ EXPORT_SYMBOL(drm_is_current_master);
> int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
> {
> struct drm_auth *auth = data;
> + struct drm_master *master;
> int ret = 0;
>
> mutex_lock(&dev->master_mutex);
> + master = file_priv->master;
> + if (!master) {
> + mutex_unlock(&dev->master_mutex);
> + return -EINVAL;
> + }
> +
> if (!file_priv->magic) {
> - ret = idr_alloc(&file_priv->master->magic_map, file_priv,
> + ret = idr_alloc(&master->magic_map, file_priv,
> 1, 0, GFP_KERNEL);
> if (ret >= 0)
> file_priv->magic = ret;
> @@ -355,8 +362,12 @@ void drm_master_release(struct drm_file *file_priv)
>
> mutex_lock(&dev->master_mutex);
> master = file_priv->master;
> +
> + if (!master)
> + goto unlock;
This is a bit convoluted, since we're in the single-threaded release path
we don't need any locking for file_priv related things. Therefore we can
pull the master check out and just directly return.
But since it's a bit surprising maybe a comment that this can happen when
drm_master_open in drm_open_helper fails?
Another option, and maybe cleaner, would be to move the drm_master_release
from drm_file_free into drm_close_helper. That would be fully symmetrical
and should also fix the bug here?
-Daniel
> +
> if (file_priv->magic)
> - idr_remove(&file_priv->master->magic_map, file_priv->magic);
> + idr_remove(&master->magic_map, file_priv->magic);
>
> if (!drm_is_current_master_locked(file_priv))
> goto out;
> @@ -379,6 +390,7 @@ void drm_master_release(struct drm_file *file_priv)
> drm_master_put(&file_priv->master);
> spin_unlock(&dev->master_lookup_lock);
> }
> +unlock:
> mutex_unlock(&dev->master_mutex);
> }
>
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index 26f3a9ede8fe..4d029d3061d9 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -121,6 +121,11 @@ int drm_getunique(struct drm_device *dev, void *data,
>
> mutex_lock(&dev->master_mutex);
> master = file_priv->master;
> + if (!master) {
> + mutex_unlock(&dev->master_mutex);
> + return -EINVAL;
> + }
> +
> if (u->unique_len >= master->unique_len) {
> if (copy_to_user(u->unique, master->unique, master->unique_len)) {
> mutex_unlock(&dev->master_mutex);
> --
> 2.25.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
On Wed, Aug 18, 2021 at 03:38:17PM +0800, Desmond Cheong Zhi Xi wrote:
> When drm_file.master changes value, the corresponding
> drm_device.master_lookup_lock should be held.
>
> In drm_master_release, a call to drm_master_put sets the
> file_priv->master to NULL, so we protect this section with
> drm_device.master_lookup_lock.
>
> Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx(a)gmail.com>
At this points all refcounts to drm_file have disappeared, so yeah this is
a lockless access, but also no one can observe it anymore. See also next
patch.
Hence I think the current code is fine.
-Daniel
> ---
> drivers/gpu/drm/drm_auth.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
> index 8efb58aa7d95..8c0e0dba1611 100644
> --- a/drivers/gpu/drm/drm_auth.c
> +++ b/drivers/gpu/drm/drm_auth.c
> @@ -373,8 +373,11 @@ void drm_master_release(struct drm_file *file_priv)
> }
>
> /* drop the master reference held by the file priv */
> - if (file_priv->master)
> + if (file_priv->master) {
> + spin_lock(&dev->master_lookup_lock);
> drm_master_put(&file_priv->master);
> + spin_unlock(&dev->master_lookup_lock);
> + }
> mutex_unlock(&dev->master_mutex);
> }
>
> --
> 2.25.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
On Wed, Aug 18, 2021 at 03:38:18PM +0800, Desmond Cheong Zhi Xi wrote:
> There is a window after calling drm_master_release, and before a file
> is freed, where drm_file can have is_master set to true, but both the
> drm_file and drm_device have no master.
>
> This could result in wrongly approving permissions in
> drm_is_current_master_locked. Add a check that fpriv->master is
> non-NULl to guard against this scenario.
>
> Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx(a)gmail.com>
This should be impossible, drm_master_release is only called when the
struct file is released, which means all ioctls and anything else have
finished (they hold a temporary reference).
fpriv->master can change (if the drm_file becomes newly minted master and
wasnt one before through the setmaster ioctl), but it cannot become NULL
before it's completely gone from the system.
-Daniel
> ---
> drivers/gpu/drm/drm_auth.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
> index 8c0e0dba1611..f9267b21556e 100644
> --- a/drivers/gpu/drm/drm_auth.c
> +++ b/drivers/gpu/drm/drm_auth.c
> @@ -66,7 +66,8 @@ static bool drm_is_current_master_locked(struct drm_file *fpriv)
> lockdep_assert_once(lockdep_is_held(&fpriv->minor->dev->master_lookup_lock) ||
> lockdep_is_held(&fpriv->minor->dev->master_mutex));
>
> - return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
> + return (fpriv->is_master && fpriv->master &&
> + drm_lease_owner(fpriv->master) == fpriv->minor->dev->master);
> }
>
> /**
> --
> 2.25.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
From: Rob Clark <robdclark(a)chromium.org>
Based on discussion from a previous series[1] to add a "boost" mechanism
when, for example, vblank deadlines are missed. Instead of a boost
callback, this approach adds a way to set a deadline on the fence, by
which the waiter would like to see the fence signalled.
I've not yet had a chance to re-work the drm/msm part of this, but
wanted to send this out as an RFC in case I don't have a chance to
finish the drm/msm part this week.
Original description:
In some cases, like double-buffered rendering, missing vblanks can
trick the GPU into running at a lower frequence, when really we
want to be running at a higher frequency to not miss the vblanks
in the first place.
This is partially inspired by a trick i915 does, but implemented
via dma-fence for a couple of reasons:
1) To continue to be able to use the atomic helpers
2) To support cases where display and gpu are different drivers
[1] https://patchwork.freedesktop.org/series/90331/
v1: https://patchwork.freedesktop.org/series/93035/
v2: Move filtering out of later deadlines to fence implementation
to avoid increasing the size of dma_fence
Rob Clark (5):
dma-fence: Add deadline awareness
drm/vblank: Add helper to get next vblank time
drm/atomic-helper: Set fence deadline for vblank
drm/scheduler: Add fence deadline support
drm/msm: Add deadline based boost support
drivers/dma-buf/dma-fence.c | 20 +++++++
drivers/gpu/drm/drm_atomic_helper.c | 36 ++++++++++++
drivers/gpu/drm/drm_vblank.c | 31 ++++++++++
drivers/gpu/drm/msm/msm_fence.c | 76 +++++++++++++++++++++++++
drivers/gpu/drm/msm/msm_fence.h | 20 +++++++
drivers/gpu/drm/msm/msm_gpu.h | 1 +
drivers/gpu/drm/msm/msm_gpu_devfreq.c | 20 +++++++
drivers/gpu/drm/scheduler/sched_fence.c | 25 ++++++++
drivers/gpu/drm/scheduler/sched_main.c | 3 +
include/drm/drm_vblank.h | 1 +
include/drm/gpu_scheduler.h | 6 ++
include/linux/dma-fence.h | 16 ++++++
12 files changed, 255 insertions(+)
--
2.31.1
On Fri, Aug 13, 2021 at 01:41:26PM +0200, Paul Cercueil wrote:
> Hi,
>
> A few months ago we (ADI) tried to upstream the interface we use with our
> high-speed ADCs and DACs. It is a system with custom ioctls on the iio
> device node to dequeue and enqueue buffers (allocated with
> dma_alloc_coherent), that can then be mmap'd by userspace applications.
> Anyway, it was ultimately denied entry [1]; this API was okay in ~2014 when
> it was designed but it feels like re-inventing the wheel in 2021.
>
> Back to the drawing table, and we'd like to design something that we can
> actually upstream. This high-speed interface looks awfully similar to
> DMABUF, so we may try to implement a DMABUF interface for IIO, unless
> someone has a better idea.
To me this does sound a lot like a dma buf use case. The interesting
question to me is how to signal arrival of new data, or readyness to
consume more data. I suspect that people that are actually using
dmabuf heavily at the moment (dri/media folks) might be able to chime
in a little more on that.
> Our first usecase is, we want userspace applications to be able to dequeue
> buffers of samples (from ADCs), and/or enqueue buffers of samples (for
> DACs), and to be able to manipulate them (mmapped buffers). With a DMABUF
> interface, I guess the userspace application would dequeue a dma buffer
> from the driver, mmap it, read/write the data, unmap it, then enqueue it to
> the IIO driver again so that it can be disposed of. Does that sound sane?
>
> Our second usecase is - and that's where things get tricky - to be able to
> stream the samples to another computer for processing, over Ethernet or
> USB. Our typical setup is a high-speed ADC/DAC on a dev board with a FPGA
> and a weak soft-core or low-power CPU; processing the data in-situ is not
> an option. Copying the data from one buffer to another is not an option
> either (way too slow), so we absolutely want zero-copy.
>
> Usual userspace zero-copy techniques (vmsplice+splice, MSG_ZEROCOPY etc)
> don't really work with mmapped kernel buffers allocated for DMA [2] and/or
> have a huge overhead, so the way I see it, we would also need DMABUF
> support in both the Ethernet stack and USB (functionfs) stack. However, as
> far as I understood, DMABUF is mostly a DRM/V4L2 thing, so I am really not
> sure we have the right idea here.
>
> And finally, there is the new kid in town, io_uring. I am not very literate
> about the topic, but it does not seem to be able to handle DMA buffers
> (yet?). The idea that we could dequeue a buffer of samples from the IIO
> device and send it over the network in one single syscall is appealing,
> though.
Think of io_uring really just as an async syscall layer. It doesn't
replace DMA buffers, but can be used as a different and for some
workloads more efficient way to dispatch syscalls.
Am 13.08.21 um 05:28 schrieb lwt105:
> in line 1503, "dma_fence_put(fence);" drop the reference to fence and may
> cause fence to be released. However, fence is used subsequently in line
> 1510 "fence->error". This may result in an use-after-free bug.
>
> It can be fixed by recording fence->error in an variable before dropping
> the reference to fence and referencing it after dropping.
>
> Signed-off-by: lwt105 <3061522931(a)qq.com>
Good catch.
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
> index 30fa1f61e0e5..99d03180e113 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
> @@ -1486,7 +1486,7 @@ static int amdgpu_cs_wait_all_fences(struct amdgpu_device *adev,
> struct drm_amdgpu_fence *fences)
> {
> uint32_t fence_count = wait->in.fence_count;
> - unsigned int i;
> + unsigned int i, error;
> long r = 1;
Would be nice to have if you could reuse the "r" variable here instead
of a new one.
Regards,
Christian.
>
> for (i = 0; i < fence_count; i++) {
> @@ -1500,6 +1500,7 @@ static int amdgpu_cs_wait_all_fences(struct amdgpu_device *adev,
> continue;
>
> r = dma_fence_wait_timeout(fence, true, timeout);
> + error = fence->error;
> dma_fence_put(fence);
> if (r < 0)
> return r;
> @@ -1507,8 +1508,8 @@ static int amdgpu_cs_wait_all_fences(struct amdgpu_device *adev,
> if (r == 0)
> break;
>
> - if (fence->error)
> - return fence->error;
> + if (error)
> + return error;
> }
>
> memset(wait, 0, sizeof(*wait));
On Fri, Aug 13, 2021 at 04:54:49PM +0800, Desmond Cheong Zhi Xi wrote:
> In drm_client_modeset.c and drm_fb_helper.c,
> drm_master_internal_{acquire,release} are used to avoid races with DRM
> userspace. These functions hold onto drm_device.master_mutex while
> committing, and bail if there's already a master.
>
> However, ioctls can still race between themselves. A
> time-of-check-to-time-of-use error can occur if an ioctl that changes
> the modeset has its rights revoked after it validates its permissions,
> but before it completes.
>
> There are three ioctls that can affect modesetting permissions:
>
> - DROP_MASTER ioctl removes rights for a master and its leases
>
> - REVOKE_LEASE ioctl revokes rights for a specific lease
>
> - SET_MASTER ioctl sets the device master if the master role hasn't
> been acquired yet
>
> All these races can be avoided by introducing an SRCU that acts as a
> barrier for ioctls that can change modesetting permissions. Processes
> that perform modesetting should hold a read lock on the new
> drm_device.master_barrier_srcu, and ioctls that change these
> permissions should call synchronize_srcu before returning.
>
> This ensures that any process that might have seen old permissions are
> flushed out before DROP_MASTER/REVOKE_LEASE/SET_MASTER ioctls return
> to userspace.
>
> Reported-by: Daniel Vetter <daniel.vetter(a)ffwll.ch>
> Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx(a)gmail.com>
This looks pretty solid, but I think there's one gap where we can still
race. Scenario.
Process A has a drm fd with master rights and two threads:
- thread 1 does a long-running display operation (like a modeset or
whatever)
- thread 2 does a drop-master
Then we start a new process B, which acquires master in drm_open (there is
no other one left). This is like setmaster ioctl, but your
DRM_MASTER_FLUSH bit doesn't work there.
The other thing is that for modeset stuff (which this all is) srcu is
probably massive overkill, and a simple rwsem should be good enough too.
Maybe even better, since the rwsem guarantees that no new reader can start
once you try to acquire the write side.
Finally, and this is a bit a bikeshed: I don't like much how
DRM_MASTER_FLUSH leaks the need of these very few places into the very
core drm_ioctl function. One idea I had was to use task_work in a special
function, roughly
void master_flush()
{
down_write(master_rwsem);
up_write(master_rwms);
}
void drm_master_flush()
{
init_task_work(fpriv->master_flush_work, master_flush)
task_work_add(fpriv->master_flush_work);
/* if task_work_add fails we're exiting, at which point the lack
* of master flush doesn't matter);
}
And maybe put a comment above the function explaining why and how this
works.
We could even do a drm_master_unlock_and_flush helper, since that's really
what everyone wants, and it would make it very clear which master state
changes need this flush. Instead of setting a flag bit in an ioctl table
very far away ...
Thoughts?
-Daniel
> ---
> drivers/gpu/drm/drm_auth.c | 17 ++++++++++++++---
> drivers/gpu/drm/drm_client_modeset.c | 10 ++++++----
> drivers/gpu/drm/drm_drv.c | 2 ++
> drivers/gpu/drm/drm_fb_helper.c | 20 ++++++++++++--------
> drivers/gpu/drm/drm_internal.h | 5 +++--
> drivers/gpu/drm/drm_ioctl.c | 25 +++++++++++++++++++++----
> include/drm/drm_device.h | 11 +++++++++++
> include/drm/drm_ioctl.h | 7 +++++++
> 8 files changed, 76 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
> index 60a6b21474b1..004506608e76 100644
> --- a/drivers/gpu/drm/drm_auth.c
> +++ b/drivers/gpu/drm/drm_auth.c
> @@ -29,6 +29,7 @@
> */
>
> #include <linux/slab.h>
> +#include <linux/srcu.h>
>
> #include <drm/drm_auth.h>
> #include <drm/drm_drv.h>
> @@ -448,21 +449,31 @@ void drm_master_put(struct drm_master **master)
> EXPORT_SYMBOL(drm_master_put);
>
> /* Used by drm_client and drm_fb_helper */
> -bool drm_master_internal_acquire(struct drm_device *dev)
> +bool drm_master_internal_acquire(struct drm_device *dev, int *idx)
> {
> + *idx = srcu_read_lock(&dev->master_barrier_srcu);
> +
> mutex_lock(&dev->master_mutex);
> if (dev->master) {
> mutex_unlock(&dev->master_mutex);
> + srcu_read_unlock(&dev->master_barrier_srcu, *idx);
> return false;
> }
> + mutex_unlock(&dev->master_mutex);
>
> return true;
> }
> EXPORT_SYMBOL(drm_master_internal_acquire);
>
> /* Used by drm_client and drm_fb_helper */
> -void drm_master_internal_release(struct drm_device *dev)
> +void drm_master_internal_release(struct drm_device *dev, int idx)
> {
> - mutex_unlock(&dev->master_mutex);
> + srcu_read_unlock(&dev->master_barrier_srcu, idx);
> }
> EXPORT_SYMBOL(drm_master_internal_release);
> +
> +/* Used by drm_ioctl */
> +void drm_master_flush(struct drm_device *dev)
> +{
> + synchronize_srcu(&dev->master_barrier_srcu);
> +}
> diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c
> index ced09c7c06f9..9885f36f71b7 100644
> --- a/drivers/gpu/drm/drm_client_modeset.c
> +++ b/drivers/gpu/drm/drm_client_modeset.c
> @@ -1165,13 +1165,14 @@ int drm_client_modeset_commit(struct drm_client_dev *client)
> {
> struct drm_device *dev = client->dev;
> int ret;
> + int idx;
>
> - if (!drm_master_internal_acquire(dev))
> + if (!drm_master_internal_acquire(dev, &idx))
> return -EBUSY;
>
> ret = drm_client_modeset_commit_locked(client);
>
> - drm_master_internal_release(dev);
> + drm_master_internal_release(dev, idx);
>
> return ret;
> }
> @@ -1215,8 +1216,9 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode)
> {
> struct drm_device *dev = client->dev;
> int ret = 0;
> + int idx;
>
> - if (!drm_master_internal_acquire(dev))
> + if (!drm_master_internal_acquire(dev, &idx))
> return -EBUSY;
>
> mutex_lock(&client->modeset_mutex);
> @@ -1226,7 +1228,7 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode)
> drm_client_modeset_dpms_legacy(client, mode);
> mutex_unlock(&client->modeset_mutex);
>
> - drm_master_internal_release(dev);
> + drm_master_internal_release(dev, idx);
>
> return ret;
> }
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 7a5097467ba5..c313f0674db3 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -574,6 +574,7 @@ static void drm_dev_init_release(struct drm_device *dev, void *res)
> mutex_destroy(&dev->clientlist_mutex);
> mutex_destroy(&dev->filelist_mutex);
> mutex_destroy(&dev->struct_mutex);
> + cleanup_srcu_struct(&dev->master_barrier_srcu);
> drm_legacy_destroy_members(dev);
> }
>
> @@ -612,6 +613,7 @@ static int drm_dev_init(struct drm_device *dev,
> mutex_init(&dev->filelist_mutex);
> mutex_init(&dev->clientlist_mutex);
> mutex_init(&dev->master_mutex);
> + init_srcu_struct(&dev->master_barrier_srcu);
>
> ret = drmm_add_action(dev, drm_dev_init_release, NULL);
> if (ret)
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 3ab078321045..0d594bc15f18 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -1116,13 +1116,14 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
> struct drm_fb_helper *fb_helper = info->par;
> struct drm_device *dev = fb_helper->dev;
> int ret;
> + int idx;
>
> if (oops_in_progress)
> return -EBUSY;
>
> mutex_lock(&fb_helper->lock);
>
> - if (!drm_master_internal_acquire(dev)) {
> + if (!drm_master_internal_acquire(dev, &idx)) {
> ret = -EBUSY;
> goto unlock;
> }
> @@ -1136,7 +1137,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
> ret = setcmap_legacy(cmap, info);
> mutex_unlock(&fb_helper->client.modeset_mutex);
>
> - drm_master_internal_release(dev);
> + drm_master_internal_release(dev, idx);
> unlock:
> mutex_unlock(&fb_helper->lock);
>
> @@ -1160,9 +1161,10 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
> struct drm_device *dev = fb_helper->dev;
> struct drm_crtc *crtc;
> int ret = 0;
> + int idx;
>
> mutex_lock(&fb_helper->lock);
> - if (!drm_master_internal_acquire(dev)) {
> + if (!drm_master_internal_acquire(dev, &idx)) {
> ret = -EBUSY;
> goto unlock;
> }
> @@ -1204,7 +1206,7 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
> ret = -ENOTTY;
> }
>
> - drm_master_internal_release(dev);
> + drm_master_internal_release(dev, idx);
> unlock:
> mutex_unlock(&fb_helper->lock);
> return ret;
> @@ -1474,12 +1476,13 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
> struct drm_fb_helper *fb_helper = info->par;
> struct drm_device *dev = fb_helper->dev;
> int ret;
> + int idx;
>
> if (oops_in_progress)
> return -EBUSY;
>
> mutex_lock(&fb_helper->lock);
> - if (!drm_master_internal_acquire(dev)) {
> + if (!drm_master_internal_acquire(dev, &idx)) {
> ret = -EBUSY;
> goto unlock;
> }
> @@ -1489,7 +1492,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
> else
> ret = pan_display_legacy(var, info);
>
> - drm_master_internal_release(dev);
> + drm_master_internal_release(dev, idx);
> unlock:
> mutex_unlock(&fb_helper->lock);
>
> @@ -1948,6 +1951,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
> int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
> {
> int err = 0;
> + int idx;
>
> if (!drm_fbdev_emulation || !fb_helper)
> return 0;
> @@ -1959,13 +1963,13 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
> return err;
> }
>
> - if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) {
> + if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev, &idx)) {
> fb_helper->delayed_hotplug = true;
> mutex_unlock(&fb_helper->lock);
> return err;
> }
>
> - drm_master_internal_release(fb_helper->dev);
> + drm_master_internal_release(fb_helper->dev, idx);
>
> drm_dbg_kms(fb_helper->dev, "\n");
>
> diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
> index 17f3548c8ed2..578fd2769913 100644
> --- a/drivers/gpu/drm/drm_internal.h
> +++ b/drivers/gpu/drm/drm_internal.h
> @@ -142,8 +142,9 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
> struct drm_file *file_priv);
> int drm_master_open(struct drm_file *file_priv);
> void drm_master_release(struct drm_file *file_priv);
> -bool drm_master_internal_acquire(struct drm_device *dev);
> -void drm_master_internal_release(struct drm_device *dev);
> +bool drm_master_internal_acquire(struct drm_device *dev, int *idx);
> +void drm_master_internal_release(struct drm_device *dev, int idx);
> +void drm_master_flush(struct drm_device *dev);
>
> /* drm_sysfs.c */
> extern struct class *drm_class;
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index be4a52dc4d6f..eb4ec3fab7d1 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -600,8 +600,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
> DRM_LEGACY_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
> DRM_LEGACY_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH),
>
> - DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, 0),
> - DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, 0),
> + DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl,
> + DRM_MASTER_FLUSH),
> + DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl,
> + DRM_MASTER_FLUSH),
>
> DRM_LEGACY_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY),
> DRM_LEGACY_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
> @@ -722,7 +724,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
> DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER),
> DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER),
> DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER),
> - DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER),
> + DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl,
> + DRM_MASTER | DRM_MASTER_FLUSH),
> };
>
> #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
> @@ -781,13 +784,17 @@ long drm_ioctl_kernel(struct file *file, drm_ioctl_t *func, void *kdata,
> struct drm_file *file_priv = file->private_data;
> struct drm_device *dev = file_priv->minor->dev;
> int retcode;
> + int idx;
>
> if (drm_dev_is_unplugged(dev))
> return -ENODEV;
>
> + if (unlikely(flags & DRM_MASTER))
> + idx = srcu_read_lock(&dev->master_barrier_srcu);
> +
> retcode = drm_ioctl_permit(flags, file_priv);
> if (unlikely(retcode))
> - return retcode;
> + goto release_master;
>
> /* Enforce sane locking for modern driver ioctls. */
> if (likely(!drm_core_check_feature(dev, DRIVER_LEGACY)) ||
> @@ -798,6 +805,16 @@ long drm_ioctl_kernel(struct file *file, drm_ioctl_t *func, void *kdata,
> retcode = func(dev, kdata, file_priv);
> mutex_unlock(&drm_global_mutex);
> }
> +
> +release_master:
> + if (unlikely(flags & DRM_MASTER))
> + srcu_read_unlock(&dev->master_barrier_srcu, idx);
> + /* After flushing, processes are guaranteed to see the new master/lease
> + * permissions, and any process which might have seen the old
> + * permissions is guaranteed to have finished.
> + */
> + if (unlikely(flags & DRM_MASTER_FLUSH))
> + drm_master_flush(dev);
> return retcode;
> }
> EXPORT_SYMBOL(drm_ioctl_kernel);
> diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h
> index 604b1d1b2d72..0ac5fdb375f8 100644
> --- a/include/drm/drm_device.h
> +++ b/include/drm/drm_device.h
> @@ -111,6 +111,17 @@ struct drm_device {
> */
> struct drm_master *master;
>
> + /**
> + * @master_barrier_srcu:
> + *
> + * Used to synchronize modesetting rights between multiple users. Users
> + * that can change the modeset or display state must hold an
> + * srcu_read_lock() on @master_barrier_srcu, and ioctls that can change
> + * modesetting rights should call synchronize_srcu() before returning
> + * to userspace.
> + */
> + struct srcu_struct master_barrier_srcu;
> +
> /**
> * @driver_features: per-device driver features
> *
> diff --git a/include/drm/drm_ioctl.h b/include/drm/drm_ioctl.h
> index afb27cb6a7bd..13a68cdcea36 100644
> --- a/include/drm/drm_ioctl.h
> +++ b/include/drm/drm_ioctl.h
> @@ -130,6 +130,13 @@ enum drm_ioctl_flags {
> * not set DRM_AUTH because they do not require authentication.
> */
> DRM_RENDER_ALLOW = BIT(5),
> + /**
> + * @DRM_MASTER_FLUSH:
> + *
> + * This must be set for any ioctl which can change the modesetting
> + * permissions for DRM users.
> + */
> + DRM_MASTER_FLUSH = BIT(6),
> };
>
> /**
> --
> 2.25.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Integrated into the scheduler now and all users converted over.
Signed-off-by: Daniel Vetter <daniel.vetter(a)intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst(a)linux.intel.com>
Cc: Maxime Ripard <mripard(a)kernel.org>
Cc: Thomas Zimmermann <tzimmermann(a)suse.de>
Cc: David Airlie <airlied(a)linux.ie>
Cc: Daniel Vetter <daniel(a)ffwll.ch>
Cc: Sumit Semwal <sumit.semwal(a)linaro.org>
Cc: "Christian König" <christian.koenig(a)amd.com>
Cc: linux-media(a)vger.kernel.org
Cc: linaro-mm-sig(a)lists.linaro.org
---
drivers/gpu/drm/drm_gem.c | 96 ---------------------------------------
include/drm/drm_gem.h | 5 --
2 files changed, 101 deletions(-)
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 09c820045859..37e2e2820f08 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1272,99 +1272,3 @@ drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
ww_acquire_fini(acquire_ctx);
}
EXPORT_SYMBOL(drm_gem_unlock_reservations);
-
-/**
- * drm_gem_fence_array_add - Adds the fence to an array of fences to be
- * waited on, deduplicating fences from the same context.
- *
- * @fence_array: array of dma_fence * for the job to block on.
- * @fence: the dma_fence to add to the list of dependencies.
- *
- * This functions consumes the reference for @fence both on success and error
- * cases.
- *
- * Returns:
- * 0 on success, or an error on failing to expand the array.
- */
-int drm_gem_fence_array_add(struct xarray *fence_array,
- struct dma_fence *fence)
-{
- struct dma_fence *entry;
- unsigned long index;
- u32 id = 0;
- int ret;
-
- if (!fence)
- return 0;
-
- /* Deduplicate if we already depend on a fence from the same context.
- * This lets the size of the array of deps scale with the number of
- * engines involved, rather than the number of BOs.
- */
- xa_for_each(fence_array, index, entry) {
- if (entry->context != fence->context)
- continue;
-
- if (dma_fence_is_later(fence, entry)) {
- dma_fence_put(entry);
- xa_store(fence_array, index, fence, GFP_KERNEL);
- } else {
- dma_fence_put(fence);
- }
- return 0;
- }
-
- ret = xa_alloc(fence_array, &id, fence, xa_limit_32b, GFP_KERNEL);
- if (ret != 0)
- dma_fence_put(fence);
-
- return ret;
-}
-EXPORT_SYMBOL(drm_gem_fence_array_add);
-
-/**
- * drm_gem_fence_array_add_implicit - Adds the implicit dependencies tracked
- * in the GEM object's reservation object to an array of dma_fences for use in
- * scheduling a rendering job.
- *
- * This should be called after drm_gem_lock_reservations() on your array of
- * GEM objects used in the job but before updating the reservations with your
- * own fences.
- *
- * @fence_array: array of dma_fence * for the job to block on.
- * @obj: the gem object to add new dependencies from.
- * @write: whether the job might write the object (so we need to depend on
- * shared fences in the reservation object).
- */
-int drm_gem_fence_array_add_implicit(struct xarray *fence_array,
- struct drm_gem_object *obj,
- bool write)
-{
- int ret;
- struct dma_fence **fences;
- unsigned int i, fence_count;
-
- if (!write) {
- struct dma_fence *fence =
- dma_resv_get_excl_unlocked(obj->resv);
-
- return drm_gem_fence_array_add(fence_array, fence);
- }
-
- ret = dma_resv_get_fences(obj->resv, NULL,
- &fence_count, &fences);
- if (ret || !fence_count)
- return ret;
-
- for (i = 0; i < fence_count; i++) {
- ret = drm_gem_fence_array_add(fence_array, fences[i]);
- if (ret)
- break;
- }
-
- for (; i < fence_count; i++)
- dma_fence_put(fences[i]);
- kfree(fences);
- return ret;
-}
-EXPORT_SYMBOL(drm_gem_fence_array_add_implicit);
diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
index 35e7f44c2a75..e55a767188af 100644
--- a/include/drm/drm_gem.h
+++ b/include/drm/drm_gem.h
@@ -407,11 +407,6 @@ int drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
struct ww_acquire_ctx *acquire_ctx);
void drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
struct ww_acquire_ctx *acquire_ctx);
-int drm_gem_fence_array_add(struct xarray *fence_array,
- struct dma_fence *fence);
-int drm_gem_fence_array_add_implicit(struct xarray *fence_array,
- struct drm_gem_object *obj,
- bool write);
int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
u32 handle, u64 *offset);
--
2.32.0
tldr; DMA buffers aren't normal memory, expecting that you can use
them like that (like calling get_user_pages works, or that they're
accounting like any other normal memory) cannot be guaranteed.
Since some userspace only runs on integrated devices, where all
buffers are actually all resident system memory, there's a huge
temptation to assume that a struct page is always present and useable
like for any more pagecache backed mmap. This has the potential to
result in a uapi nightmare.
To stop this gap require that DMA buffer mmaps are VM_PFNMAP, which
blocks get_user_pages and all the other struct page based
infrastructure for everyone. In spirit this is the uapi counterpart to
the kernel-internal CONFIG_DMABUF_DEBUG.
Motivated by a recent patch which wanted to swich the system dma-buf
heap to vm_insert_page instead of vm_insert_pfn.
v2:
Jason brought up that we also want to guarantee that all ptes have the
pte_special flag set, to catch fast get_user_pages (on architectures
that support this). Allowing VM_MIXEDMAP (like VM_SPECIAL does) would
still allow vm_insert_page, but limiting to VM_PFNMAP will catch that.
>From auditing the various functions to insert pfn pte entires
(vm_insert_pfn_prot, remap_pfn_range and all it's callers like
dma_mmap_wc) it looks like VM_PFNMAP is already required anyway, so
this should be the correct flag to check for.
v3: Change to WARN_ON_ONCE (Thomas Zimmermann)
References: https://lore.kernel.org/lkml/CAKMK7uHi+mG0z0HUmNt13QCCvutuRVjpcR0NjRL12k-Wb…
Acked-by: Christian König <christian.koenig(a)amd.com>
Acked-by: Thomas Zimmermann <tzimmermann(a)suse.de>
Cc: Thomas Zimmermann <tzimmermann(a)suse.de>
Cc: Jason Gunthorpe <jgg(a)ziepe.ca>
Cc: Suren Baghdasaryan <surenb(a)google.com>
Cc: Matthew Wilcox <willy(a)infradead.org>
Cc: John Stultz <john.stultz(a)linaro.org>
Signed-off-by: Daniel Vetter <daniel.vetter(a)intel.com>
Cc: Sumit Semwal <sumit.semwal(a)linaro.org>
Cc: "Christian König" <christian.koenig(a)amd.com>
Cc: linux-media(a)vger.kernel.org
Cc: linaro-mm-sig(a)lists.linaro.org
--
Resending this so I can test the next patches for vgem/shmem in
intel-gfx-ci.
No immediate plans to merge this patch here since ttm isn't addressed
yet (and there we have the hugepte issue, for which I don't think we
have a clear consensus yet).
-Daniel
---
drivers/dma-buf/dma-buf.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 63d32261b63f..d19b1cf6c34f 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -130,6 +130,7 @@ static struct file_system_type dma_buf_fs_type = {
static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
{
struct dma_buf *dmabuf;
+ int ret;
if (!is_dma_buf_file(file))
return -EINVAL;
@@ -145,7 +146,11 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
dmabuf->size >> PAGE_SHIFT)
return -EINVAL;
- return dmabuf->ops->mmap(dmabuf, vma);
+ ret = dmabuf->ops->mmap(dmabuf, vma);
+
+ WARN_ON_ONCE(!(vma->vm_flags & VM_PFNMAP));
+
+ return ret;
}
static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
@@ -1260,6 +1265,8 @@ EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
unsigned long pgoff)
{
+ int ret;
+
if (WARN_ON(!dmabuf || !vma))
return -EINVAL;
@@ -1280,7 +1287,11 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
vma_set_file(vma, dmabuf->file);
vma->vm_pgoff = pgoff;
- return dmabuf->ops->mmap(dmabuf, vma);
+ ret = dmabuf->ops->mmap(dmabuf, vma);
+
+ WARN_ON_ONCE(!(vma->vm_flags & VM_PFNMAP));
+
+ return ret;
}
EXPORT_SYMBOL_GPL(dma_buf_mmap);
--
2.32.0
On Wed, Aug 11, 2021 at 08:50:52PM +0300, Pavel Skripkin wrote:
> Syzbot reported general protection fault in udmabuf_create. The problem
> was in wrong error handling.
>
> In commit 16c243e99d33 ("udmabuf: Add support for mapping hugepages (v4)")
> shmem_read_mapping_page() call was replaced with find_get_page_flags(),
> but find_get_page_flags() returns NULL on failure instead PTR_ERR().
>
> Wrong error checking was causing GPF in get_page(), since passed page
> was equal to NULL. Fix it by changing if (IS_ER(!hpage)) to if (!hpage)
>
> Reported-by: syzbot+e9cd3122a37c5d6c51e8(a)syzkaller.appspotmail.com
> Fixes: 16c243e99d33 ("udmabuf: Add support for mapping hugepages (v4)")
> Signed-off-by: Pavel Skripkin <paskripkin(a)gmail.com>
Pushed to drm-misc-next.
thanks,
Gerd
This patch limits the size of total memory that can be requested in a
single allocation from the system heap. This would prevent a
buggy/malicious client from depleting system memory by requesting for an
extremely large allocation which might destabilize the system.
The limit is set to half the size of the device's total RAM which is the
same as what was set by the deprecated ION system heap.
Signed-off-by: Hridya Valsaraju <hridya(a)google.com>
---
drivers/dma-buf/heaps/system_heap.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index b7fbce66bcc0..099f5a8304b4 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -371,6 +371,12 @@ static struct dma_buf *system_heap_do_allocate(struct dma_heap *heap,
struct page *page, *tmp_page;
int i, ret = -ENOMEM;
+ if (len / PAGE_SIZE > totalram_pages() / 2) {
+ pr_err("pid %d requested too large an allocation(size %lu) from system heap\n",
+ current->pid, len);
+ return ERR_PTR(ret);
+ }
+
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer)
return ERR_PTR(-ENOMEM);
--
2.32.0.432.gabb21c7263-goog
On 8/9/21 5:22 AM, Gal Pressman wrote:
> Fix a few typos in the documentation:
> - Remove an extraneous 'or'
> - 'unpins' -> 'unpin'
> - 'braket' -> 'bracket'
> - 'mappinsg' -> 'mappings'
> - 'fullfills' -> 'fulfills'
>
> Signed-off-by: Gal Pressman <galpress(a)amazon.com>
Reviewed-by: Randy Dunlap <rdunlap(a)infradead.org>
Thanks.
> ---
> include/linux/dma-buf.h | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
> index efdc56b9d95f..772403352767 100644
> --- a/include/linux/dma-buf.h
> +++ b/include/linux/dma-buf.h
> @@ -54,7 +54,7 @@ struct dma_buf_ops {
> * device), and otherwise need to fail the attach operation.
> *
> * The exporter should also in general check whether the current
> - * allocation fullfills the DMA constraints of the new device. If this
> + * allocation fulfills the DMA constraints of the new device. If this
> * is not the case, and the allocation cannot be moved, it should also
> * fail the attach operation.
> *
> @@ -146,7 +146,7 @@ struct dma_buf_ops {
> *
> * Returns:
> *
> - * A &sg_table scatter list of or the backing storage of the DMA buffer,
> + * A &sg_table scatter list of the backing storage of the DMA buffer,
> * already mapped into the device address space of the &device attached
> * with the provided &dma_buf_attachment. The addresses and lengths in
> * the scatter list are PAGE_SIZE aligned.
> @@ -168,7 +168,7 @@ struct dma_buf_ops {
> *
> * This is called by dma_buf_unmap_attachment() and should unmap and
> * release the &sg_table allocated in @map_dma_buf, and it is mandatory.
> - * For static dma_buf handling this might also unpins the backing
> + * For static dma_buf handling this might also unpin the backing
> * storage if this is the last mapping of the DMA buffer.
> */
> void (*unmap_dma_buf)(struct dma_buf_attachment *,
> @@ -237,7 +237,7 @@ struct dma_buf_ops {
> * This callback is used by the dma_buf_mmap() function
> *
> * Note that the mapping needs to be incoherent, userspace is expected
> - * to braket CPU access using the DMA_BUF_IOCTL_SYNC interface.
> + * to bracket CPU access using the DMA_BUF_IOCTL_SYNC interface.
> *
> * Because dma-buf buffers have invariant size over their lifetime, the
> * dma-buf core checks whether a vma is too large and rejects such
> @@ -464,7 +464,7 @@ static inline bool dma_buf_is_dynamic(struct dma_buf *dmabuf)
>
> /**
> * dma_buf_attachment_is_dynamic - check if a DMA-buf attachment uses dynamic
> - * mappinsg
> + * mappings
> * @attach: the DMA-buf attachment to check
> *
> * Returns true if a DMA-buf importer wants to call the map/unmap functions with
>
--
~Randy
On Mon, Aug 02, 2021 at 06:59:57PM +0800, Desmond Cheong Zhi Xi wrote:
> In drm_is_current_master_locked, accessing drm_file.master should be
> protected by either drm_file.master_lookup_lock or
> drm_device.master_mutex. This was previously awkward to assert with
> lockdep.
>
> Following patch ("locking/lockdep: Provide lockdep_assert{,_once}()
> helpers"), this assertion is now convenient. So we add in the
> assertion and explain this lock design in the kerneldoc.
>
> Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx(a)gmail.com>
> Acked-by: Boqun Feng <boqun.feng(a)gmail.com>
> Acked-by: Waiman Long <longman(a)redhat.com>
> Acked-by: Peter Zijlstra (Intel) <peterz(a)infradead.org>
Both patches pushed to drm-misc-next, thanks.
-Daniel
> ---
> drivers/gpu/drm/drm_auth.c | 6 +++---
> include/drm/drm_file.h | 4 ++++
> 2 files changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
> index 9c24b8cc8e36..6f4d7ff23c80 100644
> --- a/drivers/gpu/drm/drm_auth.c
> +++ b/drivers/gpu/drm/drm_auth.c
> @@ -63,9 +63,9 @@
>
> static bool drm_is_current_master_locked(struct drm_file *fpriv)
> {
> - /* Either drm_device.master_mutex or drm_file.master_lookup_lock
> - * should be held here.
> - */
> + lockdep_assert_once(lockdep_is_held(&fpriv->master_lookup_lock) ||
> + lockdep_is_held(&fpriv->minor->dev->master_mutex));
> +
> return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
> }
>
> diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
> index 726cfe0ff5f5..a3acb7ac3550 100644
> --- a/include/drm/drm_file.h
> +++ b/include/drm/drm_file.h
> @@ -233,6 +233,10 @@ struct drm_file {
> * this only matches &drm_device.master if the master is the currently
> * active one.
> *
> + * To update @master, both &drm_device.master_mutex and
> + * @master_lookup_lock need to be held, therefore holding either of
> + * them is safe and enough for the read side.
> + *
> * When dereferencing this pointer, either hold struct
> * &drm_device.master_mutex for the duration of the pointer's use, or
> * use drm_file_get_master() if struct &drm_device.master_mutex is not
> --
> 2.25.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Am 31.07.21 um 10:13 schrieb Tuo Li:
> The variable ttm is assigned to the variable gtt, and the variable gtt
> is checked in:
> if (gtt && gtt->userptr)
>
> This indicates that both ttm and gtt can be NULL.
> If so, a null-pointer dereference will occur:
> if (ttm->page_flags & TTM_PAGE_FLAG_SG)
>
> Also, some null-pointer dereferences will occur in the function
> ttm_pool_free() which is called in:
> return ttm_pool_free(&adev->mman.bdev.pool, ttm);
>
> To fix these possible null-pointer dereferences, the function returns
> when ttm is NULL.
NAK, same as with the other patch.
The ttm object is mandatory, asking the driver to destroy a ttm object
which doesn't exists makes no sense at all and is a bug in the upper layer.
The NULL check is just a leftover from when the gtt and ttm objects
where distinct. Please remove that one instead.
BTW: Bonus points for changing the (void *) cast into a much cleaner
container_of().
Thanks,
Christian.
>
> Reported-by: TOTE Robot <oslab(a)tsinghua.edu.cn>
> Signed-off-by: Tuo Li <islituo(a)gmail.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> index 3a55f08e00e1..0216ca085f11 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> @@ -1146,7 +1146,10 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_device *bdev,
> struct amdgpu_ttm_tt *gtt = (void *)ttm;
> struct amdgpu_device *adev;
>
> - if (gtt && gtt->userptr) {
> + if (ttm == NULL)
> + return;
> +
> + if (gtt->userptr) {
> amdgpu_ttm_tt_set_user_pages(ttm, NULL);
> kfree(ttm->sg);
> ttm->sg = NULL;
Am 31.07.21 um 10:04 schrieb Tuo Li:
> The variable ttm is assigned to the variable gtt, and the variable gtt
> is checked in:
> if (gtt && gtt->userptr)
>
> This indicates that both ttm and gtt can be NULL.
> If so, a null-pointer dereference will occur:
> if (ttm->page_flags & TTM_PAGE_FLAG_SG)
>
> Also, some null-pointer dereferences will occur in the function
> ttm_pool_alloc() which is called in:
> return ttm_pool_alloc(&adev->mman.bdev.pool, ttm, ctx);
>
> To fix these possible null-pointer dereferences, the function returns
> -EINVAL when ttm is NULL.
NAK, the NULL test is just a leftover from when the objects where distinct.
Please remove the NULL test instead.
Regards,
Christian.
>
> Reported-by: TOTE Robot <oslab(a)tsinghua.edu.cn>
> Signed-off-by: Tuo Li <islituo(a)gmail.com>
> ---
> drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> index 3a55f08e00e1..80440f799c09 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
> @@ -1120,8 +1120,11 @@ static int amdgpu_ttm_tt_populate(struct ttm_device *bdev,
> struct amdgpu_device *adev = amdgpu_ttm_adev(bdev);
> struct amdgpu_ttm_tt *gtt = (void *)ttm;
>
> + if (ttm == NULL)
> + return -EINVAL;
> +
> /* user pages are bound by amdgpu_ttm_tt_pin_userptr() */
> - if (gtt && gtt->userptr) {
> + if (gtt->userptr) {
> ttm->sg = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
> if (!ttm->sg)
> return -ENOMEM;
Entirely unused.
Signed-off-by: Christian König <christian.koenig(a)amd.com>
---
drivers/dma-buf/Makefile | 2 +-
drivers/dma-buf/seqno-fence.c | 71 ----------------------
include/linux/seqno-fence.h | 109 ----------------------------------
3 files changed, 1 insertion(+), 181 deletions(-)
delete mode 100644 drivers/dma-buf/seqno-fence.c
delete mode 100644 include/linux/seqno-fence.h
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 40d81f23cacf..1ef021273a06 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
- dma-resv.o seqno-fence.o
+ dma-resv.o
obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
obj-$(CONFIG_DMABUF_HEAPS) += heaps/
obj-$(CONFIG_SYNC_FILE) += sync_file.o
diff --git a/drivers/dma-buf/seqno-fence.c b/drivers/dma-buf/seqno-fence.c
deleted file mode 100644
index bfe14e94c488..000000000000
--- a/drivers/dma-buf/seqno-fence.c
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * seqno-fence, using a dma-buf to synchronize fencing
- *
- * Copyright (C) 2012 Texas Instruments
- * Copyright (C) 2012-2014 Canonical Ltd
- * Authors:
- * Rob Clark <robdclark(a)gmail.com>
- * Maarten Lankhorst <maarten.lankhorst(a)canonical.com>
- */
-
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/seqno-fence.h>
-
-static const char *seqno_fence_get_driver_name(struct dma_fence *fence)
-{
- struct seqno_fence *seqno_fence = to_seqno_fence(fence);
-
- return seqno_fence->ops->get_driver_name(fence);
-}
-
-static const char *seqno_fence_get_timeline_name(struct dma_fence *fence)
-{
- struct seqno_fence *seqno_fence = to_seqno_fence(fence);
-
- return seqno_fence->ops->get_timeline_name(fence);
-}
-
-static bool seqno_enable_signaling(struct dma_fence *fence)
-{
- struct seqno_fence *seqno_fence = to_seqno_fence(fence);
-
- return seqno_fence->ops->enable_signaling(fence);
-}
-
-static bool seqno_signaled(struct dma_fence *fence)
-{
- struct seqno_fence *seqno_fence = to_seqno_fence(fence);
-
- return seqno_fence->ops->signaled && seqno_fence->ops->signaled(fence);
-}
-
-static void seqno_release(struct dma_fence *fence)
-{
- struct seqno_fence *f = to_seqno_fence(fence);
-
- dma_buf_put(f->sync_buf);
- if (f->ops->release)
- f->ops->release(fence);
- else
- dma_fence_free(&f->base);
-}
-
-static signed long seqno_wait(struct dma_fence *fence, bool intr,
- signed long timeout)
-{
- struct seqno_fence *f = to_seqno_fence(fence);
-
- return f->ops->wait(fence, intr, timeout);
-}
-
-const struct dma_fence_ops seqno_fence_ops = {
- .get_driver_name = seqno_fence_get_driver_name,
- .get_timeline_name = seqno_fence_get_timeline_name,
- .enable_signaling = seqno_enable_signaling,
- .signaled = seqno_signaled,
- .wait = seqno_wait,
- .release = seqno_release,
-};
-EXPORT_SYMBOL(seqno_fence_ops);
diff --git a/include/linux/seqno-fence.h b/include/linux/seqno-fence.h
deleted file mode 100644
index 3cca2b8fac43..000000000000
--- a/include/linux/seqno-fence.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * seqno-fence, using a dma-buf to synchronize fencing
- *
- * Copyright (C) 2012 Texas Instruments
- * Copyright (C) 2012 Canonical Ltd
- * Authors:
- * Rob Clark <robdclark(a)gmail.com>
- * Maarten Lankhorst <maarten.lankhorst(a)canonical.com>
- */
-
-#ifndef __LINUX_SEQNO_FENCE_H
-#define __LINUX_SEQNO_FENCE_H
-
-#include <linux/dma-fence.h>
-#include <linux/dma-buf.h>
-
-enum seqno_fence_condition {
- SEQNO_FENCE_WAIT_GEQUAL,
- SEQNO_FENCE_WAIT_NONZERO
-};
-
-struct seqno_fence {
- struct dma_fence base;
-
- const struct dma_fence_ops *ops;
- struct dma_buf *sync_buf;
- uint32_t seqno_ofs;
- enum seqno_fence_condition condition;
-};
-
-extern const struct dma_fence_ops seqno_fence_ops;
-
-/**
- * to_seqno_fence - cast a fence to a seqno_fence
- * @fence: fence to cast to a seqno_fence
- *
- * Returns NULL if the fence is not a seqno_fence,
- * or the seqno_fence otherwise.
- */
-static inline struct seqno_fence *
-to_seqno_fence(struct dma_fence *fence)
-{
- if (fence->ops != &seqno_fence_ops)
- return NULL;
- return container_of(fence, struct seqno_fence, base);
-}
-
-/**
- * seqno_fence_init - initialize a seqno fence
- * @fence: seqno_fence to initialize
- * @lock: pointer to spinlock to use for fence
- * @sync_buf: buffer containing the memory location to signal on
- * @context: the execution context this fence is a part of
- * @seqno_ofs: the offset within @sync_buf
- * @seqno: the sequence # to signal on
- * @cond: fence wait condition
- * @ops: the fence_ops for operations on this seqno fence
- *
- * This function initializes a struct seqno_fence with passed parameters,
- * and takes a reference on sync_buf which is released on fence destruction.
- *
- * A seqno_fence is a dma_fence which can complete in software when
- * enable_signaling is called, but it also completes when
- * (s32)((sync_buf)[seqno_ofs] - seqno) >= 0 is true
- *
- * The seqno_fence will take a refcount on the sync_buf until it's
- * destroyed, but actual lifetime of sync_buf may be longer if one of the
- * callers take a reference to it.
- *
- * Certain hardware have instructions to insert this type of wait condition
- * in the command stream, so no intervention from software would be needed.
- * This type of fence can be destroyed before completed, however a reference
- * on the sync_buf dma-buf can be taken. It is encouraged to re-use the same
- * dma-buf for sync_buf, since mapping or unmapping the sync_buf to the
- * device's vm can be expensive.
- *
- * It is recommended for creators of seqno_fence to call dma_fence_signal()
- * before destruction. This will prevent possible issues from wraparound at
- * time of issue vs time of check, since users can check dma_fence_is_signaled()
- * before submitting instructions for the hardware to wait on the fence.
- * However, when ops.enable_signaling is not called, it doesn't have to be
- * done as soon as possible, just before there's any real danger of seqno
- * wraparound.
- */
-static inline void
-seqno_fence_init(struct seqno_fence *fence, spinlock_t *lock,
- struct dma_buf *sync_buf, uint32_t context,
- uint32_t seqno_ofs, uint32_t seqno,
- enum seqno_fence_condition cond,
- const struct dma_fence_ops *ops)
-{
- BUG_ON(!fence || !sync_buf || !ops);
- BUG_ON(!ops->wait || !ops->enable_signaling ||
- !ops->get_driver_name || !ops->get_timeline_name);
-
- /*
- * ops is used in dma_fence_init for get_driver_name, so needs to be
- * initialized first
- */
- fence->ops = ops;
- dma_fence_init(&fence->base, &seqno_fence_ops, lock, context, seqno);
- get_dma_buf(sync_buf);
- fence->sync_buf = sync_buf;
- fence->seqno_ofs = seqno_ofs;
- fence->condition = cond;
-}
-
-#endif /* __LINUX_SEQNO_FENCE_H */
--
2.25.1