[PATCH 6/6] drm/tinydrm: Support device unplug
Noralf Trønnes
noralf at tronnes.org
Sat Sep 2 20:59:50 UTC 2017
Den 01.09.2017 10.38, skrev Laurent Pinchart:
> Hi Noralf,
>
> On Thursday, 31 August 2017 22:22:03 EEST Noralf Trønnes wrote:
>> Den 31.08.2017 14.59, skrev Laurent Pinchart:
>>> On Wednesday, 30 August 2017 20:18:49 EEST Daniel Vetter wrote:
>>>> On Wed, Aug 30, 2017 at 6:31 PM, Noralf Trønnes wrote:
>>>>> Den 28.08.2017 23.56, skrev Daniel Vetter:
>>>>>> On Mon, Aug 28, 2017 at 07:17:48PM +0200, Noralf Trønnes wrote:
>>>>>>> Support device unplugging to make tinydrm suitable for USB devices.
>>>>>>>
>>>>>>> Cc: David Lechner <david at lechnology.com>
>>>>>>> Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
>>>>>>> ---
>>>>>>>
>>>>>>> drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 69 ++++++++++++++++++---
>>>>>>> drivers/gpu/drm/tinydrm/mi0283qt.c | 4 ++
>>>>>>> drivers/gpu/drm/tinydrm/mipi-dbi.c | 5 ++-
>>>>>>> drivers/gpu/drm/tinydrm/repaper.c | 9 +++-
>>>>>>> drivers/gpu/drm/tinydrm/st7586.c | 9 +++-
>>>>>>> include/drm/tinydrm/tinydrm.h | 5 +++
>>>>>>> 6 files changed, 87 insertions(+), 14 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
[snip]
>>>>>>> +
>>>>>>> + drm_fbdev_cma_dev_unplug(tdev->fbdev_cma);
>>>>>>> + drm_dev_unplug(&tdev->drm);
>>>>>>> +
>>>>>>> + /* Make sure framebuffer flushing is done */
>>>>>>> + mutex_lock(&tdev->dirty_lock);
>>>>>>> + mutex_unlock(&tdev->dirty_lock);
>>>>>> Is this really needed? Or, doesn't it just paper over a driver bug you
>>>>>> have already anyway, since native kms userspace can directly call
>>>>>> fb->funcs->dirty too, and you already protect against that.
>>>>>>
>>>>>> This definitely looks like the fbdev helper is leaking implementation
>>>>>> details to callers where it shouldn't do that.
>>>>> Flushing can happen while drm_dev_unplug() is called, and when we leave
>>>>> this function the device facing resources controlled by devres will be
>>>>> removed. Thus I have to make sure any such flushing is done before
>>>>> leaving so the next flush is stopped by the drm_dev_is_unplugged()
>>>>> check. I don't see any other way of ensuring that.
>>>>>
>>>>> I see now that I should move the call to drm_atomic_helper_shutdown()
>>>>> after drm_dev_unplug() to properly protect the pipe .enable/.disable
>>>>> callbacks.
>>>> Hm, calling _shutdown when the hw is gone already won't end well.
>>>> Fundamentally this race exists for all use-cases, and I'm somewhat
>>>> leaning towards plugging it in the core.
>>>>
>>>> The general solution probably involves something that smells a lot
>>>> like srcu, i.e. at every possible entry point into a drm driver
>>>> (ioctl, fbdev, dma-buf sharing, everything really) we take that
>>>> super-cheap read-side look, and drop it when we leave.
>>> That's similar to what we plan to do in V4L2. The idea is to set a device
>>> removed flag at the beginning of the .remove() handler and wait for all
>>> pending operations to complete. The core will reject any new operation
>>> when the flag is set. To wait for completion, every entry point would
>>> increase a use count, and decrease it on exit. When the use count is
>>> decreased to 0 waiters will be woken up. This should solve the unplug/user
>>> race.
>> Ah, such a simple solution, easy to understand and difficult to get wrong!
>> And it's even nestable, no danger of deadlocking.
>>
>> Maybe I can use it with tinydrm:
> It would be better to implement that in the DRM core to reject all ioctls
> early, as some of them could call drm_driver operations that can't return an
> error (such as .disable_vblank() for instance).
I'll look at that.
drm_ioctl() already rejects calls when unplugged:
if (drm_device_is_unplugged(dev))
return -ENODEV;
>> * @dev_use: Tracks use of functions acessing the parent device.
>> * If it is zero, the device is gone. See ...
>> struct tinydrm_device {
>> atomic_t dev_use;
>> };
>>
>> /**
>> * tinydrm_dev_enter - Enter device accessing function
>> * @tdev: tinydrm device
>> *
>> * This function protects against using a device and it's resources after
>> * it's removed. Should be called at the beginning of the function.
>> *
>> * Returns:
>> * False if the device is still present, true if it is gone.
>> */
>> static inline bool tinydrm_dev_enter(struct tinydrm_device *tdev)
>> {
>> return !atomic_inc_not_zero(&tdev->dev_use);
>> }
>>
>> static inline void tinydrm_dev_exit(struct tinydrm_device *tdev)
>> {
>> atomic_dec(&tdev->dev_use);
>> }
>>
>> static inline bool tinydrm_is_unplugged(struct tinydrm_device *tdev)
>> {
>> bool ret = !atomic_read(&tdev->dev_use);
>> smp_rmb();
>> return ret;
>> }
>>
>>
>> static int tinydrm_init(...)
>> {
>> /* initialize */
>>
>> /* Set device is present */
>> atomic_set(&tdev->dev_use, 1);
>> }
>>
>> static void tinydrm_unregister(...)
>> {
>> /* Set device gone */
>> atomic_dec(&tdev->dev_use);
>>
>> /* Wait for all device facing functions to finish */
>> while (!tinydrm_is_unplugged(tdev)) {
>> cond_resched();
>> }
>>
>> /* proceed with unregistering */
>> }
>>
>> static int mipi_dbi_fb_dirty(...)
>> {
>> if (tinydrm_dev_enter(tdev))
>> return -ENODEV;
> Nitpicking, isn't the right error code -ENXIO ?
I'm using the pattern as shown in the drm_ioctl example above.
>> /* flush framebuffer */
>>
>> tinydrm_dev_exit(tdev);
>> }
[snip]
More information about the dri-devel
mailing list