[Intel-gfx] [PATCH v2 00/12] drm: Add generic fbdev emulation

Daniel Vetter daniel at ffwll.ch
Tue Jun 26 13:42:45 UTC 2018


On Tue, Jun 26, 2018 at 3:41 PM, Noralf Trønnes <noralf at tronnes.org> wrote:
>
> Den 21.06.2018 19.19, skrev Noralf Trønnes:
>>
>>
>> Den 21.06.2018 09.14, skrev Daniel Vetter:
>>>
>>> On Wed, Jun 20, 2018 at 05:28:10PM +0200, Noralf Trønnes wrote:
>>>>
>>>> Den 20.06.2018 15.52, skrev Noralf Trønnes:
>>>>>
>>>>> Den 20.06.2018 11.34, skrev Daniel Vetter:
>>>>>>
>>>>>> On Mon, Jun 18, 2018 at 04:17:27PM +0200, Noralf Trønnes wrote:
>>>>>>>
>>>>>>> This patchset adds generic fbdev emulation for drivers that
>>>>>>> supports GEM
>>>>>>> based dumb buffers which support .gem_prime_vmap and gem_prime_mmap.
>>>>>>> An
>>>>>>> API is begun to support in-kernel clients in general.
>>>>>>>
>>>>>>> Notable changes since version 1:
>>>>>>>
>>>>>>> - Rework client unregister code. I've used reference counting to
>>>>>>> manage
>>>>>>>     the fact that both the client itself and the driver through
>>>>>>>     drm_dev_unregister() can release the client. The client is
>>>>>>> now released
>>>>>>>     using drm_client_put() instead of drm_client_free().
>>>>>>
>>>>>> I started reviewing the reworked client register/unregister stuff,
>>>>>> and it
>>>>>> looks good, except this kref stuff here for clients. I don't
>>>>>> understand
>>>>>> why you need this - as long as removal from dev->clientlist is
>>>>>> properly
>>>>>> protected by the mutex, I don't see how both the client and the
>>>>>> device can
>>>>>> release the client at the same time. Of course this means that both
>>>>>> the
>>>>>> device-trigger unregister and the client-triggered unregister must
>>>>>> first
>>>>>> grab the mutex, remove the client from the list, and only if that
>>>>>> was done
>>>>>> succesfully, clean up the client. If the client is already removed
>>>>>> from
>>>>>> the list (i.e. list_empty() is true) then you need to bail out to
>>>>>> avoid
>>>>>> double-freeing.
>>>>>
>>>>> I just suck at this :/
>>>>>
>>>>> Use case:
>>>>> Unloading client module at the same time as the device is unplugged.
>>>
>>> Do we really want to be able to unload client modules? Atm you can't
>>> unload the drm fbdev emulation either while a driver is still using it.
>>> Dropping that requirement would make things even simpler (you'd just need
>>> to add an owner field to drm_client and a try_module_get when registering
>>> it, bailing out if that fails).
>>>
>>> What's the use-case you have in mind that requires that you can unload a
>>> client driver module? Does that even work with the shuffling we've done
>>> on
>>> the register side of things?
>>
>>
>> When I first started on this client API, the client could unload itself
>> and I had a sysfs file that would remove clients for a particular
>> drm_device. This would mean that unloading a driver would require clients
>> to be removed first by writing to the sysfs file.
>> Then I started to look at the possibility that the driver could remove
>> clients automatically on driver unload. I have wrecked my brain trying to
>> make it race free, but it gets very complicated as you have shown in your
>> example. So I think we'll just avoid this complexity altogether.
>>
>> So, now the question is, who gets to remove clients. The client or the
>> driver?
>> The common pattern is that clients can come and go on their own choosing.
>> It's what I would expect, that the client can be unloaded.
>> The reason I looked into auto unloading when the driver where going away,
>> was so developers shouldn't have to do the extra step to unload the
>> driver.
>>
>> Now I see a third case, and that's plug/unplug USB devices. If the driver
>> can't remove the client, we can end up with lots hanging drm_device and
>> drm_client_dev instances that have to be removed manually, which is
>> really not an option.
>>
>> So I think we remove clients on driver/device removal. This means that to
>> unload a client, the driver(s) would have to be unloaded first.
>>
>> I'll add an owner field to drm_client_funcs as you suggested and work out
>> the details.
>
>
> Currently drm_client_dev->funcs is optional, but should it be mandatory
> now that drm_client_funcs gets an owner field?

I think because of the unregister callback it makes little sense to
have clients without a function table. But you can also make the
try_module_get conditional on drm_client->funcs, if that makes more
sense to you. No opinion on my side really.
-Daniel

>
> Noralf.
>
>
>>
>> Thanks for helping me get this as simple and straightforward as possible.
>>
>> Noralf.
>>
>>>>> The client module calls drm_client_release():
>>>>>
>>>>> void drm_client_release(struct drm_client_dev *client)
>>>>> {
>>>>>      struct drm_device *dev = client->dev;
>>>
>>> Not sure (without reading your patches again) why we have
>>> drm_client_close
>>> here and ->unregister/drm_client_release below. But the trick is roughly
>>> to do
>>>     client = NULL;
>>>
>>>     mutex_lock();
>>>     client = find_it();
>>>     if (client)
>>>         list_del();
>>>     mute_unlock();
>>>
>>>     if (!client)
>>>         continue; /* or early return, whatever makes sense */
>>>
>>>     drm_client_unregister(client);
>>>
>>> This way if you race there's:
>>> - only one thread will win, since the removal from the list is locked
>>> - no deadlocks, because the actual cleanup is done outside of the locks
>>>
>>> The problem is applying this trick to each situation, since you need to
>>> make sure that you get them all. Since you want to be able to unregister
>>> from 2 different lists, with each their own locks, you need to nest the
>>> above pattern in clever ways. In the client unregister function:
>>>
>>>     mutex_lock(fbdev_clients_lock);
>>>     client = list_first(fbdev_clients_list);
>>>     if (client)
>>>         list_del();
>>>
>>>     mutex_lock(client->dev);
>>>     if (!list_empty(client->dev_list))
>>>         list_del();
>>>     else
>>>         /* someone else raced us, give up */
>>>         client = NULL;
>>>     mutex_unlock(client->dev);
>>>     mutex_unlock(fbdev_clients_lock);
>>>
>>>     if (!client)
>>>         continue; /* or early return, whatever makes sense */
>>>
>>>     drm_client_unregister(client);
>>>
>>> This way you know that as long as you hold the fbdevs_clients_lock client
>>> can't disappear, so you can look at client->dev (which also won't
>>> disappear, because the client can't disappear), which allows you to take
>>> the per-device client look to check whether you've race with removing.
>>>
>>> On the per-device client remove function we can't just do the same trick,
>>> because that would be a locking inversion. Instead we need careful
>>> ordering:
>>>
>>>
>>>     mutex_lock(client->dev);
>>>     if (!list_empty(client->dev_list))
>>>         list_del();
>>>     else
>>>         /* someone else raced us, give up */
>>>         client = NULL;
>>>     mutex_unlock(client->dev);
>>>
>>>     if (!client)
>>>         continue; /* or early return, whatever makes sense */
>>>
>>>     /* we've won the race and must do the cleanup, but first we need
>>>      * to stop use-after-free */
>>>
>>>     mutex_lock(fbdev_clients_lock);
>>>     if (!list_empty(client->fbdev_list))
>>>         list_del();
>>>     else
>>>         /* we raced and the other thread did the list removal
>>>          * already, but will have backed off by now */
>>>     mutex_unlock(fbdev_clients_lock);
>>>
>>>     /* no one can get at the client structure anymore, it's safe to
>>>      * clean it up */
>>>
>>>     drm_client_unregister(client);
>>>
>>> Lots of complexity for a feature we didn't have yet and that I don't
>>> think
>>> we need really, but it is doable :-)
>>>
>>>>> mutex_lock(&dev->clientlist_mutex);
>>>>>      list_del(&client->list);
>>>>>      drm_client_close(client);
>>>>>      mutex_unlock(&dev->clientlist_mutex);
>>>>>      drm_dev_put(dev);
>>>>> }
>>>>>
>>>>>
>>>>> drm_device_unregister() calls drm_client_dev_unregister():
>>>>>
>>>>> void drm_client_dev_unregister(struct drm_device *dev)
>>>>> {
>>>>>      struct drm_client_dev *client;
>>>>>
>>>>>      mutex_lock(&dev->clientlist_mutex);
>>>>>      list_for_each_entry(client, &dev->clientlist, list) {
>>>>>          if (client->funcs && client->funcs->unregister)
>>>>>              client->funcs->unregister(client);
>>>>>          else
>>>>>              drm_client_release(client);
>>>>>      }
>>>>>      mutex_unlock(&dev->clientlist_mutex);
>>>>> }
>>>>>
>>>>>
>>>>> How do I do this without deadlocking and without operating on a
>>>>> drm_client_dev structure that has been freed in the other codepath?
>>>>>
>>>> There's one more quirk that I forgot:
>>>> If fbdev can't release the client on .unregister due to open fd's, the
>>>> list entry should be removed but releasing resources is deferred to
>>>> the last fd being closed.
>>>
>>> For fbdev I think kref'ing it makes sense. But probably better to do that
>>> in the structure that contains the drm_client, since I think this is very
>>> much an fbdev problem, not a general drm_client problem.
>>>
>>> Cheers, Daniel
>>>
>>>> Noralf.
>>>>
>>>>> Noralf.
>>>>>
>>>>>> I don't think there's a need to use a kref here. And kref has the
>>>>>> tricky
>>>>>> issue that you tempt everyone into constructing references loops
>>>>>> between
>>>>>> drm_device and drm_client (which require lots of jumping through
>>>>>> hoops in
>>>>>> your v1 to make sure you can break those reference loops).
>>>>>>
>>>>>>> - fbdev: Use a shadow buffer for framebuffers that have a dirty
>>>>>>>     callback. This makes the fbdev client truly generic and
>>>>>>> useable for all
>>>>>>>     drivers. There's a blitting penalty, but this is generic
>>>>>>> emulation after
>>>>>>>     all. The reason for needing a shadow buffer is that deferred
>>>>>>> I/O only
>>>>>>>     works with kmalloc/vmalloc buffers and not with shmem buffers
>>>>>>>     (page->lru/mapping).
>>>>>>
>>>>>> Yeah I think that's the only feasible option. Everyone who cares more
>>>>>> about fbdev performance can keep their driver-specific code. And for
>>>>>> other
>>>>>> drm_client users this shouldn't be a problem, since they know how to
>>>>>> use
>>>>>> dirty and flipping between multiple buffers to drive kms as it was
>>>>>> designed. The issue really only exists for fbdev's assumption of a
>>>>>> direct
>>>>>> mmap of a dumb framebuffer, encoded into the uapi.
>>>>>>
>>>>>>> - Let tinydrm use the full fbdev client
>>>>>>
>>>>>> \o/
>>>>>>
>>>>>> Cheers, Daniel
>>>>>>>
>>>>>>> Noralf.
>>>>>>>
>>>>>>> Changes since version 1:
>>>>>>> - Make it possible to embed struct drm_client_dev and drop the
>>>>>>> private
>>>>>>>     pointer
>>>>>>> - Use kref reference counting to control client release since both
>>>>>>> the
>>>>>>>     client and the driver can release.
>>>>>>> - Add comment about using dma-buf as a possibility with some rework
>>>>>>> - Move buffer NULL check to drm_client_framebuffer_delete()
>>>>>>> - Move client name to struct drm_client_dev
>>>>>>> - Move up drm_dev_get/put calls to make them more visible
>>>>>>> - Move drm_client_dev.list definition to later patch that makes
>>>>>>> use of it
>>>>>>>
>>>>>>> - Embed drm_client at the beginning of drm_fb_helper to avoid a
>>>>>>> fragile
>>>>>>>     transitional kfree hack in drm_client_release()
>>>>>>> - Set owner in drm_fbdev_fb_ops
>>>>>>> - Add kerneldoc to drm_fb_helper_generic_probe()
>>>>>>>
>>>>>>> - Remove unused functions
>>>>>>> - Change name drm_client_funcs.lastclose -> .restore
>>>>>>> - Change name drm_client_funcs.remove -> .unregister
>>>>>>> - Rework unregister code
>>>>>>>
>>>>>>> - tinydrm: Use drm_fbdev_generic_setup() and remove
>>>>>>>     drm_fb_cma_fbdev_init_with_funcs()
>>>>>>>
>>>>>>> David Herrmann (1):
>>>>>>>     drm: provide management functions for drm_file
>>>>>>>
>>>>>>> Noralf Trønnes (11):
>>>>>>>     drm/file: Don't set master on in-kernel clients
>>>>>>>     drm: Make ioctls available for in-kernel clients
>>>>>>>     drm: Begin an API for in-kernel clients
>>>>>>>     drm/fb-helper: Add generic fbdev emulation .fb_probe function
>>>>>>>     drm/pl111: Set .gem_prime_vmap and .gem_prime_mmap
>>>>>>>     drm/cma-helper: Use the generic fbdev emulation
>>>>>>>     drm/client: Add client callbacks
>>>>>>>     drm/debugfs: Add internal client debugfs file
>>>>>>>     drm/fb-helper: Finish the generic fbdev emulation
>>>>>>>     drm/tinydrm: Use drm_fbdev_generic_setup()
>>>>>>>     drm/cma-helper: Remove drm_fb_cma_fbdev_init_with_funcs()
>>>>>>>
>>>>>>>    Documentation/gpu/drm-client.rst            |  12 +
>>>>>>>    Documentation/gpu/index.rst                 |   1 +
>>>>>>>    drivers/gpu/drm/Makefile                    |   2 +-
>>>>>>>    drivers/gpu/drm/drm_client.c                | 435
>>>>>>> ++++++++++++++++++++++++++++
>>>>>>>    drivers/gpu/drm/drm_crtc_internal.h         |  19 +-
>>>>>>>    drivers/gpu/drm/drm_debugfs.c               |   7 +
>>>>>>>    drivers/gpu/drm/drm_drv.c                   |   8 +
>>>>>>>    drivers/gpu/drm/drm_dumb_buffers.c          |  33 ++-
>>>>>>>    drivers/gpu/drm/drm_fb_cma_helper.c         | 380
>>>>>>> +++---------------------
>>>>>>>    drivers/gpu/drm/drm_fb_helper.c             | 330
>>>>>>> ++++++++++++++++++++-
>>>>>>>    drivers/gpu/drm/drm_file.c                  | 304
>>>>>>> ++++++++++---------
>>>>>>>    drivers/gpu/drm/drm_framebuffer.c           |  42 ++-
>>>>>>>    drivers/gpu/drm/drm_internal.h              |   2 +
>>>>>>>    drivers/gpu/drm/drm_ioctl.c                 |   4 +-
>>>>>>>    drivers/gpu/drm/drm_probe_helper.c          |   3 +
>>>>>>>    drivers/gpu/drm/pl111/pl111_drv.c           |   2 +
>>>>>>>    drivers/gpu/drm/tinydrm/core/tinydrm-core.c |   3 +-
>>>>>>>    drivers/gpu/drm/tinydrm/ili9225.c           |   1 -
>>>>>>>    drivers/gpu/drm/tinydrm/mi0283qt.c          |   1 -
>>>>>>>    drivers/gpu/drm/tinydrm/st7586.c            |   1 -
>>>>>>>    drivers/gpu/drm/tinydrm/st7735r.c           |   1 -
>>>>>>>    include/drm/drm_client.h                    | 156 ++++++++++
>>>>>>>    include/drm/drm_device.h                    |  21 ++
>>>>>>>    include/drm/drm_fb_cma_helper.h             |   6 -
>>>>>>>    include/drm/drm_fb_helper.h                 |  38 +++
>>>>>>>    25 files changed, 1298 insertions(+), 514 deletions(-)
>>>>>>>    create mode 100644 Documentation/gpu/drm-client.rst
>>>>>>>    create mode 100644 drivers/gpu/drm/drm_client.c
>>>>>>>    create mode 100644 include/drm/drm_client.h
>>>>>>>
>>>>>>> --
>>>>>>> 2.15.1
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> Intel-gfx mailing list
>>>>>>> Intel-gfx at lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel at lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
>



-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch


More information about the Intel-gfx mailing list