[PATCH 6/6] drm/tinydrm: Support device unplug
Daniel Vetter
daniel at ffwll.ch
Mon Sep 4 07:26:15 UTC 2017
On Thu, Aug 31, 2017 at 09:22:03PM +0200, Noralf Trønnes wrote:
>
> Den 31.08.2017 14.59, skrev Laurent Pinchart:
> > Hi Daniel and Noralf,
> >
> > On Wednesday, 30 August 2017 20:18:49 EEST Daniel Vetter wrote:
> > > On Wed, Aug 30, 2017 at 6:31 PM, Noralf Trønnes <noralf at tronnes.org> wrote:
> > > > Den 28.08.2017 23.56, skrev Daniel Vetter:
> > > > > On Mon, Aug 28, 2017 at 07:17:48PM +0200, Noralf Trønnes wrote:
> > > > > > +
> > > > > > + 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:
>
> * @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;
>
> /* flush framebuffer */
>
> tinydrm_dev_exit(tdev);
> }
Yup, expect imo this should be done in drm core (as much as possible, i.e.
for ioctl at least, standard sysfs), plus then enter/exit exported to
drivers for their stuff.
And it really needs to be srcu. For kms drivers the atomic_inc/dec wont
matter, for render drivers it will show up (some of our ioctls are 100%
lock less in the fastpath, not a single atomic op in there).
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the dri-devel
mailing list